根据上面的建议,这是我想出来的初步(仍在测试中)C#代码,它将在特定SHA处截断主分支,创建一个新的初始提交。它还会删除所有悬空引用和Blob。
public class RepositoryUtility
{
public RepositoryUtility()
{
}
public String[] GetPaths(Commit commit)
{
List<String> paths = new List<string>();
RecursivelyGetPaths(paths, commit.Tree);
return paths.ToArray();
}
private void RecursivelyGetPaths(List<String> paths, Tree tree)
{
foreach (TreeEntry te in tree)
{
paths.Add(te.Path);
if (te.TargetType == TreeEntryTargetType.Tree)
{
RecursivelyGetPaths(paths, te.Target as Tree);
}
}
}
public void TruncateCommits(String repositoryPath, Int32 maximumCommitCount)
{
IRepository repository = new Repository(repositoryPath);
Int32 count = 0;
string newInitialCommitSHA = null;
foreach (Commit masterCommit in repository.Head.Commits)
{
count++;
if (count == maximumCommitCount)
{
newInitialCommitSHA = masterCommit.Sha;
}
}
if (count > maximumCommitCount)
{
TruncateCommits(repository, repositoryPath, newInitialCommitSHA);
}
}
private void RecursivelyCheckTreeItems(Tree tree,Dictionary<String, TreeEntry> treeItems, Dictionary<String, GitObject> gitObjectDeleteList)
{
foreach (TreeEntry treeEntry in tree)
{
if (!treeItems.ContainsKey(treeEntry.Target.Sha))
{
if (!gitObjectDeleteList.ContainsKey(treeEntry.Target.Sha))
{
gitObjectDeleteList.Add(treeEntry.Target.Sha, treeEntry.Target);
}
}
if (treeEntry.TargetType == TreeEntryTargetType.Tree)
{
RecursivelyCheckTreeItems(treeEntry.Target as Tree, treeItems, gitObjectDeleteList);
}
}
}
private void RecursivelyAddTreeItems(Dictionary<String, TreeEntry> treeItems, Tree tree)
{
foreach (TreeEntry treeEntry in tree)
{
if (!treeItems.ContainsKey(treeEntry.Target.Sha))
{
treeItems.Add(treeEntry.Target.Sha, treeEntry);
}
if (treeEntry.TargetType == TreeEntryTargetType.Tree)
{
RecursivelyAddTreeItems(treeItems, treeEntry.Target as Tree);
}
}
}
private void TruncateCommits(IRepository repository, String repositoryPath, string newInitialCommitSHA)
{
Dictionary<String, TreeEntry> treeItems = new Dictionary<string, TreeEntry>();
Commit selectedCommit = null;
Dictionary<String, GitObject> gitObjectDeleteList = new Dictionary<String, GitObject>();
foreach (Commit masterCommit in repository.Head.Commits)
{
if (selectedCommit != null)
{
gitObjectDeleteList.Add(masterCommit.Sha, masterCommit);
RecursivelyCheckTreeItems(masterCommit.Tree, treeItems, gitObjectDeleteList);
}
else
{
if (String.Equals(masterCommit.Sha, newInitialCommitSHA, StringComparison.CurrentCultureIgnoreCase))
{
selectedCommit = masterCommit;
}
RecursivelyAddTreeItems(treeItems, masterCommit.Tree);
}
}
Func<Commit, IEnumerable<Commit>> rewriter = (c) => { return new Commit[0]; };
repository.Refs.RewriteHistory(new RewriteHistoryOptions() { CommitParentsRewriter = rewriter }, selectedCommit);
foreach (var reference in repository.Refs.FromGlob("refs/original/*"))
{
repository.Refs.Remove(reference);
if (reference.CanonicalName.IndexOf("master", 0, StringComparison.CurrentCultureIgnoreCase) == -1)
{
DeleteGitBlob(repositoryPath, reference.TargetIdentifier);
}
}
foreach (var reference in repository.Refs.FromGlob("refs/tags/*"))
{
if (gitObjectDeleteList.ContainsKey(reference.TargetIdentifier))
{
repository.Refs.Remove(reference);
}
}
foreach (KeyValuePair<String, GitObject> kvp in gitObjectDeleteList)
{
DeleteGitBlob(repositoryPath, kvp.Value.Sha);
}
}
private void DeleteGitBlob(String repositoryPath, String blobSHA)
{
String shaDirName = System.IO.Path.Combine(System.IO.Path.Combine(repositoryPath, ".git\\objects"), blobSHA.Substring(0, 2));
String shaFileName = System.IO.Path.Combine(shaDirName, blobSHA.Substring(2));
if (System.IO.Directory.Exists(shaDirName))
{
String[] directoryFiles = System.IO.Directory.GetFiles(shaDirName);
foreach (String directoryFile in directoryFiles)
{
if (String.Equals(shaFileName, directoryFile, StringComparison.CurrentCultureIgnoreCase))
{
FileInfo fi = new FileInfo(shaFileName);
if (fi.IsReadOnly)
{
fi.IsReadOnly = false;
}
File.Delete(shaFileName);
if (directoryFiles.Length == 1)
{
System.IO.Directory.Delete(shaDirName);
}
}
}
}
}
}
感谢您的所有帮助,我们非常感激。
请注意,我从原始代码中进行了编辑,因为它没有考虑到目录。