/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.community.intellij.plugins.communitycase.checkin;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Clock;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.text.DateFormatUtil;
import org.community.intellij.plugins.communitycase.Branch;
import org.community.intellij.plugins.communitycase.Util;
import org.community.intellij.plugins.communitycase.Vcs;
import org.community.intellij.plugins.communitycase.commands.Command;
import org.community.intellij.plugins.communitycase.commands.Handler;
import org.community.intellij.plugins.communitycase.commands.LineHandler;
import org.community.intellij.plugins.communitycase.commands.StringScanner;
import org.community.intellij.plugins.communitycase.config.VcsSettings;
import org.community.intellij.plugins.communitycase.rebase.InteractiveRebaseEditorHandler;
import org.community.intellij.plugins.communitycase.rebase.RebaseEditorService;
import org.community.intellij.plugins.communitycase.update.BaseRebaseProcess;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* This is subclass of {@link org.community.intellij.plugins.communitycase.update.BaseRebaseProcess} that implement rebase operation for {@link PushActiveBranchesDialog}.
* This operation reorders commits if needed.
*/
public class PushRebaseProcess extends BaseRebaseProcess {
/**
* The logger
*/
private static final Logger LOG = Logger.getInstance("#"+PushRebaseProcess.class.getName());
/**
* Save changes policy
*/
private final VcsSettings.UpdateChangesPolicy mySavePolicy;
/**
* The map from vcs root to list of the commit identifier for reordered commits, if vcs root is not provided, the reordering is not needed.
*/
private final Map<VirtualFile, List<String>> myReorderedCommits;
/**
* A set of roots that have non-pushed merges
*/
private final Set<VirtualFile> myRootsWithMerges;
/**
* The registration number for the rebase editor
*/
private Integer myRebaseEditorNo;
/**
* The rebase editor service
*/
private final RebaseEditorService myRebaseEditorService;
/**
* The constructor
*
* @param vcs the vcs instance
* @param project the project instance
* @param exceptions the list of exceptions for the process
* @param savePolicy the save policy for the rebase process
* @param rootsWithMerges a set of roots with merges
*/
public PushRebaseProcess(final Vcs vcs,
final Project project,
List<VcsException> exceptions,
VcsSettings.UpdateChangesPolicy savePolicy,
Map<VirtualFile, List<String>> reorderedCommits,
Set<VirtualFile> rootsWithMerges) {
super(vcs, project, exceptions);
mySavePolicy = savePolicy;
myReorderedCommits = reorderedCommits;
myRootsWithMerges = rootsWithMerges;
myRebaseEditorService = RebaseEditorService.getInstance();
}
/**
* {@inheritDoc}
*/
@Override
protected LineHandler makeStartHandler(VirtualFile root) throws VcsException {
List<String> commits = myReorderedCommits.get(root);
boolean hasMerges = myRootsWithMerges.contains(root);
LineHandler h = new LineHandler(myProject, root, Command.REBASE);
if (commits != null || hasMerges) {
h.addParameters("-i");
PushRebaseEditor pushRebaseEditor = new PushRebaseEditor(root, commits, hasMerges, h);
myRebaseEditorNo = pushRebaseEditor.getHandlerNo();
myRebaseEditorService.configureHandler(h, myRebaseEditorNo);
if (hasMerges) {
h.addParameters("-p");
}
}
h.addParameters("-m", "-v");
Branch currentBranch = Branch.current(myProject, root);
assert currentBranch != null;
Branch trackedBranch = currentBranch.tracked(myProject, root);
assert trackedBranch != null;
h.addParameters(trackedBranch.getFullName());
return h;
}
/**
* {@inheritDoc}
*/
@Override
protected void cleanupHandler(VirtualFile root, LineHandler h) {
if (myRebaseEditorNo != null) {
myRebaseEditorService.unregisterHandler(myRebaseEditorNo);
myRebaseEditorNo = null;
}
}
/**
* {@inheritDoc}
*/
@Override
protected void configureRebaseEditor(VirtualFile root, LineHandler h) {
InteractiveRebaseEditorHandler editorHandler = new InteractiveRebaseEditorHandler(myRebaseEditorService, myProject, root, h);
editorHandler.setRebaseEditorShown();
myRebaseEditorNo = editorHandler.getHandlerNo();
myRebaseEditorService.configureHandler(h, myRebaseEditorNo);
}
/**
* {@inheritDoc}
*/
@Override
protected String makeStashMessage() {
return "Uncommitted changes before rebase operation in push dialog at " +
DateFormatUtil.formatDateTime(Clock.getTime());
}
/**
* {@inheritDoc}
*/
@Override
protected VcsSettings.UpdateChangesPolicy getUpdatePolicy() {
return mySavePolicy;
}
/**
* The rebase editor that just overrides the list of commits
*/
class PushRebaseEditor extends InteractiveRebaseEditorHandler {
/**
* The reordered commits
*/
private final List<String> myCommits;
/**
* The true means that the root has merges
*/
private final boolean myHasMerges;
/**
* The constructor from fields that is expected to be
* accessed only from {@link org.community.intellij.plugins.communitycase.rebase.RebaseEditorService}.
*
* @param root the git repository root
* @param commits the reordered commits
* @param hasMerges if true, the vcs root has merges
*/
public PushRebaseEditor(final VirtualFile root, List<String> commits, boolean hasMerges, Handler h) {
super(myRebaseEditorService, myProject, root, h);
myCommits = commits;
myHasMerges = hasMerges;
}
/**
* {@inheritDoc}
*/
public int editCommits(String path) {
if (!myRebaseEditorShown) {
myRebaseEditorShown = true;
if (myHasMerges) {
return 0;
}
try {
TreeMap<String, String> pickLines = new TreeMap<String, String>();
StringScanner s = new StringScanner(new String(FileUtil.loadFileText(new File(path), Util.UTF8_ENCODING)));
while (s.hasMoreData()) {
if (!s.tryConsume("pick ")) {
s.line();
continue;
}
String commit = s.spaceToken();
pickLines.put(commit, "pick " + commit + " " + s.line());
}
PrintWriter w = new PrintWriter(new OutputStreamWriter(new FileOutputStream(path), Util.UTF8_ENCODING));
try {
for (String commit : myCommits) {
String key = pickLines.headMap(commit + "\u0000").lastKey();
if (key == null || !commit.startsWith(key)) {
continue; // commit from merged branch
}
w.print(pickLines.get(key) + "\n");
}
}
finally {
w.close();
}
return 0;
}
catch (Exception ex) {
LOG.error("Editor failed: ", ex);
return 1;
}
}
else {
return super.editCommits(path);
}
}
}
}