package com.beijunyi.parallelgit.filesystem.commands; import java.io.IOException; import java.util.Collections; import java.util.Map; import javax.annotation.Nonnull; import com.beijunyi.parallelgit.filesystem.GfsStatusProvider; import com.beijunyi.parallelgit.filesystem.GitFileSystem; import com.beijunyi.parallelgit.filesystem.exceptions.NoHeadCommitException; import com.beijunyi.parallelgit.filesystem.merge.MergeConflict; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.merge.*; import org.eclipse.jgit.revwalk.RevCommit; import static com.beijunyi.parallelgit.filesystem.commands.GfsApplyStash.Result.*; import static com.beijunyi.parallelgit.filesystem.commands.GfsApplyStash.Status.*; import static com.beijunyi.parallelgit.filesystem.io.GfsDefaultCheckout.checkout; import static com.beijunyi.parallelgit.filesystem.io.GfsTreeIterator.iterateRoot; import static com.beijunyi.parallelgit.filesystem.merge.GfsMergeCheckout.handleConflicts; import static com.beijunyi.parallelgit.filesystem.merge.MergeConflict.readConflicts; import static com.beijunyi.parallelgit.utils.CommitUtils.getCommit; import static java.util.Collections.unmodifiableMap; import static org.eclipse.jgit.dircache.DirCache.newInCore; import static org.eclipse.jgit.lib.Constants.STASH; import static org.eclipse.jgit.merge.MergeStrategy.RECURSIVE; public class GfsApplyStash extends GfsCommand<GfsApplyStash.Result> { private static final String LAST_STASH = makeStashId(0); private MergeStrategy strategy = RECURSIVE; private MergeFormatter formatter = new MergeFormatter(); private DirCache cache = newInCore(); private RevCommit head; private String stashId; private RevCommit stash; private ResolveMerger merger; public GfsApplyStash(GitFileSystem gfs) { super(gfs); } @Nonnull public GfsApplyStash stash(String id) throws IOException { this.stashId = id; return this; } @Nonnull public GfsApplyStash stash(int id) throws IOException { return stash(makeStashId(id)); } @Nonnull @Override protected GfsApplyStash.Result doExecute(GfsStatusProvider.Update update) throws IOException { prepareHead(); prepareStash(); prepareMerger(); return mergeStash(); } private void prepareHead() throws IOException { if(!status.isInitialized()) throw new NoHeadCommitException(); head = status.commit(); } private void prepareStash() throws IOException { if(stashId == null) stashId = LAST_STASH; stash = getCommit(stashId, repo); } private void prepareMerger() throws IOException { merger = (ResolveMerger)strategy.newMerger(repo, true); merger.setBase(stash.getParent(0)); merger.setCommitNames(new String[] {"BASE", "Updated upstream", "Stashed changes"}); merger.setWorkingTreeIterator(iterateRoot(gfs)); } @Nonnull private GfsApplyStash.Result mergeStash() throws IOException { boolean success = merger.merge(head, stash); GfsApplyStash.Result ret; if(success) { checkoutFiles(merger); ret = success(); } else { writeConflicts(merger); ret = conflicting(readConflicts(merger)); } return ret; } private void checkoutFiles(Merger merger) throws IOException { AnyObjectId treeId = merger.getResultTreeId(); checkout(gfs, treeId); } private void writeConflicts(ResolveMerger merger) throws IOException { Map<String, MergeConflict> conflicts = readConflicts(merger); handleConflicts(gfs, conflicts) .withFormatter(formatter) .checkout(cache); } @Nonnull private static String makeStashId(int id) { return STASH + "@{" + id + "}"; } public enum Status { SUCCESS, CONFLICTING } public static class Result implements GfsCommandResult { private final GfsApplyStash.Status status; private final Map<String, MergeConflict> conflicts; private Result(Status status, Map<String, MergeConflict> conflicts) { this.status = status; this.conflicts = unmodifiableMap(conflicts); } @Nonnull public static GfsApplyStash.Result success() { return new GfsApplyStash.Result(SUCCESS, Collections.<String, MergeConflict>emptyMap()); } @Nonnull public static GfsApplyStash.Result conflicting(Map<String, MergeConflict> conflicts) { return new GfsApplyStash.Result(CONFLICTING, conflicts); } @Override public boolean isSuccessful() { return SUCCESS == status; } public boolean hasConflicts() { return CONFLICTING == status; } @Nonnull public Map<String, MergeConflict> getConflicts() { return conflicts; } } }