libgit2 / libgit2sharp

Git + .NET = ❤
http://libgit2.github.com
MIT License
3.12k stars 878 forks source link

Question: Using `RewriteHistory` multiple times on the same commit #2023

Open arron-paul opened 1 year ago

arron-paul commented 1 year ago

Hi Folks, This isn't a bug report but a question I have regarding calling the RewriteHistory method on the same commit.

public class Tests
{
    Repository Repository;
    DirectoryInfo RepositoryDirectoryInfo;

    private static void CopyDirectoryContents(string sourcePath, string targetPath)
    {
       // copies repository, containg .git and repository content to a temp path. not important for this.
    }

    private static Repository LoadRepository(string repositoryAbsolutePath)
    {
        return new Repository(repositoryAbsolutePath);
    }

    [SetUp]
    public void Setup()
    {
        // Create a temporary directory
        string temporaryGitRepositoryAbsPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
        RepositoryDirectoryInfo = Directory.CreateDirectory(temporaryGitRepositoryAbsPath);

        // Create a copy of the git Repository fixture as testing will modify the fixture itself
        string fixtureGitRepositoryRelPath = "Fixtures\\SampleGitRepository";
        CopyDirectoryContents(fixtureGitRepositoryRelPath, RepositoryDirectoryInfo.FullName);

        Repository = LoadRepository(RepositoryDirectoryInfo.FullName);
    }

    [Test]
    public void Test1()
    {
        // Get the commits and grab the first one
        List<Commit> firstCommits = Repository.Commits.ToList();
        Commit firstChangeToFirstCommit = firstCommits.First();

        // Rewrite commit. Change commit message.
        GitRewriter.RewriteCommit(
            repository: Repository,
            commit: firstChangeToFirstCommit,
            author: firstChangeToFirstCommit.Author,
            committer: firstChangeToFirstCommit.Committer,
            commitMessage: "first change to commit message"
        );

        // Not sure if required (I dont think it is)
        Repository.Reset(ResetMode.Hard, Repository.Head.Tip);

        // Reload Repository thats changed.
        // a.k.a return new Repository("the abs path of the repository");
        Repository = LoadRepository(RepositoryDirectoryInfo.FullName);

        // Get the commits again, and grab the first one like before
        List<Commit> secondCommits = Repository.Commits.ToList();
        Commit secondChangeToFirstCommit = secondCommits.First();

        // Rewrite a second time
        GitRewriter.RewriteCommit(
            repository: Repository,
            commit: secondChangeToFirstCommit,
            author: secondChangeToFirstCommit.Author,
            committer: secondChangeToFirstCommit.Committer,
            commitMessage: "second change to commit message"
        );
    }
}
public class GitRewriter
{
  public static void RewriteCommits(Repository repository, Commit[] commits, Signature author, Signature committer, string commitMessage)
  {
      RewriteHistoryOptions options = new()
      {
          OnError = (error) => ... ,
          OnSucceeding = () => ... ,
          CommitHeaderRewriter = commit =>
          {
              return CommitRewriteInfo.From(
                  commit: commit,
                  author: author,
                  committer: committer,
                  message: commitMessage
              );
          }
      };
      repository.Refs.RewriteHistory(options, commits);
  }
  public static void RewriteCommit(Repository repository, Commit commit, Signature author, Signature committer, string commitMessage)
  {
      Commit[] singleCommit = new Commit[] { commit };
      RewriteCommits(
          repository: repository,
          commits: singleCommit,
          author: author,
          committer: committer,
          commitMessage: commitMessage
      );
  }
}

If you look at my test case Test1, I'm trying to use RewriteHistory on the same commit, twice.

The first rewrite works fine.

On the second attempt, I get the exception: Can't back up reference 'refs/heads/master' - 'refs/original/heads/master' already exists

Message: 
System.InvalidOperationException : Can't back up reference 'refs/heads/master' - 'refs/original/heads/master' already exists

Stack Trace: 
HistoryRewriter.RewriteReference[TRef,TTarget](TRef oldRef, Func`2 selectTarget, Func`2 rewriteTarget, ReferenceUpdater`2 updateTarget)
HistoryRewriter.RewriteReference(Reference reference)
HistoryRewriter.Execute()
ReferenceCollection.RewriteHistory(RewriteHistoryOptions options, IEnumerable`1 commitsToRewrite)
ReferenceCollection.RewriteHistory(RewriteHistoryOptions options, Commit[] commitsToRewrite)
GitRewriter.RewriteCommit(Repository repository, Commit[] commits, Signature author, Signature committer, String commitMessage) line 29
GitRewriter.RewriteCommit(Repository repository, Commit commit, Signature author, Signature committer, String commitMessage) line 34
Tests.Test1() line 168
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)

I'm aware that when a commit is changed, a new SHA is allocated to that changed commit, and when after I've changed the commit the first time, the SHA does indeed change, but when I attempt to change it the second time, using the changed SHA, I recieve the error as described above.

I've checked the examples of LibGit2 and I could not find anything specific regarding this use-case of changing the same commit twice.

I've tried 'reloading' the repository by created a new Repository but no dice.

Any help, tips or pointers to documentation would be greatly appreciated. Thank you,