package com.link_intersystems.gitdirstat.domain; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jgit.api.GarbageCollectCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.internal.storage.file.RefDirectory; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.ReflogEntry; import org.eclipse.jgit.lib.Repository; import com.link_intersystems.gitdirstat.domain.ExpireReflogCommand.ReflogEntryFilter; public class HistoryUpdate { private class SkipReflogEntryFilter implements ReflogEntryFilter { private Set<ObjectId> expireCommits; public SkipReflogEntryFilter(Set<ObjectId> expireCommits) { this.expireCommits = expireCommits; } @Override public boolean accept(ReflogEntry reflogEntry) { ObjectId newId = reflogEntry.getNewId(); ObjectId oldId = reflogEntry.getOldId(); boolean oldIdExpired = !expireCommits.contains(oldId); boolean newIdExpired = !expireCommits.contains(newId); boolean noIdExpired = !oldIdExpired || !newIdExpired; return noIdExpired; } } Map<ObjectId, ObjectId> replacedCommits = new HashMap<ObjectId, ObjectId>(); private GitRepository gitRepository; private IndexUpdate indexUpdate; public HistoryUpdate(GitRepository gitRepository) { this.gitRepository = gitRepository; } public void updateRefs() throws IOException, GitAPIException { List<Ref> allRefs = gitRepository.getRefs(Ref.class); List<String> refsToBePacked = new ArrayList<String>(allRefs.size()); for (Ref ref : allRefs) { if (ref.isUpdateable()) { if (nullSafeCommitUpdate(ref)) { refsToBePacked.add(ref.getName()); } } } if (!refsToBePacked.isEmpty()) { Repository repo = gitRepository.getRepository(); RefDatabase refDatabase = repo.getRefDatabase(); ((RefDirectory) refDatabase).pack(refsToBePacked); refDatabase.refresh(); } } public void cleanupRepository() throws GitAPIException { Set<ObjectId> expireCommits = new HashSet<ObjectId>(); expireCommits.addAll(replacedCommits.keySet()); if (indexUpdate != null) { expireCommits = indexUpdate.getTouchedCommits(); } ExpireReflogCommand expireReflogCommand = new ExpireReflogCommand( gitRepository); expireReflogCommand.setReflogEntryFilter(new SkipReflogEntryFilter( expireCommits)); expireReflogCommand.setExpire(null); expireReflogCommand.call(); gc(); } private void gc() throws GitAPIException { Git git = gitRepository.getGit(); GarbageCollectCommand gc = git.gc(); gc.setExpire(null); gc.call(); } private boolean nullSafeCommitUpdate(Ref ref) throws IOException { ObjectId objectId = ref.getCommitId(); ObjectId rewrittenCommit = replacedCommits.get(objectId); boolean refUpdate = false; if (rewrittenCommit != null) { ref.update(rewrittenCommit); refUpdate = true; } return refUpdate; } public boolean hasReplacedParents(Commit commit) { ObjectId[] parentIds = commit.getParentIds(); for (int i = 0; i < parentIds.length; i++) { ObjectId parentId = parentIds[i]; if (replacedCommits.containsKey(parentId)) { return true; } } return false; } public void replaceCommit(Commit commit, ObjectId replacedCommitId) { replacedCommits.put(commit.getId(), replacedCommitId); } public ObjectId[] getParentIds(Commit commit) { ObjectId[] parentIds = commit.getParentIds(); ObjectId[] replacedParentIds = new ObjectId[parentIds.length]; for (int i = 0; i < parentIds.length; i++) { ObjectId parentId = parentIds[i]; ObjectId replacedCommit = replacedCommits.get(parentId); if (replacedCommit == null) { replacedParentIds[i] = parentId; } else { replacedParentIds[i] = replacedCommit; } } return replacedParentIds; } public IndexUpdate begin() throws IOException, GitAPIException { indexUpdate = new IndexUpdate(gitRepository, this); return indexUpdate; } public void close() throws GitAPIException { Git git = gitRepository.getGit(); git.reset().setMode(ResetType.HARD).call(); if (indexUpdate != null) { indexUpdate.close(); } } }