libgit2/libgit2sharp — pull request #511 by dahlbyk (2013-09-15 06:55:20+00:00)
Title: RewriteHistory.OnComplete

Had cause to actually use RewriteHistory tonight to purge some passwords that should never have been pushed. Two proposed enhancements came out of that hacking:

* `OnComplete` action: Until I was ready to pull the trigger on the actual rewrite, I found it quite handy to be able to verify that I was on the right track but then throw to rollback and continue development.

Using this on every test case is probably overkill, but I couldn't decide where to stop.
* My desired behavior was essentially "inject cleansed file from a branch" into earlier trees. Without `TreeEntryDefinition.From(te)`, I have to do something like:

```csharp
var blob = (Blob)te.Target;
td.Add(te.Path, blob, blob.Mode);
```

Not a huge deal, but I resent the extra work. Things would get even more complicated if you're dealing with more than one type of `TreeEntry`.

For posterity, here's a simplified version of the rewrite code I ended up with:

```csharp
var clean = repo.Branches["clean"];
var todo = new[]
{
clean[@"file1"],
clean[@"file2"],
clean[@"file3"],
};

repo.Refs.RewriteHistory(
new RewriteHistoryOptions
{
CommitTreeRewriter =
c =>
{
var td = TreeDefinition.From(c);

foreach (var te in todo)
if (td[te.Path] != null)
td.Add(te.Path, TreeEntryDefinition.From(te));

return td;
},
PruneEmptyCommits = true,
OnComplete =
() =>
{
foreach (var te in todo)
{
Console.WriteLine("=== {0} ===", te.Path);
Console.WriteLine(((Blob)repo.Head[te.Path].Target).ContentAsText());
}

throw new Exception("Forcing rollback");
},
}, repo.Commits.QueryBy(new CommitFilter { Since = repo.Refs }));
```