mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Refresh only that part of the VFS that has been changed by Git operations
Refreshing the whole VFS is reliable but slow, especially in case of feature branches, when branches diverge just a little, and refreshing only a couple of files is required which is much faster.
On the other hand, collecting the difference between current working tree and the target reference is quick enough: even `git diff origin/143` takes ~1–2s.
A registry option is introduced to be able to turn the optimization off in case there is a report that not files were refreshed.
Following operations are optimized:
* reset (including "undo commit"): uses `git diff <revision>` which compares with working tree: working tree may change as the result of reset --hard.
* checkout and merge: use `git diff HEAD..<branch>`.
Comparing with working tree is unnecessary ("local changes would be overwritten by checkout/merge" means that local changes are not touched by these operations).
Merge should actually compare with the merge base (`git diff HEAD...<branch>`), but that's much slower, while refreshing several more files is not that slower.
* rebase can happen together with checkout (`git rebase master topic`), so at first it checks the diff between current branch and the branch which is to be checked out; and then it additionally checks the diff for the rebase itself (again, should be calculated with "..." for more precise results, but ".." is used because it is faster).
Note that the reason ".." can be used instead of "..." is because we don't care about the changes themselves and how accurate they are: we just need a superset of affected files.
Only trivial cases are covered: e.g. smart checkout, rollbacks, conflicts, continue rebase, etc. still perform full refresh for now, since they are relatively rare and a bit trickier).
This fixes IDEA-163043
This commit is contained in:
@@ -916,6 +916,11 @@ git.diff.renameLimit.description=Sets diff.renameLimit configuration variable va
|
||||
Setting this value too high (or zero) may lead to increased memory consumption by a Git process and longer execution time.\
|
||||
This variable only used when reading git commits for indexing.\
|
||||
Value from git configuration (1000 by default) is used for other purposes (eg for displaying changes in changes view).
|
||||
git.refresh.vfs.total=false
|
||||
git.refresh.vfs.total.description=If set to true, the whole Git repository root will be refreshed after performing a Git operation.\
|
||||
Otherwise only the affected files will be refreshed.\
|
||||
Affects only a subset of Git operations.\
|
||||
The option is needed to debug possible "incomplete VFS refresh" reports, if they appear.
|
||||
|
||||
hg4idea.process.ignored=true
|
||||
hg4idea.process.ignored.description=Process and highlight mercurial ignored files. \
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogBuilder;
|
||||
import com.intellij.openapi.ui.ex.MultiLineLabel;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.AbstractVcsHelper;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
@@ -34,9 +35,11 @@ import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManagerEx;
|
||||
import com.intellij.openapi.vcs.changes.ContentRevision;
|
||||
import com.intellij.openapi.vcs.update.RefreshVFsSynchronously;
|
||||
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Function;
|
||||
@@ -71,6 +74,7 @@ import java.util.stream.Collectors;
|
||||
import static com.intellij.dvcs.DvcsUtil.getShortRepositoryName;
|
||||
import static com.intellij.dvcs.DvcsUtil.joinShortNames;
|
||||
import static com.intellij.util.ObjectUtils.assertNotNull;
|
||||
import static java.util.Arrays.stream;
|
||||
|
||||
/**
|
||||
* Git utility/helper methods
|
||||
@@ -1033,4 +1037,33 @@ public class GitUtil {
|
||||
OpenTHashSet<Change> allChanges = new OpenTHashSet<>(changeListManager.getAllChanges());
|
||||
return ContainerUtil.mapNotNull(originalChanges, allChanges::get);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to refresh either a part of the VFS modified by the given changes, or the whole root recursively.
|
||||
*
|
||||
* @param changes The changes which files were modified by a Git operation.
|
||||
* If null, the whole root is refreshed. Otherwise, only the files touched by these changes.
|
||||
*/
|
||||
public static void refreshVfs(@NotNull VirtualFile root, @Nullable Collection<Change> changes) {
|
||||
if (changes == null || Registry.is("git.refresh.vfs.total")) {
|
||||
VfsUtil.markDirtyAndRefresh(false, true, false, root);
|
||||
}
|
||||
else {
|
||||
RefreshVFsSynchronously.updateChanges(changes);
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateAndRefreshVfs(@NotNull GitRepository repository, @Nullable Collection<Change> changes) {
|
||||
repository.update();
|
||||
refreshVfs(repository.getRoot(), changes);
|
||||
}
|
||||
|
||||
public static void updateAndRefreshVfs(GitRepository... repositories) {
|
||||
// repositories state will be auto-updated with the following VFS refresh => there is no need to call GitRepository#update()
|
||||
// but we want repository state to be updated as soon as possible, without waiting for the whole VFS refresh to complete.
|
||||
stream(repositories).forEach(GitRepository::update);
|
||||
for (GitRepository repository : repositories) {
|
||||
refreshVfs(repository.getRoot(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.VcsNotifier;
|
||||
import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import git4idea.GitUtil;
|
||||
@@ -293,15 +292,6 @@ abstract class GitBranchOperation {
|
||||
myUiHandler.showUnmergedFilesNotification(getOperationName(), getRepositories());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously refreshes the VFS root directory of the given repository.
|
||||
*/
|
||||
protected void refreshRoot(@NotNull GitRepository repository) {
|
||||
// marking all files dirty, because sometimes FileWatcher is unable to process such a large set of changes that can happen during
|
||||
// checkout on a large repository: IDEA-89944
|
||||
VfsUtil.markDirtyAndRefresh(false, true, false, repository.getRoot());
|
||||
}
|
||||
|
||||
protected void fatalLocalChangesError(@NotNull String reference) {
|
||||
String title = String.format("Couldn't %s %s", getOperationName(), reference);
|
||||
if (wereSuccessful()) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.changes.GitChangeUtils;
|
||||
import git4idea.commands.*;
|
||||
import git4idea.repo.GitRepository;
|
||||
import git4idea.util.GitPreservingProcess;
|
||||
@@ -39,12 +40,11 @@ import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static com.intellij.util.containers.UtilKt.getIfSingle;
|
||||
import static git4idea.GitUtil.getRootsFromRepositories;
|
||||
import static git4idea.GitUtil.*;
|
||||
import static git4idea.branch.GitSmartOperationDialog.Choice.FORCE;
|
||||
import static git4idea.branch.GitSmartOperationDialog.Choice.SMART;
|
||||
import static git4idea.config.GitVcsSettings.UpdateChangesPolicy.STASH;
|
||||
import static git4idea.util.GitUIUtil.code;
|
||||
import static java.util.Arrays.stream;
|
||||
|
||||
/**
|
||||
* Represents {@code git checkout} operation.
|
||||
@@ -85,8 +85,10 @@ class GitCheckoutOperation extends GitBranchOperation {
|
||||
try {
|
||||
while (hasMoreRepositories() && !fatalErrorHappened) {
|
||||
final GitRepository repository = next();
|
||||
|
||||
VirtualFile root = repository.getRoot();
|
||||
|
||||
Collection<Change> changes = GitChangeUtils.getDiff(repository, HEAD, myStartPointReference);
|
||||
|
||||
GitLocalChangesWouldBeOverwrittenDetector localChangesDetector =
|
||||
new GitLocalChangesWouldBeOverwrittenDetector(root, GitLocalChangesWouldBeOverwrittenDetector.Operation.CHECKOUT);
|
||||
GitSimpleEventDetector unmergedFiles = new GitSimpleEventDetector(GitSimpleEventDetector.Event.UNMERGED_PREVENTING_CHECKOUT);
|
||||
@@ -97,7 +99,7 @@ class GitCheckoutOperation extends GitBranchOperation {
|
||||
GitCommandResult result = myGit.checkout(repository, myStartPointReference, myNewBranch, false, myDetach,
|
||||
localChangesDetector, unmergedFiles, unknownPathspec, untrackedOverwrittenByCheckout);
|
||||
if (result.success()) {
|
||||
refresh(repository);
|
||||
updateAndRefreshVfs(repository, changes);
|
||||
markSuccessful(repository);
|
||||
}
|
||||
else if (unmergedFiles.hasHappened()) {
|
||||
@@ -168,7 +170,7 @@ class GitCheckoutOperation extends GitBranchOperation {
|
||||
if (smartCheckedOutSuccessfully) {
|
||||
for (GitRepository conflictingRepository : allConflictingRepositories) {
|
||||
markSuccessful(conflictingRepository);
|
||||
refresh(conflictingRepository);
|
||||
updateAndRefreshVfs(conflictingRepository);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -181,7 +183,7 @@ class GitCheckoutOperation extends GitBranchOperation {
|
||||
boolean forceCheckoutSucceeded = checkoutOrNotify(allConflictingRepositories, myStartPointReference, myNewBranch, true);
|
||||
if (forceCheckoutSucceeded) {
|
||||
markSuccessful(ArrayUtil.toObjectArray(allConflictingRepositories, GitRepository.class));
|
||||
refresh(ArrayUtil.toObjectArray(allConflictingRepositories, GitRepository.class));
|
||||
updateAndRefreshVfs(ArrayUtil.toObjectArray(allConflictingRepositories, GitRepository.class));
|
||||
}
|
||||
return forceCheckoutSucceeded;
|
||||
}
|
||||
@@ -223,7 +225,7 @@ class GitCheckoutOperation extends GitBranchOperation {
|
||||
*/
|
||||
deleteResult.append(repository, myGit.branchDelete(repository, myNewBranch, true));
|
||||
}
|
||||
refresh(repository);
|
||||
updateAndRefreshVfs(repository);
|
||||
}
|
||||
if (!checkoutResult.totalSuccess() || !deleteResult.totalSuccess()) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
@@ -281,15 +283,6 @@ class GitCheckoutOperation extends GitBranchOperation {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void refresh(GitRepository... repositories) {
|
||||
// repositories state will be auto-updated with the following VFS refresh => there is no need to call GitRepository#update()
|
||||
// but we want repository state to be updated as soon as possible, without waiting for the whole VFS refresh to complete.
|
||||
stream(repositories).forEach(GitRepository::update);
|
||||
for (GitRepository repository : repositories) {
|
||||
refreshRoot(repository);
|
||||
}
|
||||
}
|
||||
|
||||
private class RollbackOperationNotificationListener implements NotificationListener {
|
||||
@Override
|
||||
public void hyperlinkUpdate(@NotNull Notification notification,
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.changes.GitChangeUtils;
|
||||
import git4idea.commands.*;
|
||||
import git4idea.merge.GitMergeCommittingConflictResolver;
|
||||
import git4idea.merge.GitMerger;
|
||||
@@ -40,6 +41,8 @@ import javax.swing.event.HyperlinkEvent;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static git4idea.GitUtil.HEAD;
|
||||
import static git4idea.GitUtil.updateAndRefreshVfs;
|
||||
import static git4idea.config.GitVcsSettings.UpdateChangesPolicy.STASH;
|
||||
|
||||
class GitMergeOperation extends GitBranchOperation {
|
||||
@@ -75,8 +78,10 @@ class GitMergeOperation extends GitBranchOperation {
|
||||
while (hasMoreRepositories() && !fatalErrorHappened) {
|
||||
final GitRepository repository = next();
|
||||
LOG.info("next repository: " + repository);
|
||||
|
||||
VirtualFile root = repository.getRoot();
|
||||
|
||||
Collection<Change> changes = GitChangeUtils.getDiff(repository, HEAD, myBranchToMerge);
|
||||
|
||||
GitLocalChangesWouldBeOverwrittenDetector localChangesDetector =
|
||||
new GitLocalChangesWouldBeOverwrittenDetector(root, GitLocalChangesWouldBeOverwrittenDetector.Operation.MERGE);
|
||||
GitSimpleEventDetector unmergedFiles = new GitSimpleEventDetector(GitSimpleEventDetector.Event.UNMERGED_PREVENTING_MERGE);
|
||||
@@ -90,7 +95,7 @@ class GitMergeOperation extends GitBranchOperation {
|
||||
alreadyUpToDateDetector);
|
||||
if (result.success()) {
|
||||
LOG.info("Merged successfully");
|
||||
refresh(repository);
|
||||
updateAndRefreshVfs(repository, changes);
|
||||
markSuccessful(repository);
|
||||
if (alreadyUpToDateDetector.hasHappened()) {
|
||||
alreadyUpToDateRepositories += 1;
|
||||
@@ -111,7 +116,7 @@ class GitMergeOperation extends GitBranchOperation {
|
||||
else if (mergeConflict.hasHappened()) {
|
||||
LOG.info("Merge conflict");
|
||||
myConflictedRepositories.put(repository, Boolean.FALSE);
|
||||
refresh(repository);
|
||||
updateAndRefreshVfs(repository);
|
||||
markSuccessful(repository);
|
||||
}
|
||||
else if (untrackedOverwrittenByMerge.wasMessageDetected()) {
|
||||
@@ -233,7 +238,7 @@ class GitMergeOperation extends GitBranchOperation {
|
||||
if (!result.success()) {
|
||||
if (mergeConflict.hasHappened()) {
|
||||
myConflictedRepositories.put(repository, Boolean.TRUE);
|
||||
refresh(repository);
|
||||
updateAndRefreshVfs(repository);
|
||||
markSuccessful(repository);
|
||||
}
|
||||
else {
|
||||
@@ -242,7 +247,7 @@ class GitMergeOperation extends GitBranchOperation {
|
||||
}
|
||||
}
|
||||
else {
|
||||
refresh(repository);
|
||||
updateAndRefreshVfs(repository);
|
||||
markSuccessful(repository);
|
||||
}
|
||||
}
|
||||
@@ -313,7 +318,7 @@ class GitMergeOperation extends GitBranchOperation {
|
||||
@NotNull
|
||||
private GitCommandResult rollbackMerge(@NotNull GitRepository repository) {
|
||||
GitCommandResult result = myGit.resetMerge(repository, null);
|
||||
refresh(repository);
|
||||
updateAndRefreshVfs(repository);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -342,13 +347,6 @@ class GitMergeOperation extends GitBranchOperation {
|
||||
return "merge";
|
||||
}
|
||||
|
||||
private void refresh(GitRepository... repositories) {
|
||||
for (GitRepository repository : repositories) {
|
||||
refreshRoot(repository);
|
||||
repository.update();
|
||||
}
|
||||
}
|
||||
|
||||
private class MyMergeConflictResolver extends GitMergeCommittingConflictResolver {
|
||||
public MyMergeConflictResolver() {
|
||||
super(GitMergeOperation.this.myProject, myGit, new GitMerger(GitMergeOperation.this.myProject),
|
||||
|
||||
@@ -34,6 +34,7 @@ import git4idea.commands.GitCommand;
|
||||
import git4idea.commands.GitHandler;
|
||||
import git4idea.commands.GitSimpleHandler;
|
||||
import git4idea.history.browser.SHAHash;
|
||||
import git4idea.repo.GitRepository;
|
||||
import git4idea.util.StringScanner;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -43,6 +44,7 @@ import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.assertNotNull;
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
/**
|
||||
* Change related utilities
|
||||
@@ -391,7 +393,7 @@ public class GitChangeUtils {
|
||||
String output = getDiffOutput(project, root, range, dirtyPaths);
|
||||
|
||||
Collection<Change> changes = new ArrayList<>();
|
||||
parseChanges(project, root, newRev, oldRev, output, changes, Collections.emptySet());
|
||||
parseChanges(project, root, newRev, oldRev, output, changes, emptySet());
|
||||
return changes;
|
||||
}
|
||||
|
||||
@@ -402,7 +404,7 @@ public class GitChangeUtils {
|
||||
String output = diff.run();
|
||||
|
||||
Collection<Change> changes = new ArrayList<>();
|
||||
parseChanges(project, root, null, GitRevisionNumber.HEAD, output, changes, Collections.emptySet());
|
||||
parseChanges(project, root, null, GitRevisionNumber.HEAD, output, changes, emptySet());
|
||||
return changes;
|
||||
}
|
||||
|
||||
@@ -415,7 +417,7 @@ public class GitChangeUtils {
|
||||
Collection<Change> changes = new ArrayList<>();
|
||||
final GitRevisionNumber revisionNumber = resolveReference(project, root, oldRevision);
|
||||
parseChanges(project, root, reverse ? revisionNumber : null, reverse ? null : revisionNumber, output, changes,
|
||||
Collections.emptySet());
|
||||
emptySet());
|
||||
return changes;
|
||||
}
|
||||
|
||||
@@ -465,4 +467,30 @@ public class GitChangeUtils {
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the changes between current working tree state and the given ref, or null if fails to get the diff.
|
||||
*/
|
||||
@Nullable
|
||||
public static Collection<Change> getDiffWithWorkingTree(@NotNull GitRepository repository, @NotNull String refToCompare) {
|
||||
Collection<Change> changes;
|
||||
try {
|
||||
changes = getDiffWithWorkingDir(repository.getProject(), repository.getRoot(), refToCompare, null, false);
|
||||
}
|
||||
catch (VcsException e) {
|
||||
LOG.warn("Couldn't collect diff", e);
|
||||
changes = null;
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Collection<Change> getDiff(@NotNull GitRepository repository, @NotNull String oldRevision, @NotNull String newRevision) {
|
||||
try {
|
||||
return getDiff(repository.getProject(), repository.getRoot(), oldRevision, newRevision, null);
|
||||
}
|
||||
catch (VcsException e) {
|
||||
LOG.warn("Couldn't collect changes between " + oldRevision + " and " + newRevision, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.VcsNotifier;
|
||||
import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -38,6 +39,7 @@ import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.branch.GitRebaseParams;
|
||||
import git4idea.changes.GitChangeUtils;
|
||||
import git4idea.commands.Git;
|
||||
import git4idea.commands.GitCommandResult;
|
||||
import git4idea.commands.GitLineHandlerListener;
|
||||
@@ -55,19 +57,16 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.dvcs.DvcsUtil.getShortRepositoryName;
|
||||
import static com.intellij.openapi.vfs.VfsUtilCore.toVirtualFileArray;
|
||||
import static com.intellij.util.ObjectUtils.assertNotNull;
|
||||
import static com.intellij.util.ObjectUtils.coalesce;
|
||||
import static com.intellij.util.ObjectUtils.notNull;
|
||||
import static com.intellij.util.containers.ContainerUtil.*;
|
||||
import static com.intellij.util.containers.ContainerUtilRt.newArrayList;
|
||||
import static com.intellij.util.containers.ContainerUtilRt.newLinkedHashSet;
|
||||
import static git4idea.GitUtil.getRootsFromRepositories;
|
||||
import static git4idea.GitUtil.*;
|
||||
import static java.util.Collections.singleton;
|
||||
|
||||
public class GitRebaseProcess {
|
||||
@@ -117,7 +116,6 @@ public class GitRebaseProcess {
|
||||
LOG.debug("Started rebase with the following spec: " + myRebaseSpec);
|
||||
|
||||
Map<GitRepository, GitRebaseStatus> statuses = newLinkedHashMap(myRebaseSpec.getStatuses());
|
||||
Collection<GitRepository> toRefresh = newLinkedHashSet();
|
||||
List<GitRepository> repositoriesToRebase = myRepositoryManager.sortByDependency(myRebaseSpec.getIncompleteRepositories());
|
||||
AccessToken token = DvcsUtil.workingTreeChangeStarted(myProject);
|
||||
try {
|
||||
@@ -130,11 +128,13 @@ public class GitRebaseProcess {
|
||||
customMode = myCustomMode == null ? GitRebaseResumeMode.CONTINUE : myCustomMode;
|
||||
}
|
||||
|
||||
Collection<Change> changes = collectFutureChanges(repository);
|
||||
|
||||
GitRebaseStatus rebaseStatus = rebaseSingleRoot(repository, customMode, getSuccessfulRepositories(statuses));
|
||||
repository.update(); // make the repo state info actual ASAP
|
||||
statuses.put(repository, rebaseStatus);
|
||||
if (shouldBeRefreshed(rebaseStatus)) {
|
||||
toRefresh.add(repository);
|
||||
refreshVfs(repository.getRoot(), changes);
|
||||
}
|
||||
if (rebaseStatus.getType() != GitRebaseStatus.Type.SUCCESS) {
|
||||
failed = repository;
|
||||
@@ -146,7 +146,6 @@ public class GitRebaseProcess {
|
||||
LOG.debug("Rebase completed successfully.");
|
||||
mySaver.load();
|
||||
}
|
||||
refresh(toRefresh);
|
||||
if (failed == null) {
|
||||
notifySuccess(getSuccessfulRepositories(statuses), getSkippedCommits(statuses));
|
||||
}
|
||||
@@ -165,6 +164,28 @@ public class GitRebaseProcess {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Collection<Change> collectFutureChanges(@NotNull GitRepository repository) {
|
||||
GitRebaseParams params = myRebaseSpec.getParams();
|
||||
if (params == null) return null;
|
||||
|
||||
Collection<Change> changes = new ArrayList<>();
|
||||
String branch = params.getBranch();
|
||||
if (branch != null) {
|
||||
Collection<Change> changesFromCheckout = GitChangeUtils.getDiff(repository, HEAD, branch);
|
||||
if (changesFromCheckout == null) return null;
|
||||
changes.addAll(changesFromCheckout);
|
||||
}
|
||||
|
||||
String rev1 = coalesce(params.getNewBase(), branch, HEAD);
|
||||
String rev2 = params.getUpstream();
|
||||
Collection<Change> changesFromRebase = GitChangeUtils.getDiff(repository, rev1, rev2);
|
||||
if (changesFromRebase == null) return null;
|
||||
|
||||
changes.addAll(changesFromRebase);
|
||||
return changes;
|
||||
}
|
||||
|
||||
private void saveUpdatedSpec(@NotNull Map<GitRepository, GitRebaseStatus> statuses) {
|
||||
if (myRebaseSpec.shouldBeSaved()) {
|
||||
GitRebaseSpec newRebaseInfo = myRebaseSpec.cloneWithNewStatuses(statuses);
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.VcsNotifier;
|
||||
import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
@@ -34,6 +33,7 @@ import com.intellij.vcs.log.Hash;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.branch.GitBranchUiHandlerImpl;
|
||||
import git4idea.branch.GitSmartOperationDialog;
|
||||
import git4idea.changes.GitChangeUtils;
|
||||
import git4idea.commands.Git;
|
||||
import git4idea.commands.GitCommandResult;
|
||||
import git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector;
|
||||
@@ -47,10 +47,12 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static git4idea.GitUtil.updateAndRefreshVfs;
|
||||
import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.RESET;
|
||||
|
||||
public class GitResetOperation {
|
||||
|
||||
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final Map<GitRepository, Hash> myCommits;
|
||||
@NotNull private final GitResetMode myMode;
|
||||
@@ -83,6 +85,8 @@ public class GitResetOperation {
|
||||
String target = entry.getValue().asString();
|
||||
GitLocalChangesWouldBeOverwrittenDetector detector = new GitLocalChangesWouldBeOverwrittenDetector(root, RESET);
|
||||
|
||||
Collection<Change> changes = GitChangeUtils.getDiffWithWorkingTree(repository, target);
|
||||
|
||||
GitCommandResult result = myGit.reset(repository, myMode, target, detector);
|
||||
if (!result.success() && detector.wasMessageDetected()) {
|
||||
GitCommandResult smartResult = proposeSmartReset(detector, repository, target);
|
||||
@@ -91,8 +95,8 @@ public class GitResetOperation {
|
||||
}
|
||||
}
|
||||
results.put(repository, result);
|
||||
repository.update();
|
||||
VfsUtil.markDirtyAndRefresh(false, true, false, root);
|
||||
|
||||
updateAndRefreshVfs(repository, changes);
|
||||
VcsDirtyScopeManager.getInstance(myProject).dirDirtyRecursively(root);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.reset
|
||||
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||
import com.intellij.openapi.vcs.FileStatus
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.vcs.log.Hash
|
||||
import com.intellij.vcs.log.impl.HashImpl
|
||||
import git4idea.test.GitSingleRepoTest
|
||||
import git4idea.test.file
|
||||
import git4idea.test.last
|
||||
|
||||
class GitResetTest : GitSingleRepoTest() {
|
||||
|
||||
fun `test file is refreshed on hard reset`() {
|
||||
val (oldHash, vf) = prepare()
|
||||
|
||||
GitResetOperation(myProject, mapOf(myRepo to oldHash), GitResetMode.HARD, EmptyProgressIndicator()).execute()
|
||||
|
||||
assertSuccessfulNotification("Reset successful")
|
||||
assertEquals("Branch is on incorrect point", oldHash.asString(), last())
|
||||
assertEquals("VirtualFile wasn't refreshed", "initial\n", String(vf.contentsToByteArray()))
|
||||
}
|
||||
|
||||
fun `test file status is refreshed on soft reset`() {
|
||||
val (oldHash, vf) = prepare()
|
||||
|
||||
GitResetOperation(myProject, mapOf(myRepo to oldHash), GitResetMode.SOFT, EmptyProgressIndicator()).execute()
|
||||
|
||||
assertSuccessfulNotification("Reset successful")
|
||||
assertEquals("Branch is on incorrect point", oldHash.asString(), last())
|
||||
changeListManager.ensureUpToDate(false)
|
||||
assertEquals("File status wasn't refreshed", FileStatus.MODIFIED, changeListManager.getChange(vf)!!.fileStatus)
|
||||
}
|
||||
|
||||
private fun prepare(): Pair<Hash, VirtualFile> {
|
||||
val file = file("f.txt").create().write("initial\n")
|
||||
val prevHash = HashImpl.build(file.addCommit("created").hash())
|
||||
file.append("more\n")
|
||||
file.addCommit("Added more")
|
||||
val vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file.file)!!
|
||||
return Pair(prevHash, vf)
|
||||
}
|
||||
}
|
||||
@@ -41,9 +41,7 @@ abstract class GitSingleRepoTest : GitPlatformTest() {
|
||||
cd(myProjectPath)
|
||||
}
|
||||
|
||||
protected open fun makeInitialCommit(): Boolean {
|
||||
return true
|
||||
}
|
||||
protected open fun makeInitialCommit() = true
|
||||
|
||||
protected fun VcsConfiguration.StandardConfirmation.doSilently() =
|
||||
AbstractVcsTestCase.setStandardConfirmation(myProject, GitVcs.NAME, this, DO_ACTION_SILENTLY)
|
||||
@@ -74,9 +72,7 @@ abstract class GitSingleRepoTest : GitPlatformTest() {
|
||||
updateChangeListManager()
|
||||
}
|
||||
|
||||
protected fun build(f: RepoBuilder.() -> Unit) {
|
||||
build(myRepo, f)
|
||||
}
|
||||
protected fun build(f: RepoBuilder.() -> Unit) = build(myRepo, f)
|
||||
|
||||
protected fun assertUnversioned(file: VirtualFile) {
|
||||
assertTrue("File should be unversioned! All changes: " + GitUtil.getLogString(myProjectPath, changeListManager.allChanges),
|
||||
|
||||
Reference in New Issue
Block a user