diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchPair.java b/plugins/git4idea/src/git4idea/branch/GitBranchPair.java new file mode 100644 index 000000000000..be898be1640a --- /dev/null +++ b/plugins/git4idea/src/git4idea/branch/GitBranchPair.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2011 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 git4idea.branch; + +import git4idea.GitBranch; + +/** + * Holder for branch and its tracked branch (if any). + * + * @author Kirill Likhodedov + */ +public class GitBranchPair { + private GitBranch myBranch; + private GitBranch myTrackedName; + + public GitBranchPair(GitBranch branch, GitBranch tracked) { + myBranch = branch; + myTrackedName = tracked; + } + + public GitBranch getBranch() { + return myBranch; + } + + public GitBranch getTracked() { + return myTrackedName; + } + +} diff --git a/plugins/git4idea/src/git4idea/update/GitChangesSaver.java b/plugins/git4idea/src/git4idea/update/GitChangesSaver.java index 5fbbeacba856..68a1b9a2d6f0 100644 --- a/plugins/git4idea/src/git4idea/update/GitChangesSaver.java +++ b/plugins/git4idea/src/git4idea/update/GitChangesSaver.java @@ -26,11 +26,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ui.UIUtil; import git4idea.GitVcs; import git4idea.config.GitVcsSettings; import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; import java.util.Collection; @@ -86,10 +88,14 @@ public abstract class GitChangesSaver { /** * Saves local changes in stash or in shelf. + * @param rootsToSave Save changes only from these roots. */ - public void saveLocalChanges() throws VcsException { + public void saveLocalChanges(@Nullable Collection rootsToSave) throws VcsException { + if (rootsToSave == null || rootsToSave.isEmpty()) { + return; + } myChangeLists = myChangeManager.getChangeListsCopy(); - save(); + save(rootsToSave); } /** @@ -111,10 +117,33 @@ public abstract class GitChangesSaver { } } + public List getChangeLists() { + return myChangeLists == null ? myChangeManager.getChangeLists() : myChangeLists; + } + + /** + * Utility method - gets {@link FilePath}s of changed files in a single collection. + */ + public Collection getChangedFiles() { + final HashSet files = new HashSet(); + for (LocalChangeList changeList : getChangeLists()) { + for (Change c : changeList.getChanges()) { + if (c.getAfterRevision() != null) { + files.add(c.getAfterRevision().getFile()); + } + if (c.getBeforeRevision() != null) { + files.add(c.getBeforeRevision().getFile()); + } + } + } + return files; + } + /** * Saves local changes - specific for chosen save strategy. + * @param rootsToSave local changes should be saved on these roots. */ - protected abstract void save() throws VcsException; + protected abstract void save(Collection rootsToSave) throws VcsException; /** * Loads the changes - specific for chosen save strategy. @@ -136,30 +165,15 @@ public abstract class GitChangesSaver { */ protected abstract void showSavedChanges(); - /** - * Utility method - gets {@link FilePath}s of changed files in a single collection. - */ - protected Collection getChangedFiles() { - final HashSet files = new HashSet(); - for (LocalChangeList changeList : myChangeLists) { - for (Change c : changeList.getChanges()) { - if (c.getAfterRevision() != null) { - files.add(c.getAfterRevision().getFile()); - } - if (c.getBeforeRevision() != null) { - files.add(c.getBeforeRevision().getFile()); - } - } - } - return files; - } - // Move files back to theirs change lists private void restoreChangeLists() { UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { myChangeManager.invokeAfterUpdate(new Runnable() { public void run() { + if (myChangeLists == null) { + return; + } for (LocalChangeList changeList : myChangeLists) { final Collection changes = changeList.getChanges(); LOG.debug( diff --git a/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java b/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java index 4488d49414a0..ad456f7c0606 100644 --- a/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java +++ b/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java @@ -19,16 +19,23 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; -import git4idea.GitVcs; +import git4idea.GitUtil; +import git4idea.branch.GitBranchPair; import git4idea.commands.*; import git4idea.merge.GitMergeConflictResolver; import git4idea.merge.GitMerger; import git4idea.ui.GitUIUtil; +import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** @@ -37,7 +44,7 @@ import java.util.concurrent.atomic.AtomicReference; public class GitMergeUpdater extends GitUpdater { private static final Logger LOG = Logger.getInstance(GitMergeUpdater.class); - private GitVcs myVcs; + private final GitUpdateProcess myUpdateProcess; private enum MergeError { CONFLICT, @@ -45,17 +52,21 @@ public class GitMergeUpdater extends GitUpdater { OTHER } - public GitMergeUpdater(Project project, VirtualFile root, ProgressIndicator progressIndicator, UpdatedFiles updatedFiles) { + public GitMergeUpdater(Project project, + VirtualFile root, + GitUpdateProcess gitUpdateProcess, + ProgressIndicator progressIndicator, + UpdatedFiles updatedFiles) { super(project, root, progressIndicator, updatedFiles); - myVcs = GitVcs.getInstance(project); + myUpdateProcess = gitUpdateProcess; } @Override protected GitUpdateResult doUpdate() { final GitMerger merger = new GitMerger(myProject); - final GitLineHandler pullHandler = makePullHandler(myRoot); + final GitLineHandler mergeHandler = makeMergeHandler(myRoot); final AtomicReference mergeError = new AtomicReference(MergeError.OTHER); - pullHandler.addLineListener(new GitLineHandlerAdapter() { + mergeHandler.addLineListener(new GitLineHandlerAdapter() { @Override public void onLineAvailable(String line, Key outputType) { if (line.contains("Automatic merge failed; fix conflicts and then commit the result")) { @@ -66,11 +77,11 @@ public class GitMergeUpdater extends GitUpdater { } }); - final GitTask pullTask = new GitTask(myProject, pullHandler, "git pull"); - pullTask.setExecuteResultInAwt(false); - pullTask.setProgressAnalyzer(new GitStandardProgressAnalyzer()); + final GitTask mergeTask = new GitTask(myProject, mergeHandler, "git merge"); + mergeTask.setExecuteResultInAwt(false); + mergeTask.setProgressAnalyzer(new GitStandardProgressAnalyzer()); final AtomicReference updateResult = new AtomicReference(); - pullTask.executeInBackground(true, new GitTaskResultHandlerAdapter() { + mergeTask.executeInBackground(true, new GitTaskResultHandlerAdapter() { @Override protected void onSuccess() { updateResult.set(GitUpdateResult.SUCCESS); } @@ -83,21 +94,22 @@ public class GitMergeUpdater extends GitUpdater { @Override protected void onFailure() { final MergeError error = mergeError.get(); if (error == MergeError.CONFLICT) { - final boolean allMerged = new GitMergeConflictResolver(myProject, true, "Merge conflicts detected. Resolve them before continuing update.", - "Can't update", "") { - @Override protected boolean proceedIfNothingToMerge() throws VcsException { - merger.mergeCommit(myRoot); - return true; - } + final boolean allMerged = + new GitMergeConflictResolver(myProject, true, "Merge conflicts detected. Resolve them before continuing update.", + "Can't update", "") { + @Override protected boolean proceedIfNothingToMerge() throws VcsException { + merger.mergeCommit(myRoot); + return true; + } - @Override protected boolean proceedAfterAllMerged() throws VcsException { - merger.mergeCommit(myRoot); - return true; - } - }.mergeFiles(Collections.singleton(myRoot)); + @Override protected boolean proceedAfterAllMerged() throws VcsException { + merger.mergeCommit(myRoot); + return true; + } + }.mergeFiles(Collections.singleton(myRoot)); updateResult.set(allMerged ? GitUpdateResult.SUCCESS : GitUpdateResult.INCOMPLETE); } else { - GitUIUtil.notifyImportantError(myProject, "Error merging", GitUIUtil.stringifyErrors(pullHandler.errors())); + GitUIUtil.notifyImportantError(myProject, "Error merging", GitUIUtil.stringifyErrors(mergeHandler.errors())); updateResult.set(GitUpdateResult.ERROR); } } @@ -105,6 +117,81 @@ public class GitMergeUpdater extends GitUpdater { return updateResult.get(); } + @Override + public boolean isSaveNeeded() { + boolean fetchSuccess = fetch(); + if (!fetchSuccess) { + return true; // fail safe: fetch failed, will save the root. + } + + // git log --name-status master..origin/master + GitBranchPair gitBranchPair = myUpdateProcess.getTrackedBranches().get(myRoot); + String currentBranch = gitBranchPair.getBranch().getName(); + String remoteBranch = gitBranchPair.getTracked().getName(); + try { + Collection remotelyChanged = getRemotelyChangedPaths(currentBranch, remoteBranch); + Collection locallyChanged = myUpdateProcess.getSaver().getChangedFiles(); + for (FilePath localPath : locallyChanged) { + if (remotelyChanged.contains(localPath.getPath())) { // found a file which was changed locally and remotely => need to save + return true; + } + } + return false; + } catch (VcsException e) { + LOG.info("failed to get remotely changed files for " + currentBranch + ".." + remoteBranch, e); + return true; // fail safe + } + } + + /** + * Fetches the tracked remote for current branch. + * @return true if fetch was successful, false in the case of error. + * @param remote + */ + private boolean fetch() { + final GitLineHandler h = new GitLineHandler(myProject, myRoot, GitCommand.FETCH); + + final GitTask fetchTask = new GitTask(myProject, h, "Fetching changes..."); + fetchTask.setProgressAnalyzer(new GitStandardProgressAnalyzer()); + final AtomicBoolean success = new AtomicBoolean(); + fetchTask.executeInBackground(true, new GitTaskResultHandlerAdapter() { + @Override protected void onSuccess() { + success.set(true); + } + + @Override protected void onCancel() { + LOG.info("Cancelled fetch."); + } + + @Override protected void onFailure() { + LOG.info("Error fetching: " + h.errors()); + } + }); + return success.get(); + } + + // git log --name-status master..origin/master + private @NotNull Collection getRemotelyChangedPaths(String currentBranch, String remoteBranch) throws VcsException { + final GitSimpleHandler toPull = new GitSimpleHandler(myProject, myRoot, GitCommand.LOG); + toPull.addParameters("--name-only", "--pretty=format:"); + toPull.addParameters(currentBranch + ".." + remoteBranch); + toPull.setNoSSH(true); + toPull.setStdoutSuppressed(true); + toPull.setStderrSuppressed(true); + final String output = toPull.run(); + + final Collection remoteChanges = new HashSet(); + for (StringScanner s = new StringScanner(output); s.hasMoreData();) { + final String relative = s.line(); + if (StringUtil.isEmptyOrSpaces(relative)) { + continue; + } + final String path = myRoot.getPath() + "/" + GitUtil.unescapePath(relative); + remoteChanges.add(path); + } + return remoteChanges; + } + private void cancel() { try { GitSimpleHandler h = new GitSimpleHandler(myProject, myRoot, GitCommand.RESET); @@ -116,7 +203,7 @@ public class GitMergeUpdater extends GitUpdater { } } - protected GitLineHandler makePullHandler(VirtualFile root) { + private GitLineHandler makeMergeHandler(VirtualFile root) { GitLineHandler h = new GitLineHandler(myProject, root, GitCommand.PULL); h.addParameters("--no-rebase"); h.addParameters("--no-stat"); diff --git a/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java b/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java index 826a3aebe2a6..6a081e47a3d3 100644 --- a/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java +++ b/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java @@ -39,11 +39,19 @@ public class GitRebaseUpdater extends GitUpdater { private static final Logger LOG = Logger.getInstance(GitRebaseUpdater.class.getName()); private final GitRebaser myRebaser; - public GitRebaseUpdater(Project project, VirtualFile root, ProgressIndicator progressIndicator, UpdatedFiles updatedFiles) { + public GitRebaseUpdater(Project project, + VirtualFile root, + GitUpdateProcess gitUpdateProcess, + ProgressIndicator progressIndicator, + UpdatedFiles updatedFiles) { super(project, root, progressIndicator, updatedFiles); myRebaser = new GitRebaser(myProject); } + @Override public boolean isSaveNeeded() { + return true; + } + protected GitUpdateResult doUpdate() { final GitLineHandler pullHandler = makePullHandler(myRoot); final GitRebaseProblemDetector rebaseConflictDetector = new GitRebaseProblemDetector(); diff --git a/plugins/git4idea/src/git4idea/update/GitShelveChangesSaver.java b/plugins/git4idea/src/git4idea/update/GitShelveChangesSaver.java index 85fc6469d279..e537fb1d0283 100644 --- a/plugins/git4idea/src/git4idea/update/GitShelveChangesSaver.java +++ b/plugins/git4idea/src/git4idea/update/GitShelveChangesSaver.java @@ -19,13 +19,19 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList; import com.intellij.openapi.vcs.changes.shelf.ShelvedChangesViewManager; +import com.intellij.openapi.vfs.VirtualFile; +import git4idea.GitUtil; import git4idea.i18n.GitBundle; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; /** @@ -43,10 +49,10 @@ public class GitShelveChangesSaver extends GitChangesSaver { } @Override - protected void save() throws VcsException { + protected void save(Collection rootsToSave) throws VcsException { ArrayList changes = new ArrayList(); for (LocalChangeList l : myChangeLists) { - changes.addAll(l.getChanges()); + changes.addAll(filterChangesByRoots(l.getChanges(), rootsToSave)); // adding only changes from roots which are to be saved } if (!changes.isEmpty()) { myProgressIndicator.setText(GitBundle.getString("update.shelving.changes")); @@ -83,4 +89,32 @@ public class GitShelveChangesSaver extends GitChangesSaver { @Override protected void showSavedChanges() { myShelveViewManager.activateView(myShelvedChangeList); } + + /** + * Goes through the changes and returns only those of them which belong to any of the given roots, + * throwing away the changes which don't belong to any of the given roots. + */ + private static @NotNull Collection filterChangesByRoots(@NotNull Collection changes, @NotNull Collection rootsToSave) { + Collection filteredChanges = new HashSet(); + for (Change change : changes) { + final ContentRevision beforeRevision = change.getBeforeRevision(); + if (beforeRevision != null) { + final VirtualFile root = GitUtil.getGitRootOrNull(beforeRevision.getFile()); + if (root != null && rootsToSave.contains(root)) { + filteredChanges.add(change); + continue; + } + } + + final ContentRevision afterRevision = change.getAfterRevision(); + if (afterRevision != null) { + final VirtualFile root = GitUtil.getGitRootOrNull(afterRevision.getFile()); + if (root != null && rootsToSave.contains(root)) { + filteredChanges.add(change); + } + } + } + return filteredChanges; + } + } diff --git a/plugins/git4idea/src/git4idea/update/GitStashChangesSaver.java b/plugins/git4idea/src/git4idea/update/GitStashChangesSaver.java index f05c76b7c01a..aed097589da1 100644 --- a/plugins/git4idea/src/git4idea/update/GitStashChangesSaver.java +++ b/plugins/git4idea/src/git4idea/update/GitStashChangesSaver.java @@ -55,8 +55,8 @@ public class GitStashChangesSaver extends GitChangesSaver { } @Override - protected void save() throws VcsException { - Map> changes = groupChangesByRoots(); + protected void save(Collection rootsToSave) throws VcsException { + Map> changes = groupChangesByRoots(rootsToSave); convertSeparatorsIfNeeded(changes); stash(changes.keySet()); } @@ -146,24 +146,28 @@ public class GitStashChangesSaver extends GitChangesSaver { } // Sort changes from myChangesLists by their git roots. - private Map> groupChangesByRoots() { + // And use only supplied roots, ignoring changes from other roots. + private Map> groupChangesByRoots(Collection rootsToSave) { final Map> sortedChanges = new HashMap>(); for (LocalChangeList l : myChangeLists) { final Collection changeCollection = l.getChanges(); for (Change c : changeCollection) { if (c.getAfterRevision() != null) { - storeChangeInMap(sortedChanges, c, c.getAfterRevision()); + storeChangeInMap(sortedChanges, c, c.getAfterRevision(), rootsToSave); } else if (c.getBeforeRevision() != null) { - storeChangeInMap(sortedChanges, c, c.getBeforeRevision()); + storeChangeInMap(sortedChanges, c, c.getBeforeRevision(), rootsToSave); } } } return sortedChanges; } - private static void storeChangeInMap(Map> sortedChanges, Change c, ContentRevision before) { - final VirtualFile root = GitUtil.getGitRootOrNull(before.getFile()); - if (root != null) { + private static void storeChangeInMap(Map> sortedChanges, + Change c, + ContentRevision contentRevision, + Collection rootsToSave) { + final VirtualFile root = GitUtil.getGitRootOrNull(contentRevision.getFile()); + if (root != null && rootsToSave.contains(root)) { Collection changes = sortedChanges.get(root); if (changes == null) { changes = new ArrayList(); diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java b/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java index 32320c798675..4441eecf836a 100644 --- a/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java +++ b/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java @@ -20,21 +20,18 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.util.Clock; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.text.DateFormatUtil; import git4idea.GitBranch; -import git4idea.GitVcs; +import git4idea.branch.GitBranchPair; import git4idea.merge.GitMergeConflictResolver; import git4idea.merge.GitMergeUtil; import git4idea.merge.GitMerger; import git4idea.rebase.GitRebaser; -import java.util.Collection; -import java.util.Set; +import java.util.*; import static git4idea.ui.GitUIUtil.notifyError; import static git4idea.ui.GitUIUtil.notifyImportantError; @@ -52,9 +49,10 @@ public class GitUpdateProcess { private final Set myRoots; private final UpdatedFiles myUpdatedFiles; private final ProgressIndicator myProgressIndicator; - private final GitVcs myVcs; - private final AbstractVcsHelper myVcsHelper; private final GitMerger myMerger; + private final GitChangesSaver mySaver; + + private final Map myTrackedBranches = new HashMap(); public GitUpdateProcess(Project project, ProgressIndicator progressIndicator, @@ -64,9 +62,9 @@ public class GitUpdateProcess { myUpdatedFiles = updatedFiles; myProgressIndicator = progressIndicator; myProjectManager = ProjectManagerEx.getInstanceEx(); - myVcs = GitVcs.getInstance(myProject); - myVcsHelper = AbstractVcsHelper.getInstance(project); myMerger = new GitMerger(myProject); + mySaver = GitChangesSaver.getSaver(myProject, myProgressIndicator, + "Uncommitted changes before update operation at " + DateFormatUtil.formatDateTime(Clock.getTime())); } /** @@ -75,22 +73,30 @@ public class GitUpdateProcess { */ public boolean update() { LOG.info("update started"); - final GitChangesSaver saver = GitChangesSaver.getSaver(myProject, myProgressIndicator, - "Uncommitted changes before update operation at " + DateFormatUtil.formatDateTime(Clock.getTime())); myProjectManager.blockReloadingProjectOnExternalChanges(); try { // check if update is possible if (checkRebaseInProgress() || checkMergeInProgress() || checkUnmergedFiles()) { return false; } - if (!allTrackedBranchesConfigured()) { return false; } + if (!checkTrackedBranchesConfigured()) { return false; } + + // define updaters for each root + Collection rootsToSave = new HashSet(1); + for (VirtualFile root : myRoots) { + final GitUpdater updater = GitUpdater.getUpdater(myProject, this, root, myProgressIndicator, myUpdatedFiles); + if (updater.isSaveNeeded()) { + rootsToSave.add(root); + } + } + + mySaver.saveLocalChanges(rootsToSave); - saver.saveLocalChanges(); // update each root boolean incomplete = false; boolean success = true; for (final VirtualFile root : myRoots) { try { - final GitUpdater updater = GitUpdater.getUpdater(myProject, root, myProgressIndicator, myUpdatedFiles); + final GitUpdater updater = GitUpdater.getUpdater(myProject, this, root, myProgressIndicator, myUpdatedFiles); GitUpdateResult res = updater.update(); if (res == GitUpdateResult.INCOMPLETE) { incomplete = true; @@ -103,9 +109,9 @@ public class GitUpdateProcess { } finally { try { if (!incomplete) { - saver.restoreLocalChanges(); + mySaver.restoreLocalChanges(); } else { - saver.notifyLocalChangesAreNotRestored(); + mySaver.notifyLocalChangesAreNotRestored(); } } catch (VcsException e) { LOG.info("Couldn't restore local changes after update", e); @@ -118,7 +124,7 @@ public class GitUpdateProcess { } catch (VcsException e) { LOG.info("Couldn't save local changes", e); notifyError(myProject, "Couldn't save local changes", - "Tried to save uncommitted changes in " + saver.getSaverName() + " before update, but failed with an error.
" + + "Tried to save uncommitted changes in " + mySaver.getSaverName() + " before update, but failed with an error.
" + "Update was cancelled.", true, e); } finally { myProjectManager.unblockReloadingProjectOnExternalChanges(); @@ -126,12 +132,20 @@ public class GitUpdateProcess { return false; } + public Map getTrackedBranches() { + return myTrackedBranches; + } + + public GitChangesSaver getSaver() { + return mySaver; + } + /** * For each root check that the repository is on branch, and this branch is tracking a remote branch. * If it is not true for at least one of roots, notify and return false. * If branch configuration is OK for all roots, return true. */ - private boolean allTrackedBranchesConfigured() { + private boolean checkTrackedBranchesConfigured() { for (VirtualFile root : myRoots) { try { final GitBranch branch = GitBranch.current(myProject, root); @@ -141,8 +155,8 @@ public class GitUpdateProcess { "Checkout a branch to make update possible."); return false; } - final String value = branch.getTrackedRemoteName(myProject, root); - if (StringUtil.isEmpty(value)) { + final GitBranch tracked = branch.tracked(myProject, root); + if (tracked == null) { final String branchName = branch.getName(); notifyImportantError(myProject, "Can't update: no tracked branch", "No tracked branch configured for branch " + branchName + @@ -150,6 +164,7 @@ public class GitUpdateProcess { "git branch --set-upstream " + branchName + " origin/" + branchName + ""); return false; } + myTrackedBranches.put(root, new GitBranchPair(branch, tracked)); } catch (VcsException e) { notifyImportantError(myProject, "Can't update: error identifying tracked branch", e.getLocalizedMessage()); return false; diff --git a/plugins/git4idea/src/git4idea/update/GitUpdater.java b/plugins/git4idea/src/git4idea/update/GitUpdater.java index 68816f7e3089..230bc4353205 100644 --- a/plugins/git4idea/src/git4idea/update/GitUpdater.java +++ b/plugins/git4idea/src/git4idea/update/GitUpdater.java @@ -60,38 +60,48 @@ public abstract class GitUpdater { /** * Returns proper updater based on the update policy (merge or rebase) selected by user or stored in his .git/config * + * + * @param gitUpdateProcess * @param root * @param progressIndicator * @return {@link GitMergeUpdater} or {@link GitRebaseUpdater}. */ - public static GitUpdater getUpdater(Project project, VirtualFile root, ProgressIndicator progressIndicator, UpdatedFiles updatedFiles) { + public static GitUpdater getUpdater(Project project, + GitUpdateProcess gitUpdateProcess, + VirtualFile root, + ProgressIndicator progressIndicator, + UpdatedFiles updatedFiles) { final GitVcsSettings settings = GitVcsSettings.getInstance(project); if (settings == null) { - return getDefaultUpdaterForBranch(project, root, progressIndicator, updatedFiles); + return getDefaultUpdaterForBranch(project, root, gitUpdateProcess, progressIndicator, updatedFiles); } switch (settings.getUpdateType()) { case REBASE: - return new GitRebaseUpdater(project, root, progressIndicator, updatedFiles); + return new GitRebaseUpdater(project, root, gitUpdateProcess, progressIndicator, updatedFiles); case MERGE: - return new GitMergeUpdater(project, root, progressIndicator, updatedFiles); + return new GitMergeUpdater(project, root, gitUpdateProcess, progressIndicator, updatedFiles); case BRANCH_DEFAULT: // use default for the branch - return getDefaultUpdaterForBranch(project, root, progressIndicator, updatedFiles); + return getDefaultUpdaterForBranch(project, root, gitUpdateProcess, progressIndicator, updatedFiles); } - return getDefaultUpdaterForBranch(project, root, progressIndicator, updatedFiles); + return getDefaultUpdaterForBranch(project, root, gitUpdateProcess, progressIndicator, updatedFiles); } - private static GitUpdater getDefaultUpdaterForBranch(Project project, VirtualFile root, ProgressIndicator progressIndicator, UpdatedFiles updatedFiles) { + private static GitUpdater getDefaultUpdaterForBranch(Project project, + VirtualFile root, + GitUpdateProcess gitUpdateProcess, + ProgressIndicator progressIndicator, + UpdatedFiles updatedFiles) { try { final GitBranch branchName = GitBranch.current(project, root); final String rebase = GitConfigUtil.getValue(project, root, "branch." + branchName + ".rebase"); if (rebase != null && rebase.equalsIgnoreCase("true")) { - return new GitRebaseUpdater(project, root, progressIndicator, updatedFiles); + return new GitRebaseUpdater(project, root, gitUpdateProcess, progressIndicator, updatedFiles); } } catch (VcsException e) { LOG.info("getDefaultUpdaterForBranch branch", e); } - return new GitMergeUpdater(project, root, progressIndicator, updatedFiles); + return new GitMergeUpdater(project, root, gitUpdateProcess, progressIndicator, updatedFiles); } public GitUpdateResult update() throws VcsException { @@ -103,6 +113,13 @@ public abstract class GitUpdater { } } + /** + * Checks the repository if local changes need to be saved before update. + * For rebase local changes need to be saved always, for merge - only in the case if merge affects the same files. + * @return true if local changes from this root need to be saved, false if not. + */ + public abstract boolean isSaveNeeded(); + /** * Performs update (via rebase or merge - depending on the implementing classes). */