mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
HgBranches feature first take (git-style )
* New "Branches" action for HG invokes HgBranchPopup. So does the click on HgBranchWidget on status bar.
* HgBranchPopup shows common actions ("New branch") and the list of repositories and not active branches.
* Merge and UpdateTo branch actions are supported from PopUp view.
* HgRepository stores information about branches
* HgRepositoryReader collects information from files
* HgRepositoryUpdater call repositpry reader if .hg files were changed externally
* Checkbox for new branch creation push added
* Check if .hg internal files exist added
* Additional checks for new repository(without any commit) added (cache,branch and other files maybe not exist)
This commit is contained in:
@@ -127,3 +127,4 @@ hg4idea.history.copy.revision.number=Copy Revision Number
|
||||
hg4idea.changelist.column.branch=Branch
|
||||
|
||||
hg4idea.annotation.tool.tip=commit {0}\nAuthor: {1}\nDate: {2}\n\n{3}
|
||||
hg4idea.push.asNewBranch=push as &new remote branch
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
<action id="hg4idea.merge.files" class="org.zmlx.hg4idea.action.HgMerge" icon="AllIcons.Vcs.MergeSourcesTree"/>
|
||||
<separator/>
|
||||
|
||||
<action id="hg4idea.branches" class="org.zmlx.hg4idea.action.HgBranchesAction" text="_Branches..."/>
|
||||
<action id="hg4idea.tag" class="org.zmlx.hg4idea.action.HgCreateTagAction"/>
|
||||
<action id="hg4idea.updateTo" class="org.zmlx.hg4idea.action.HgUpdateToAction"/>
|
||||
<action id="hg4idea.resolve.mark" class="org.zmlx.hg4idea.action.HgMarkResolved"/>
|
||||
|
||||
@@ -137,6 +137,7 @@ public class HgPusher {
|
||||
command.setRevision(dialog.getRevision());
|
||||
command.setForce(dialog.isForce());
|
||||
command.setBranch(dialog.getBranch());
|
||||
command.setIsNewBranch(dialog.isNewBranch());
|
||||
return command;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ abstract class HgAbstractGlobalAction extends AnAction {
|
||||
|
||||
protected abstract void execute(Project project, Collection<VirtualFile> repositories, @Nullable VirtualFile selectedRepo);
|
||||
|
||||
protected static void handleException(Project project, Exception e) {
|
||||
public static void handleException(Project project, Exception e) {
|
||||
LOG.info(e);
|
||||
new HgCommandResultNotifier(project).notifyError(null, "Error", e.getMessage());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.action;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.ListPopup;
|
||||
import com.intellij.openapi.ui.popup.PopupStep;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.ErrorLabel;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.panels.OpaquePanel;
|
||||
import com.intellij.ui.popup.PopupFactoryImpl;
|
||||
import com.intellij.ui.popup.WizardPopup;
|
||||
import com.intellij.ui.popup.list.ListPopupImpl;
|
||||
import com.intellij.ui.popup.list.PopupListElementRenderer;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.zmlx.hg4idea.repo.HgRepository;
|
||||
import org.zmlx.hg4idea.repo.HgRepositoryImpl;
|
||||
import org.zmlx.hg4idea.util.HgUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The popup which allows to quickly switch and control Hg branches.
|
||||
* </p>
|
||||
* <p>
|
||||
* Use {@link #asListPopup()} to achieve the {@link com.intellij.openapi.ui.popup.ListPopup} itself.
|
||||
* </p>
|
||||
*
|
||||
* @author NadyaZabrodina
|
||||
*/
|
||||
public class HgBranchPopup {
|
||||
|
||||
private final Project myProject;
|
||||
|
||||
private final HgRepository myCurrentRepository;
|
||||
private final ListPopupImpl myPopup;
|
||||
|
||||
public ListPopup asListPopup() {
|
||||
return myPopup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param currentRepository Current repository, which means the repository of the currently open or selected file.
|
||||
*/
|
||||
public static HgBranchPopup getInstance(@NotNull Project project, @NotNull HgRepository currentRepository) {
|
||||
return new HgBranchPopup(project, currentRepository);
|
||||
}
|
||||
|
||||
private HgBranchPopup(@NotNull Project project, @NotNull HgRepository currentRepository) {
|
||||
myProject = project;
|
||||
myCurrentRepository = currentRepository;
|
||||
String title = createPopupTitle(currentRepository);
|
||||
|
||||
Condition<AnAction> preselectActionCondition = new Condition<AnAction>() {
|
||||
@Override
|
||||
public boolean value(AnAction action) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
myPopup = new HgBranchActionGroupPopup(title, project, preselectActionCondition);
|
||||
|
||||
setCurrentBranchInfo();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private static String createPopupTitle(@NotNull HgRepository currentRepository) {
|
||||
String title = "Hg Branches";
|
||||
|
||||
title += " in " + currentRepository.getRoot().getName();
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
private void setCurrentBranchInfo() {
|
||||
String branchText = "Current branch : ";
|
||||
myPopup.setAdText(branchText + HgUtil.getDisplayableBranchText(myCurrentRepository), SwingConstants.CENTER);
|
||||
}
|
||||
|
||||
|
||||
private ActionGroup createActions() {
|
||||
DefaultActionGroup popupGroup = new DefaultActionGroup(null, false);
|
||||
|
||||
fillPopupWithCurrentRepositoryActions(popupGroup, createRepositoriesActions());
|
||||
|
||||
popupGroup.addSeparator();
|
||||
return popupGroup;
|
||||
}
|
||||
|
||||
|
||||
private DefaultActionGroup createRepositoriesActions() {
|
||||
DefaultActionGroup popupGroup = new DefaultActionGroup(null, false);
|
||||
popupGroup.addSeparator("Repositories");
|
||||
for (VirtualFile repository : HgUtil.getHgRepositories(myProject)) {
|
||||
HgRepository repo = HgRepositoryImpl.getFullInstance(repository, myProject, myProject);
|
||||
popupGroup.add(new RootAction(repo, null));
|
||||
}
|
||||
return popupGroup;
|
||||
}
|
||||
|
||||
|
||||
private void fillPopupWithCurrentRepositoryActions(@NotNull DefaultActionGroup popupGroup, @Nullable DefaultActionGroup actions) {
|
||||
popupGroup.addAll(new HgBranchPopupActions(myProject, myCurrentRepository).createActions(actions));
|
||||
}
|
||||
|
||||
private static class RootAction extends ActionGroup {
|
||||
|
||||
private final HgRepository myRepository;
|
||||
|
||||
/**
|
||||
* @param currentRepository Pass null in the case of common repositories - none repository will be highlighted then.
|
||||
*/
|
||||
RootAction(@NotNull HgRepository repository, @Nullable HgRepository currentRepository) {
|
||||
super(repository.getRoot().getName(), true);
|
||||
myRepository = repository;
|
||||
if (repository.equals(currentRepository)) {
|
||||
getTemplatePresentation().setIcon(PlatformIcons.CHECK_ICON);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AnAction[] getChildren(@Nullable AnActionEvent e) {
|
||||
ActionGroup group = new HgBranchPopupActions(myRepository.getProject(), myRepository).createActions(null);
|
||||
return group.getChildren(e);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getCaption() {
|
||||
String captionText = "Current branch in " + myRepository.getRoot().getName() + ": ";
|
||||
return captionText + HgUtil.getDisplayableBranchText(myRepository);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getBranch() {
|
||||
return myRepository.getCurrentBranch();
|
||||
}
|
||||
}
|
||||
|
||||
private class HgBranchActionGroupPopup extends PopupFactoryImpl.ActionGroupPopup {
|
||||
public HgBranchActionGroupPopup(@NotNull String title, @NotNull Project project,
|
||||
@NotNull Condition<AnAction> preselectActionCondition) {
|
||||
super(title, HgBranchPopup.this.createActions(), SimpleDataContext.getProjectContext(project), false, false, false, true, null, -1,
|
||||
preselectActionCondition, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WizardPopup createPopup(WizardPopup parent, PopupStep step, Object parentValue) {
|
||||
WizardPopup popup = super.createPopup(parent, step, parentValue);
|
||||
RootAction rootAction = getRootAction(parentValue);
|
||||
if (rootAction != null) {
|
||||
popup.setAdText((rootAction).getCaption());
|
||||
}
|
||||
return popup;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RootAction getRootAction(Object value) {
|
||||
if (value instanceof PopupFactoryImpl.ActionItem) {
|
||||
AnAction action = ((PopupFactoryImpl.ActionItem)value).getAction();
|
||||
if (action instanceof RootAction) {
|
||||
return (RootAction)action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListCellRenderer getListElementRenderer() {
|
||||
return new PopupListElementRenderer(this) {
|
||||
|
||||
private ErrorLabel myBranchLabel;
|
||||
|
||||
@Override
|
||||
protected void customizeComponent(JList list, Object value, boolean isSelected) {
|
||||
super.customizeComponent(list, value, isSelected);
|
||||
|
||||
RootAction rootAction = getRootAction(value);
|
||||
if (rootAction != null) {
|
||||
myBranchLabel.setVisible(true);
|
||||
myBranchLabel.setText(String.format("[%s]", rootAction.getBranch()));
|
||||
|
||||
if (isSelected) {
|
||||
setSelected(myBranchLabel);
|
||||
}
|
||||
else {
|
||||
myBranchLabel.setBackground(getBackground());
|
||||
myBranchLabel.setForeground(JBColor.GRAY); // different foreground than for other elements
|
||||
}
|
||||
|
||||
adjustOpacity(myBranchLabel, isSelected);
|
||||
}
|
||||
else {
|
||||
myBranchLabel.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createItemComponent() {
|
||||
myTextLabel = new ErrorLabel();
|
||||
myTextLabel.setOpaque(true);
|
||||
myTextLabel.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||
|
||||
myBranchLabel = new ErrorLabel();
|
||||
myBranchLabel.setOpaque(true);
|
||||
myBranchLabel.setBorder(BorderFactory.createEmptyBorder(1, UIUtil.DEFAULT_HGAP, 1, 1));
|
||||
|
||||
JPanel compoundPanel = new OpaquePanel(new BorderLayout(), Color.white);
|
||||
compoundPanel.add(myTextLabel, BorderLayout.CENTER);
|
||||
compoundPanel.add(myBranchLabel, BorderLayout.EAST);
|
||||
|
||||
return layoutComponent(compoundPanel);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.action;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.update.UpdatedFiles;
|
||||
import com.intellij.util.IconUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.zmlx.hg4idea.HgRevisionNumber;
|
||||
import org.zmlx.hg4idea.HgVcs;
|
||||
import org.zmlx.hg4idea.command.HgBranchCreateCommand;
|
||||
import org.zmlx.hg4idea.command.HgMergeCommand;
|
||||
import org.zmlx.hg4idea.command.HgUpdateCommand;
|
||||
import org.zmlx.hg4idea.execution.HgCommandException;
|
||||
import org.zmlx.hg4idea.execution.HgCommandResult;
|
||||
import org.zmlx.hg4idea.execution.HgCommandResultHandler;
|
||||
import org.zmlx.hg4idea.provider.update.HgConflictResolver;
|
||||
import org.zmlx.hg4idea.provider.update.HgHeadMerger;
|
||||
import org.zmlx.hg4idea.repo.HgRepository;
|
||||
import org.zmlx.hg4idea.util.HgErrorUtil;
|
||||
import org.zmlx.hg4idea.util.HgUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
public class HgBranchPopupActions {
|
||||
|
||||
private final Project myProject;
|
||||
private final HgRepository myRepository;
|
||||
|
||||
HgBranchPopupActions(Project project, HgRepository repository) {
|
||||
myProject = project;
|
||||
myRepository = repository;
|
||||
}
|
||||
|
||||
ActionGroup createActions(@Nullable DefaultActionGroup toInsert) {
|
||||
DefaultActionGroup popupGroup = new DefaultActionGroup(null, false);
|
||||
popupGroup.addAction(new NewBranchAction(myProject, Collections.singletonList(myRepository)));
|
||||
|
||||
if (toInsert != null) {
|
||||
popupGroup.addAll(toInsert);
|
||||
}
|
||||
|
||||
popupGroup.addSeparator("Branches");
|
||||
List<String> branches = new ArrayList<String>(myRepository.getBranches());
|
||||
Collections.sort(branches);
|
||||
for (String branch : branches) {
|
||||
if (!branch.equals(myRepository.getCurrentBranch())) { // don't show current branch in the list
|
||||
popupGroup.add(new BranchActions(myProject, branch, myRepository));
|
||||
}
|
||||
}
|
||||
return popupGroup;
|
||||
}
|
||||
|
||||
static class NewBranchAction extends DumbAwareAction {
|
||||
private final Project myProject;
|
||||
private final List<HgRepository> myRepositories;
|
||||
|
||||
NewBranchAction(@NotNull Project project, @NotNull List<HgRepository> repositories) {
|
||||
super("New Branch", "Create and checkout new branch", IconUtil.getAddIcon());
|
||||
myProject = project;
|
||||
myRepositories = repositories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
final String name = HgUtil.getNewBranchNameFromUser(myProject, "Create New Branch");
|
||||
|
||||
try {
|
||||
new HgBranchCreateCommand(myProject, HgUtil.getRootForSelectedFile(myProject), name).execute(new HgCommandResultHandler() {
|
||||
@Override
|
||||
public void process(@Nullable HgCommandResult result) {
|
||||
myProject.getMessageBus().syncPublisher(HgVcs.BRANCH_TOPIC).update(myProject, null);
|
||||
if (HgErrorUtil.hasErrorsInCommandExecution(result)) {
|
||||
new HgCommandResultNotifier(myProject)
|
||||
.notifyError(result, "Creation failed", "Branch creation [" + name + "] failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (HgCommandException exception) {
|
||||
HgAbstractGlobalAction.handleException(myProject, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
if (anyRepositoryIsFresh()) {
|
||||
e.getPresentation().setEnabled(false);
|
||||
e.getPresentation().setDescription("Checkout of a new branch is not possible before the first commit.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean anyRepositoryIsFresh() {
|
||||
for (HgRepository repository : myRepositories) {
|
||||
if (repository.isFresh()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actions available for branches.
|
||||
*/
|
||||
static class BranchActions extends ActionGroup {
|
||||
|
||||
private final Project myProject;
|
||||
private String myBranchName;
|
||||
@NotNull private final HgRepository mySelectedRepository;
|
||||
|
||||
BranchActions(@NotNull Project project, @NotNull String branchName,
|
||||
@NotNull HgRepository selectedRepository) {
|
||||
super("", true);
|
||||
myProject = project;
|
||||
myBranchName = branchName;
|
||||
mySelectedRepository = selectedRepository;
|
||||
getTemplatePresentation().setText(calcBranchText(), false); // no mnemonics
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String calcBranchText() {
|
||||
return myBranchName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AnAction[] getChildren(@Nullable AnActionEvent e) {
|
||||
return new AnAction[]{
|
||||
new UpdateToAction(myProject, mySelectedRepository, myBranchName),
|
||||
new MergeAction(myProject, mySelectedRepository, myBranchName)
|
||||
};
|
||||
}
|
||||
|
||||
private static class MergeAction extends DumbAwareAction {
|
||||
|
||||
private final Project myProject;
|
||||
private final HgRepository mySelectedRepository;
|
||||
private final String myBranchName;
|
||||
|
||||
public MergeAction(@NotNull Project project,
|
||||
@NotNull HgRepository selectedRepository,
|
||||
@NotNull String branchName) {
|
||||
super("Merge");
|
||||
myProject = project;
|
||||
mySelectedRepository = selectedRepository;
|
||||
myBranchName = branchName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
UpdatedFiles updatedFiles = UpdatedFiles.create();
|
||||
HgMergeCommand hgMergeCommand = new HgMergeCommand(myProject, mySelectedRepository.getRoot());
|
||||
hgMergeCommand.setBranch(myBranchName);
|
||||
//hgMergeCommand.setRevision(myBranchName.getHead().getChangeset());
|
||||
HgCommandResultNotifier notifier = new HgCommandResultNotifier(myProject);
|
||||
try {
|
||||
new HgHeadMerger(myProject, hgMergeCommand)
|
||||
.merge(mySelectedRepository.getRoot(), updatedFiles, HgRevisionNumber.NULL_REVISION_NUMBER);
|
||||
new HgConflictResolver(myProject, updatedFiles).resolve(mySelectedRepository.getRoot());
|
||||
}
|
||||
catch (VcsException exception) {
|
||||
if (exception.isWarning()) {
|
||||
notifier.notifyWarning("Warning during merge", exception.getMessage());
|
||||
}
|
||||
else {
|
||||
notifier.notifyError(null, "Exception during merge", exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpdateToAction extends DumbAwareAction {
|
||||
|
||||
private final Project myProject;
|
||||
private final HgRepository mySelectedRepository;
|
||||
private final String myBranch;
|
||||
|
||||
public UpdateToAction(@NotNull Project project,
|
||||
@NotNull HgRepository selectedRepository,
|
||||
@NotNull String branch) {
|
||||
super("Update To");
|
||||
myProject = project;
|
||||
mySelectedRepository = selectedRepository;
|
||||
myBranch = branch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
HgUpdateCommand hgUpdateCommand = new HgUpdateCommand(myProject, mySelectedRepository.getRoot());
|
||||
hgUpdateCommand.setBranch(myBranch);
|
||||
HgCommandResult result = hgUpdateCommand.execute();
|
||||
if (HgErrorUtil.hasErrorsInCommandExecution(result)) {
|
||||
new HgCommandResultNotifier(myProject).notifyError(result, "", "Update failed");
|
||||
}
|
||||
myProject.getMessageBus().syncPublisher(HgVcs.BRANCH_TOPIC).update(myProject, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.action;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.zmlx.hg4idea.repo.HgRepository;
|
||||
import org.zmlx.hg4idea.repo.HgRepositoryImpl;
|
||||
import org.zmlx.hg4idea.util.HgUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
|
||||
public class HgBranchesAction extends HgAbstractGlobalAction {
|
||||
|
||||
@Override
|
||||
protected void execute(Project project, Collection<VirtualFile> repositories, @Nullable VirtualFile selectedRepo) {
|
||||
HgRepository repository;
|
||||
if (selectedRepo != null) {
|
||||
repository = HgRepositoryImpl.getFullInstance(selectedRepo, project, project);
|
||||
}
|
||||
else {
|
||||
VirtualFile selectedRoot = HgUtil.getRootForSelectedFile(project);
|
||||
if (selectedRoot != null) {
|
||||
repository = HgRepositoryImpl.getFullInstance(selectedRoot, project, project);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
HgBranchPopup.getInstance(project, repository).asListPopup().showInFocusCenter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.command;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.zmlx.hg4idea.execution.HgCommandException;
|
||||
import org.zmlx.hg4idea.execution.HgCommandExecutor;
|
||||
import org.zmlx.hg4idea.execution.HgCommandResultHandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
public class HgBranchCreateCommand {
|
||||
|
||||
private final Project project;
|
||||
private final VirtualFile repo;
|
||||
private final String branchName;
|
||||
|
||||
public HgBranchCreateCommand(@NotNull Project project, @NotNull VirtualFile repo, @Nullable String branchName) {
|
||||
this.project = project;
|
||||
this.repo = repo;
|
||||
this.branchName = branchName;
|
||||
}
|
||||
|
||||
public void execute(@Nullable HgCommandResultHandler resultHandler) throws HgCommandException {
|
||||
if (StringUtil.isEmptyOrSpaces(branchName)) {
|
||||
throw new HgCommandException("branch name is empty");
|
||||
}
|
||||
new HgCommandExecutor(project).execute(repo, "branch", Arrays.asList(branchName), resultHandler);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ public class HgPushCommand {
|
||||
private String myRevision;
|
||||
private boolean myForce;
|
||||
private HgTagBranch myBranch;
|
||||
private boolean myIsNewBranch;
|
||||
|
||||
public HgPushCommand(Project project, @NotNull VirtualFile repo, String destination) {
|
||||
myProject = project;
|
||||
@@ -53,6 +54,10 @@ public class HgPushCommand {
|
||||
myBranch = branch;
|
||||
}
|
||||
|
||||
public void setIsNewBranch(boolean isNewBranch) {
|
||||
myIsNewBranch = isNewBranch;
|
||||
}
|
||||
|
||||
public void execute(final HgCommandResultHandler resultHandler) {
|
||||
final List<String> arguments = new LinkedList<String>();
|
||||
if (!StringUtil.isEmptyOrSpaces(myRevision)) {
|
||||
@@ -60,8 +65,13 @@ public class HgPushCommand {
|
||||
arguments.add(myRevision);
|
||||
}
|
||||
if (myBranch != null) {
|
||||
arguments.add("-b");
|
||||
arguments.add(myBranch.getName());
|
||||
if (myIsNewBranch) {
|
||||
arguments.add("--new-branch");
|
||||
}
|
||||
else {
|
||||
arguments.add("-b");
|
||||
arguments.add(myBranch.getName());
|
||||
}
|
||||
}
|
||||
if (myForce) {
|
||||
arguments.add("-f");
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.repo;
|
||||
|
||||
/**
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
public class HgRepoStateException extends RuntimeException {
|
||||
|
||||
public HgRepoStateException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public HgRepoStateException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.repo;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* HgRepository is a representation of a Hg repository stored under the specified directory.
|
||||
* It stores the information about the repository, which is frequently requested by other plugin components.
|
||||
* All get-methods (like {@link #getCurrentRevision()}) are just getters of the correspondent fields and thus are very fast.
|
||||
* </p>
|
||||
* <p>
|
||||
* The HgRepository is updated "externally" by the {@link org.zmlx.hg4idea.repo.HgRepositoryUpdater}, when correspondent {@code .hg/} service files
|
||||
* change.
|
||||
* </p>
|
||||
* Other components may subscribe to HgRepository changes via the STATUS_TOPIC {@link com.intellij.util.messages.Topic}
|
||||
* </p>
|
||||
*
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
|
||||
public interface HgRepository {
|
||||
|
||||
String DEFAULT_BRANCH = "default";
|
||||
|
||||
/**
|
||||
* Current state of the repository.
|
||||
*/
|
||||
|
||||
enum State {
|
||||
/**
|
||||
* HEAD is on branch, no merge process is in progress.
|
||||
*/
|
||||
|
||||
NORMAL,
|
||||
/**
|
||||
* During merge (for instance, merge failed with conflicts that weren't immediately resolved).
|
||||
*/
|
||||
|
||||
MERGING {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Merging";
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
VirtualFile getRoot();
|
||||
|
||||
@NotNull
|
||||
VirtualFile getHgDir();
|
||||
|
||||
@NotNull
|
||||
String getPresentableUrl();
|
||||
|
||||
@NotNull
|
||||
Project getProject();
|
||||
|
||||
|
||||
@NotNull
|
||||
State getState();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the hash of the revision, which HEAD currently points to.
|
||||
* Returns null only in the case of a fresh repository, when no commit have been made.
|
||||
*/
|
||||
|
||||
@Nullable
|
||||
String getCurrentRevision();
|
||||
|
||||
/**
|
||||
* Returns the current branch of this Hg repository.
|
||||
* If the repository is being rebased, then the current branch is the branch being rebased (which was current before the rebase
|
||||
* operation has started).
|
||||
* Returns null, if the repository is not on a branch and not in the REBASING state.
|
||||
*/
|
||||
|
||||
@NotNull
|
||||
String getCurrentBranch();
|
||||
|
||||
@NotNull
|
||||
Collection<String> getBranches();
|
||||
|
||||
/**
|
||||
* @return true if current repository is "fresh", i.e. if no commits have been made yet.
|
||||
*/
|
||||
|
||||
boolean isFresh();
|
||||
|
||||
/**
|
||||
* Updates the HgRepository by reading information from .hg dir...
|
||||
*/
|
||||
|
||||
void update();
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.repo;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Stores paths to Hg service files (from .hg/ directory) that are used by IDEA, and provides methods to check if a file
|
||||
* matches once of them.
|
||||
*
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
public class HgRepositoryFiles {
|
||||
|
||||
public static final String BRANCHHEADS = "cache/branchheads";
|
||||
public static final String MERGE = "merge";
|
||||
public static final String BRANCH = "branch";
|
||||
|
||||
|
||||
private final String myBranchHeadsPath;
|
||||
private final String myMergePath;
|
||||
private final String myBranchPath;
|
||||
|
||||
|
||||
public static HgRepositoryFiles getInstance(@NotNull VirtualFile hgDir) {
|
||||
return new HgRepositoryFiles(hgDir);
|
||||
}
|
||||
|
||||
private HgRepositoryFiles(@NotNull VirtualFile hgDir) {
|
||||
myBranchHeadsPath = hgDir.getPath() + slash(BRANCHHEADS);
|
||||
myBranchPath = hgDir.getPath() + slash(BRANCH);
|
||||
myMergePath = hgDir.getPath() + slash(MERGE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String slash(@NotNull String s) {
|
||||
return "/" + s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns subdirectories of .hg which we are interested in - they should be watched by VFS.
|
||||
*/
|
||||
@NotNull
|
||||
static Collection<String> getSubDirRelativePaths() {
|
||||
return Arrays.asList(slash(BRANCHHEADS), slash(MERGE));
|
||||
}
|
||||
|
||||
public String getBranchHeadsPath() {
|
||||
return myBranchHeadsPath;
|
||||
}
|
||||
|
||||
|
||||
public boolean isbranchHeadsFile(String filePath) {
|
||||
return filePath.equals(myBranchHeadsPath);
|
||||
}
|
||||
|
||||
public boolean isBranchFile(String filePath) {
|
||||
return filePath.equals(myBranchPath);
|
||||
}
|
||||
|
||||
public boolean isMergeFile(String filePath) {
|
||||
return filePath.startsWith(myMergePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.repo;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.concurrency.QueueProcessor;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.zmlx.hg4idea.HgVcs;
|
||||
import org.zmlx.hg4idea.util.HgUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
|
||||
public class HgRepositoryImpl implements HgRepository, Disposable {
|
||||
|
||||
private static final Object STUB_OBJECT = new Object();
|
||||
|
||||
private final Project myProject;
|
||||
private final VirtualFile myRootDir;
|
||||
private final HgRepositoryReader myReader;
|
||||
private final VirtualFile myHgDir;
|
||||
private final QueueProcessor<Object> myNotifier;
|
||||
|
||||
@NotNull private volatile State myState = State.NORMAL;
|
||||
@Nullable private volatile String myCurrentRevision = null;
|
||||
@NotNull private volatile String myCurrentBranch = DEFAULT_BRANCH;
|
||||
@NotNull private volatile List<String> myBranches = Collections.emptyList();
|
||||
|
||||
|
||||
/*
|
||||
* Get the HgRepository instance from the {@link HgRepositoryManager}.
|
||||
* If you need to have an instance of HgRepository for a repository outside the project, use
|
||||
* {@link #getLightInstance(com.intellij.openapi.vfs.VirtualFile, com.intellij.openapi.project.Project, PlatformFacade, com.intellij.openapi.Disposable)}.
|
||||
|
||||
*/
|
||||
protected HgRepositoryImpl(@NotNull VirtualFile rootDir, @NotNull Project project,
|
||||
@NotNull Disposable parentDisposable) {
|
||||
myRootDir = rootDir;
|
||||
myProject = project;
|
||||
Disposer.register(parentDisposable, this);
|
||||
|
||||
myHgDir = myRootDir.findChild(HgUtil.DOT_HG);
|
||||
assert myHgDir != null : ".hg directory wasn't found under " + rootDir.getPresentableUrl();
|
||||
|
||||
myReader = new HgRepositoryReader(VfsUtilCore.virtualToIoFile(myHgDir));
|
||||
|
||||
MessageBus messageBus = project.getMessageBus();
|
||||
myNotifier = new QueueProcessor<Object>(new NotificationConsumer(myProject, messageBus, this), myProject.getDisposed());
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
public static HgRepository getFullInstance(@NotNull VirtualFile root, @NotNull Project project,
|
||||
@NotNull Disposable parentDisposable) {
|
||||
HgRepositoryImpl repository = new HgRepositoryImpl(root, project, parentDisposable);
|
||||
repository.setupUpdater();
|
||||
return repository;
|
||||
}
|
||||
|
||||
private void setupUpdater() {
|
||||
HgRepositoryUpdater updater = new HgRepositoryUpdater(this);
|
||||
Disposer.register(this, updater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public VirtualFile getRoot() {
|
||||
return myRootDir;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public VirtualFile getHgDir() {
|
||||
return myHgDir;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getPresentableUrl() {
|
||||
return getRoot().getPresentableUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public State getState() {
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getCurrentRevision() {
|
||||
return myCurrentRevision;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getCurrentBranch() {
|
||||
return myCurrentBranch;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<String> getBranches() {
|
||||
return myBranches;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isFresh() {
|
||||
return !myReader.headExist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
readRepository();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
private void readRepository() {
|
||||
if (!isFresh()) {
|
||||
myState = myReader.readState();
|
||||
myCurrentRevision = myReader.readCurrentRevision();
|
||||
myCurrentBranch = myReader.readCurrentBranch();
|
||||
myBranches = myReader.readBranches();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyListeners() {
|
||||
myNotifier.add(STUB_OBJECT); // we don't have parameters for listeners
|
||||
}
|
||||
|
||||
private static class NotificationConsumer implements Consumer<Object> {
|
||||
|
||||
private final Project myProject;
|
||||
private final MessageBus myMessageBus;
|
||||
private final HgRepository myRepository;
|
||||
|
||||
NotificationConsumer(Project project, MessageBus messageBus, HgRepository repository) {
|
||||
myProject = project;
|
||||
myMessageBus = messageBus;
|
||||
myRepository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(Object o) {
|
||||
if (!Disposer.isDisposed(myProject)) {
|
||||
myMessageBus.syncPublisher(HgVcs.STATUS_TOPIC).update(myProject, myRepository.getRoot());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getPresentableUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
HgRepositoryImpl that = (HgRepositoryImpl)o;
|
||||
|
||||
if (!myProject.equals(that.myProject)) return false;
|
||||
if (!myRootDir.equals(that.myRootDir)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myProject.hashCode();
|
||||
result = 31 * result + (myRootDir.hashCode());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.repo;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Reads information about the Hg repository from Hg service files located in the {@code .hg} folder.
|
||||
* NB: works with {@link java.io.File}, i.e. reads from disk. Consider using caching.
|
||||
* Throws a {@link HgRepoStateException} in the case of incorrect Hg file format.
|
||||
*
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
class HgRepositoryReader {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(HgRepositoryReader.class);
|
||||
private static Pattern BRANCH_PATTERN = Pattern.compile("\\s*(.+)\\s+(.+)");
|
||||
|
||||
private static final int IO_RETRIES = 3; // number of retries before fail if an IOException happens during file read.
|
||||
|
||||
private final File myHgDir; // .hg/
|
||||
private final File myBranchHeadsFile; // .hg/cache/branchheads - this file does not exist before first commit
|
||||
private final File myCurrentBranch; // .hg/branch
|
||||
|
||||
HgRepositoryReader(@NotNull File hgDir) {
|
||||
myHgDir = hgDir;
|
||||
assertFileExists(myHgDir, ".hg directory not found in " + hgDir);
|
||||
myBranchHeadsFile = new File(new File(myHgDir, "cache"), "branchheads");
|
||||
myCurrentBranch = new File(myHgDir, "branch");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds current revision value.
|
||||
*
|
||||
* @return The current revision hash, or <b>{@code null}</b> if current revision is unknown - it is the initial repository state.
|
||||
*/
|
||||
@Nullable
|
||||
String readCurrentRevision() {
|
||||
String[] branchesWithHeads = tryLoadFile(myBranchHeadsFile).trim().split("\n");
|
||||
String head = branchesWithHeads[0];
|
||||
Matcher matcher = BRANCH_PATTERN.matcher(head);
|
||||
if (matcher.matches()) {
|
||||
return (matcher.group(1));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current branch
|
||||
*/
|
||||
@NotNull
|
||||
String readCurrentBranch() {
|
||||
if (branchExist()) {
|
||||
String rev = tryLoadFile(myCurrentBranch);
|
||||
return rev.trim();
|
||||
}
|
||||
return HgRepository.DEFAULT_BRANCH;
|
||||
}
|
||||
|
||||
|
||||
List<String> readBranches() {
|
||||
List<String> branches = new ArrayList<String>();
|
||||
String[] branchesWithHeads = tryLoadFile(myBranchHeadsFile).trim().split("\n");
|
||||
// first one - is a head revision: head hash + head number;
|
||||
for (int i = 1; i < branchesWithHeads.length; ++i) {
|
||||
Matcher matcher = BRANCH_PATTERN.matcher(branchesWithHeads[i]);
|
||||
if (matcher.matches()) {
|
||||
branches.add(matcher.group(2));
|
||||
}
|
||||
}
|
||||
return branches;
|
||||
}
|
||||
|
||||
|
||||
private static void assertFileExists(File file, String message) {
|
||||
if (!file.exists()) {
|
||||
throw new HgRepoStateException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the file content.
|
||||
* Tries 3 times, then a {@link HgRepoStateException} is thrown.
|
||||
*
|
||||
* @param file File to read.
|
||||
* @return file content.
|
||||
*/
|
||||
@NotNull
|
||||
private static String tryLoadFile(final File file) {
|
||||
return tryOrThrow(new Callable<String>() {
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
return FileUtil.loadFile(file);
|
||||
}
|
||||
}, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to execute the given action.
|
||||
* If an IOException happens, tries again up to 3 times, and then throws a {@link HgRepoStateException}.
|
||||
* If an other exception happens, rethrows it as a {@link HgRepoStateException}.
|
||||
* In the case of success returns the result of the task execution.
|
||||
*/
|
||||
private static String tryOrThrow(Callable<String> actionToTry, File fileToLoad) {
|
||||
IOException cause = null;
|
||||
for (int i = 0; i < IO_RETRIES; i++) {
|
||||
try {
|
||||
return actionToTry.call();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info("IOException while loading " + fileToLoad, e);
|
||||
cause = e;
|
||||
}
|
||||
catch (Exception e) { // this shouldn't happen since only IOExceptions are thrown in clients.
|
||||
throw new HgRepoStateException("Couldn't load file " + fileToLoad, e);
|
||||
}
|
||||
}
|
||||
throw new HgRepoStateException("Couldn't load file " + fileToLoad, cause);
|
||||
}
|
||||
|
||||
public HgRepository.State readState() {
|
||||
if (isMergeInProgress()) {
|
||||
return HgRepository.State.MERGING;
|
||||
}
|
||||
return HgRepository.State.NORMAL;
|
||||
}
|
||||
|
||||
private boolean isMergeInProgress() {
|
||||
File mergeFile = new File(myHgDir, "merge");
|
||||
return mergeFile.exists();
|
||||
}
|
||||
|
||||
public boolean headExist() {
|
||||
return myBranchHeadsFile.exists();
|
||||
}
|
||||
|
||||
public boolean branchExist() {
|
||||
return myCurrentBranch.exists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.zmlx.hg4idea.repo;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.concurrency.QueueProcessor;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.vcsUtil.VcsUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Listens to .hg service files changes and updates {@link HgRepository} when needed.
|
||||
*
|
||||
* @author Nadya Zabrodina
|
||||
*/
|
||||
final class HgRepositoryUpdater implements Disposable, BulkFileListener {
|
||||
private final HgRepositoryFiles myRepositoryFiles;
|
||||
private final MessageBusConnection myMessageBusConnection;
|
||||
private final QueueProcessor<Object> myUpdateQueue;
|
||||
private final Object DUMMY_UPDATE_OBJECT = new Object();
|
||||
private final VirtualFile myBranchHeadsDir;
|
||||
private final LocalFileSystem.WatchRequest myWatchRequest;
|
||||
|
||||
|
||||
HgRepositoryUpdater(HgRepository repository) {
|
||||
VirtualFile hgDir = repository.getHgDir();
|
||||
myWatchRequest = LocalFileSystem.getInstance().addRootToWatch(hgDir.getPath(), true);
|
||||
myRepositoryFiles = HgRepositoryFiles.getInstance(hgDir);
|
||||
visitHgDirVfs(hgDir);
|
||||
|
||||
myBranchHeadsDir = VcsUtil.getVirtualFile(myRepositoryFiles.getBranchHeadsPath());
|
||||
|
||||
Project project = repository.getProject();
|
||||
myUpdateQueue = new QueueProcessor<Object>(new Updater(repository), project.getDisposed());
|
||||
if (!project.isDisposed()) {
|
||||
myMessageBusConnection = project.getMessageBus().connect();
|
||||
myMessageBusConnection.subscribe(VirtualFileManager.VFS_CHANGES, this);
|
||||
}
|
||||
else {
|
||||
myMessageBusConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void visitHgDirVfs(@NotNull VirtualFile hgDir) {
|
||||
hgDir.getChildren();
|
||||
for (String subdir : HgRepositoryFiles.getSubDirRelativePaths()) {
|
||||
VirtualFile dir = hgDir.findFileByRelativePath(subdir);
|
||||
// process recursively, because we need to visit all branches under cache/branchheads
|
||||
visitAllChildrenRecursively(dir);
|
||||
}
|
||||
}
|
||||
|
||||
private static void visitAllChildrenRecursively(@Nullable VirtualFile dir) {
|
||||
if (dir == null) {
|
||||
return;
|
||||
}
|
||||
VfsUtil.processFilesRecursively(dir, new Processor<VirtualFile>() {
|
||||
@Override
|
||||
public boolean process(VirtualFile virtualFile) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (myWatchRequest != null) {
|
||||
LocalFileSystem.getInstance().removeWatchedRoot(myWatchRequest);
|
||||
}
|
||||
if (myMessageBusConnection != null) {
|
||||
myMessageBusConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void before(@NotNull List<? extends VFileEvent> events) {
|
||||
// everything is handled in #after()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void after(@NotNull List<? extends VFileEvent> events) {
|
||||
// which files in .hg were changed
|
||||
boolean branchHeadsChanged = false;
|
||||
boolean branchFileChanged = false;
|
||||
boolean mergeFileChanged = false;
|
||||
for (VFileEvent event : events) {
|
||||
String filePath = event.getPath();
|
||||
if (filePath == null) {
|
||||
continue;
|
||||
}
|
||||
if (myRepositoryFiles.isbranchHeadsFile(filePath)) {
|
||||
branchHeadsChanged = true;
|
||||
}
|
||||
else if (myRepositoryFiles.isBranchFile(filePath)) {
|
||||
branchFileChanged = true;
|
||||
visitAllChildrenRecursively(myBranchHeadsDir);
|
||||
}
|
||||
else if (myRepositoryFiles.isMergeFile(filePath)) {
|
||||
mergeFileChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (branchHeadsChanged || branchFileChanged || mergeFileChanged) {
|
||||
myUpdateQueue.add(DUMMY_UPDATE_OBJECT);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Updater implements Consumer<Object> {
|
||||
private final HgRepository myRepository;
|
||||
|
||||
public Updater(HgRepository repository) {
|
||||
myRepository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(Object dummy) {
|
||||
myRepository.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.ListPopup;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.StatusBarWidget;
|
||||
@@ -31,22 +32,25 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.zmlx.hg4idea.HgProjectSettings;
|
||||
import org.zmlx.hg4idea.HgUpdater;
|
||||
import org.zmlx.hg4idea.HgVcs;
|
||||
import org.zmlx.hg4idea.action.HgBranchPopup;
|
||||
import org.zmlx.hg4idea.repo.HgRepositoryImpl;
|
||||
import org.zmlx.hg4idea.status.HgCurrentBranchStatus;
|
||||
import org.zmlx.hg4idea.util.HgUtil;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
/**
|
||||
* Widget to display basic hg status in the IJ status bar.
|
||||
*/
|
||||
public class HgStatusWidget extends EditorBasedWidget implements StatusBarWidget.TextPresentation, StatusBarWidget.Multiframe, HgUpdater {
|
||||
public class HgStatusWidget extends EditorBasedWidget
|
||||
implements StatusBarWidget.MultipleTextValuesPresentation,
|
||||
StatusBarWidget.Multiframe, HgUpdater {
|
||||
|
||||
private static final String MAX_STRING = "hg: default branch (128)";
|
||||
|
||||
@NotNull private final HgVcs myVcs;
|
||||
@NotNull private final HgProjectSettings myProjectSettings;
|
||||
@NotNull private final HgCurrentBranchStatus myCurrentBranchStatus;
|
||||
private MessageBusConnection myBusConnection;
|
||||
|
||||
private volatile String myText = "";
|
||||
private volatile String myTooltip = "";
|
||||
@@ -91,13 +95,20 @@ public class HgStatusWidget extends EditorBasedWidget implements StatusBarWidget
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTooltipText() {
|
||||
return myTooltip;
|
||||
public ListPopup getPopupStep() {
|
||||
Project project = getProject();
|
||||
if (project == null) {
|
||||
return null;
|
||||
}
|
||||
VirtualFile root = HgUtil.getRootForSelectedFile(project);
|
||||
if (root != null) {
|
||||
return HgBranchPopup.getInstance(project, HgRepositoryImpl.getFullInstance(root, project, project)).asListPopup();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
public String getSelectedValue() {
|
||||
final String text = myText;
|
||||
return StringUtil.isEmpty(text) ? "" : "hg: " + text;
|
||||
}
|
||||
@@ -105,15 +116,17 @@ public class HgStatusWidget extends EditorBasedWidget implements StatusBarWidget
|
||||
@NotNull
|
||||
@Override
|
||||
@Deprecated
|
||||
public String getMaxPossibleText() {
|
||||
public String getMaxValue() {
|
||||
return MAX_STRING;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float getAlignment() {
|
||||
return Component.LEFT_ALIGNMENT;
|
||||
public String getTooltipText() {
|
||||
return myTooltip;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
// Updates branch information on click
|
||||
public Consumer<MouseEvent> getClickConsumer() {
|
||||
@@ -161,8 +174,8 @@ public class HgStatusWidget extends EditorBasedWidget implements StatusBarWidget
|
||||
return;
|
||||
}
|
||||
|
||||
myBusConnection = project.getMessageBus().connect();
|
||||
myBusConnection.subscribe(HgVcs.STATUS_TOPIC, this);
|
||||
MessageBusConnection busConnection = project.getMessageBus().connect();
|
||||
busConnection.subscribe(HgVcs.STATUS_TOPIC, this);
|
||||
|
||||
DvcsUtil.installStatusBarWidget(myProject, this);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<grid id="3b42" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="3b42" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
|
||||
@@ -55,12 +55,12 @@
|
||||
</component>
|
||||
<hspacer id="27a66">
|
||||
<constraints>
|
||||
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<component id="5fcc4" class="javax.swing.JCheckBox" binding="forceCheckBox" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="org/zmlx/hg4idea/HgVcsMessages" key="hg4idea.push.force"/>
|
||||
@@ -80,6 +80,20 @@
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="e76b" class="javax.swing.JCheckBox" binding="newBranchCheckBox">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<enabled value="false"/>
|
||||
<text resource-bundle="org/zmlx/hg4idea/HgVcsMessages" key="hg4idea.push.asNewBranch"/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="af930">
|
||||
<constraints>
|
||||
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
</children>
|
||||
</grid>
|
||||
<nested-form id="9e17d" form-file="org/zmlx/hg4idea/ui/HgRepositorySelectorComponent.form" binding="hgRepositorySelectorComponent">
|
||||
|
||||
@@ -51,6 +51,7 @@ public class HgPushDialog extends DialogWrapper {
|
||||
private JCheckBox branchCheckBox;
|
||||
private JComboBox branchComboBox;
|
||||
private EditorComboBox myRepositoryURL;
|
||||
private JCheckBox newBranchCheckBox;
|
||||
private String myCurrentRepositoryUrl;
|
||||
|
||||
public HgPushDialog(Project project, Collection<VirtualFile> repos, List<HgTagBranch> branches, @Nullable VirtualFile selectedRepo) {
|
||||
@@ -114,6 +115,10 @@ public class HgPushDialog extends DialogWrapper {
|
||||
return forceCheckBox.isSelected();
|
||||
}
|
||||
|
||||
public boolean isNewBranch() {
|
||||
return newBranchCheckBox.isSelected();
|
||||
}
|
||||
|
||||
protected JComponent createCenterPanel() {
|
||||
return contentPanel;
|
||||
}
|
||||
@@ -159,6 +164,7 @@ public class HgPushDialog extends DialogWrapper {
|
||||
setOKActionEnabled(validateOptions());
|
||||
revisionTxt.setEnabled(revisionCbx.isSelected());
|
||||
branchComboBox.setEnabled(branchCheckBox.isSelected());
|
||||
newBranchCheckBox.setEnabled(branchCheckBox.isSelected());
|
||||
}
|
||||
|
||||
private boolean validateOptions() {
|
||||
|
||||
@@ -14,7 +14,14 @@ package org.zmlx.hg4idea.util;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.TextEditor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.ShutDownTracker;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vcs.*;
|
||||
@@ -24,6 +31,9 @@ import com.intellij.openapi.vcs.history.VcsFileRevisionEx;
|
||||
import com.intellij.openapi.vcs.vfs.VcsVirtualFile;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.StatusBar;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.openapi.wm.impl.status.StatusBarUtil;
|
||||
import com.intellij.ui.GuiUtils;
|
||||
import com.intellij.vcsUtil.VcsUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -33,6 +43,7 @@ import org.zmlx.hg4idea.command.HgRemoveCommand;
|
||||
import org.zmlx.hg4idea.command.HgStatusCommand;
|
||||
import org.zmlx.hg4idea.command.HgWorkingCopyRevisionsCommand;
|
||||
import org.zmlx.hg4idea.provider.HgChangeProvider;
|
||||
import org.zmlx.hg4idea.repo.HgRepository;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
@@ -50,6 +61,7 @@ public abstract class HgUtil {
|
||||
public static final Pattern URL_WITH_PASSWORD = Pattern.compile("(?:.+)://(?:.+)(:.+)@(?:.+)"); //http(s)://username:password@url
|
||||
public static final int MANY_FILES = 100;
|
||||
private static final Logger LOG = Logger.getInstance(HgUtil.class);
|
||||
public static final String DOT_HG = ".hg";
|
||||
|
||||
public static File copyResourceToTempFile(String basename, String extension) throws IOException {
|
||||
final InputStream in = HgUtil.class.getClassLoader().getResourceAsStream("python/" + basename + extension);
|
||||
@@ -203,7 +215,7 @@ public abstract class HgUtil {
|
||||
* Checks if the given directory is an hg root.
|
||||
*/
|
||||
public static boolean isHgRoot(VirtualFile dir) {
|
||||
return dir.findChild(".hg") != null;
|
||||
return dir.findChild(DOT_HG) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,6 +261,52 @@ public abstract class HgUtil {
|
||||
return getHgRootOrThrow(project, VcsUtil.getFilePath(file.getPath()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently selected file, based on which HgBranch components will identify the current repository root.
|
||||
*/
|
||||
@Nullable
|
||||
public static VirtualFile getSelectedFile(@NotNull Project project) {
|
||||
StatusBar statusBar = WindowManager.getInstance().getStatusBar(project);
|
||||
final FileEditor fileEditor = StatusBarUtil.getCurrentFileEditor(project, statusBar);
|
||||
VirtualFile result = null;
|
||||
if (fileEditor != null) {
|
||||
if (fileEditor instanceof TextEditor) {
|
||||
Document document = ((TextEditor)fileEditor).getEditor().getDocument();
|
||||
result = FileDocumentManager.getInstance().getFile(document);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
final FileEditorManager manager = FileEditorManager.getInstance(project);
|
||||
if (manager != null) {
|
||||
Editor editor = manager.getSelectedTextEditor();
|
||||
if (editor != null) {
|
||||
result = FileDocumentManager.getInstance().getFile(editor.getDocument());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile getRootForSelectedFile(@NotNull Project project) {
|
||||
VirtualFile selectedFile = getSelectedFile(project);
|
||||
if (selectedFile != null) {
|
||||
return getHgRootOrNull(project, selectedFile);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shows a message dialog to enter the name of new branch.
|
||||
* @return name of new branch or {@code null} if user has cancelled the dialog.
|
||||
*/
|
||||
@Nullable
|
||||
public static String getNewBranchNameFromUser(@NotNull Project project,
|
||||
@NotNull String dialogTitle) {
|
||||
return Messages.showInputDialog(project, "Enter the name of new branch:", dialogTitle, Messages.getQuestionIcon());
|
||||
}
|
||||
/**
|
||||
* Checks is a merge operation is in progress on the given repository.
|
||||
* Actually gets the number of parents of the current revision. If there are 2 parents, then a merge is going on. Otherwise there is
|
||||
@@ -481,4 +539,13 @@ public abstract class HgUtil {
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String getDisplayableBranchText(HgRepository repository) {
|
||||
HgRepository.State state = repository.getState();
|
||||
String branchText = "";
|
||||
if (state == HgRepository.State.MERGING) {
|
||||
branchText += state.toString() + " ";
|
||||
}
|
||||
return branchText + repository.getCurrentBranch();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user