package com.link_intersystems.gitdirstat.domain;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import com.link_intersystems.gitdirstat.domain.walk.CommitRangeTreeBuilder;
import com.link_intersystems.gitdirstat.domain.walk.CommitWalker;
import com.link_intersystems.gitdirstat.domain.walk.RevWalkCommitRangeTreeBuilder;
public class GitRepository {
private Repository repository;
private RefFactory refFactory;
private ObjectDatabase objectDatabase;
private ObjectReader objectReader;
private CommitDatabase commitDatabase = new CommitDatabase(this);
public GitRepository(Repository repository) {
this.repository = repository;
refFactory = new RefFactory(this);
}
public GitRepository(Git git) {
this(git.getRepository());
}
static String createId(File repositoryDirectory) {
FileRepositoryBuilder builder = new FileRepositoryBuilder();
builder.readEnvironment();
builder.findGitDir(repositoryDirectory);
File gitDir = builder.getGitDir();
if (gitDir == null) {
String message = MessageFormat.format(
"No git repository found at {0}", repositoryDirectory);
throw new GitRepositoryException(message);
}
try {
return gitDir.getCanonicalPath();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
public String getId() {
try {
return getRepository().getDirectory().getCanonicalPath();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
public Repository getRepository() {
return repository;
}
@SuppressWarnings("unchecked")
public <T extends Ref> List<T> getRefs(Class<T> refType) {
Repository repository = getRepository();
RefDatabase refDatabase = repository.getRefDatabase();
refDatabase.refresh();
List<String> prefixes = refFactory.getRefPrefixes(refType);
List<T> refList = new ArrayList<T>();
for (String prefix : prefixes) {
try {
Map<String, org.eclipse.jgit.lib.Ref> refs = refDatabase
.getRefs(prefix);
for (Entry<String, org.eclipse.jgit.lib.Ref> refEntry : refs
.entrySet()) {
org.eclipse.jgit.lib.Ref jgitRef = refEntry.getValue();
Ref ref = refFactory.create(jgitRef);
if (ref != null) {
refList.add((T) ref);
}
}
} catch (IOException e) {
throw new GitRepositoryException(e);
}
}
Collections.sort(refList, DefaultRefSorter.INSTANCE);
return refList;
}
public CommitRange getCommitRange(String revstr) throws IOException {
Repository repository = getRepository();
ObjectId toInclusive = repository.resolve(revstr);
ObjectId fromInclusive = getInitialCommit(toInclusive);
CommitRange revRange = new CommitRange(fromInclusive, toInclusive);
return revRange;
}
public Collection<CommitRange> getCommitRanges(List<? extends Ref> refs,
ProgressListener progressListener) throws IOException {
progressListener.start(refs.size());
try {
Collection<CommitRange> commitRanges = new ArrayList<CommitRange>();
for (Ref ref : refs) {
if (progressListener.isCanceled()) {
commitRanges = Collections.emptyList();
break;
}
ObjectId toInclusive = ref.getCommitId();
if (toInclusive != null) {
CommitRange revRange = new CommitRange(null, toInclusive);
commitRanges.add(revRange);
}
progressListener.update(1);
}
return commitRanges;
} finally {
progressListener.end();
}
}
public Collection<CommitRange> getCommitRanges(List<? extends Ref> refs)
throws IOException {
return getCommitRanges(refs, new NullProgressListener());
}
private RevCommit getInitialCommit(AnyObjectId headId) throws IOException {
Repository repository = getRepository();
RevWalk rw = new RevWalk(repository);
RevCommit c = null;
RevCommit root = rw.parseCommit(headId);
rw.markStart(root);
rw.sort(RevSort.REVERSE);
c = rw.next();
return c;
}
public TreeObject getCommitRangeTree(CommitRange commitRange,
ProgressListener progressListener) {
List<CommitRange> singletonList = Collections
.singletonList(commitRange);
return getCommitRangeTree(singletonList, progressListener);
}
public TreeObject getCommitRangeTree(Collection<CommitRange> commitRanges,
ProgressListener progressListener) {
CommitRangeTreeBuilder commitRangeTreeBuilder = new RevWalkCommitRangeTreeBuilder(
this);
return commitRangeTreeBuilder.build(commitRanges, progressListener);
}
private CommitWalker createCommitWalker(Collection<CommitRange> commitRanges)
throws IOException {
CommitWalker commitWalk = new CommitWalker(this);
commitWalk.setCommitRanges(commitRanges);
commitWalk.sort(RevSort.TOPO, RevSort.REVERSE);
return commitWalk;
}
public TreeObject getCommitRangeTree(CommitRange commitRange) {
return getCommitRangeTree(commitRange, NullProgressListener.INSTANCE);
}
public TreeObject getCommitRangeTree(List<? extends Ref> selectedRefs,
ProgressListener progressListener) throws IOException {
progressListener.start(1000);
try {
Collection<CommitRange> commitRanges = getCommitRanges(selectedRefs);
return getCommitRangeTree(commitRanges, progressListener);
} finally {
progressListener.end();
}
}
public void applyFilter(Collection<CommitRange> commitRanges,
IndexFilter indexFilter, ProgressListener progressListener)
throws IOException, GitAPIException {
HistoryUpdate historyUpdate = new HistoryUpdate(this);
IndexUpdate indexUpdate = historyUpdate.begin();
CommitWalker commitWalk = createCommitWalker(commitRanges);
int totalWork = commitWalk.getWalkCount();
Iterator<Commit> commitIterator = commitWalk.iterator();
try {
progressListener.start(totalWork);
while (commitIterator.hasNext()) {
Commit commit = commitIterator.next();
CacheCommitUpdate commitUpdate = indexUpdate
.beginUpdate(commit);
indexFilter.apply(commitUpdate);
commitUpdate.writeCommit();
progressListener.update(1);
if (progressListener.isCanceled()) {
break;
}
}
commitWalk.close();
if (!progressListener.isCanceled()) {
applyHistoryUpdate(historyUpdate, progressListener);
}
} finally {
try {
historyUpdate.close();
} catch (GitAPIException e) {
}
progressListener.end();
}
}
private void applyHistoryUpdate(HistoryUpdate historyUpdate,
ProgressListener progressListener) throws IOException,
GitAPIException {
progressListener.start(ProgressListener.UNKNOWN);
historyUpdate.updateRefs();
historyUpdate.cleanupRepository();
}
public Git getGit() {
return new Git(getRepository());
}
public void applyFilter(Collection<CommitRange> commitRanges,
IndexFilter indexFilter) throws IOException,
CheckoutConflictException, GitAPIException {
applyFilter(commitRanges, indexFilter, NullProgressListener.INSTANCE);
}
public void applyFilter(IndexFilter indexFilter) throws IOException,
GitAPIException {
List<Ref> refs = getRefs(Ref.class);
applyFilter(refs, indexFilter, NullProgressListener.INSTANCE);
}
public void applyFilter(IndexFilter indexFilter,
ProgressListener progressListener) throws IOException,
GitAPIException {
List<Ref> refs = getRefs(Ref.class);
applyFilter(refs, indexFilter, progressListener);
}
public void applyFilter(List<? extends Ref> refs, IndexFilter indexFilter)
throws IOException, GitAPIException {
Collection<CommitRange> commitRanges = getCommitRanges(refs);
applyFilter(commitRanges, indexFilter, NullProgressListener.INSTANCE);
}
public void applyFilter(List<? extends Ref> refs, IndexFilter indexFilter,
ProgressListener progressListener) throws IOException,
GitAPIException {
Collection<CommitRange> commitRanges = getCommitRanges(refs);
applyFilter(commitRanges, indexFilter, progressListener);
}
CommitDatabase getCommitAccess() {
return commitDatabase;
}
public Commit getCommit(RevCommit revCommit) {
return getCommitAccess().getCommit(revCommit);
}
ObjectDatabase getObjectDatabase() {
if (objectDatabase == null) {
objectDatabase = repository.getObjectDatabase();
}
return objectDatabase;
}
public ObjectReader getObjectReader() {
if (objectReader == null) {
objectReader = getObjectDatabase().newReader();
}
return objectReader;
}
public void release() {
if (objectReader != null) {
objectReader.release();
}
repository.close();
}
public void getRefs(org.eclipse.jgit.lib.Ref jgitRef) {
// TODO Auto-generated method stub
}
}