Support rejected multi-repo push if the branch in other repo is deleted

Suppose the branch 'feature' is removed in repoA, and got a commit in
repoB. Then push to repoB is rejected. Subsequent update fails,
because the branch is removed in repoA. That shouldn't happen, because
this fact doesn't affect pushing to repoB.

The fix is to provide a flag not to check for such removed branches
during update.

Fixes IDEA-169877.
This commit is contained in:
Kirill Likhodedov
2017-03-25 22:05:40 +00:00
parent ec652e0cc8
commit 7edbebca10
7 changed files with 67 additions and 16 deletions
@@ -412,7 +412,7 @@ public class GitPushOperation {
boolean checkForRebaseOverMergeProblem) {
GitUpdateResult updateResult = new GitUpdateProcess(myProject, myProgressIndicator,
new HashSet<>(rootsToUpdate), UpdatedFiles.create(),
checkForRebaseOverMergeProblem).update(updateMethod);
checkForRebaseOverMergeProblem, false).update(updateMethod);
for (GitRepository repository : rootsToUpdate) {
repository.getRoot().refresh(true, true);
repository.update();
@@ -56,7 +56,7 @@ public class GitUpdateEnvironment implements UpdateEnvironment {
GitRepositoryManager repositoryManager = getRepositoryManager(myProject);
final GitUpdateProcess gitUpdateProcess = new GitUpdateProcess(myProject,
progressIndicator, getRepositoriesFromRoots(repositoryManager, roots),
updatedFiles, true);
updatedFiles, true, true);
boolean result = gitUpdateProcess.update(mySettings.getUpdateType()).isSuccess();
return new GitUpdateSession(result);
}
@@ -76,6 +76,7 @@ public class GitUpdateProcess {
@NotNull private final List<GitRepository> myRepositories;
private final boolean myCheckRebaseOverMergeProblem;
private final boolean myCheckForTrackedBranchExistance;
private final UpdatedFiles myUpdatedFiles;
@NotNull private final ProgressIndicator myProgressIndicator;
@NotNull private final GitMerger myMerger;
@@ -84,9 +85,11 @@ public class GitUpdateProcess {
@Nullable ProgressIndicator progressIndicator,
@NotNull Collection<GitRepository> repositories,
@NotNull UpdatedFiles updatedFiles,
boolean checkRebaseOverMergeProblem) {
boolean checkRebaseOverMergeProblem,
boolean checkForTrackedBranchExistance) {
myProject = project;
myCheckRebaseOverMergeProblem = checkRebaseOverMergeProblem;
myCheckForTrackedBranchExistance = checkForTrackedBranchExistance;
myGit = Git.getInstance();
myChangeListManager = ChangeListManager.getInstance(project);
myVcsManager = ProjectLevelVcsManager.getInstance(project);
@@ -285,6 +288,7 @@ public class GitUpdateProcess {
LOG.info("updateImpl: defining updaters...");
for (GitRepository repository : myRepositories) {
VirtualFile root = repository.getRoot();
if (trackedBranches.get(root) == null) continue;
GitUpdater updater = GitUpdater.getUpdater(myProject, myGit, trackedBranches, root, myProgressIndicator, myUpdatedFiles,
updateMethod);
if (updater.isUpdateNeeded()) {
@@ -330,12 +334,15 @@ public class GitUpdateProcess {
}
GitBranchTrackInfo trackInfo = GitBranchUtil.getTrackInfoForBranch(repository, branch);
if (trackInfo == null) {
final String branchName = branch.getName();
LOG.info(String.format("checkTrackedBranchesConfigured: no track info for current branch %s in %s", branch, repository));
notifyImportantError(repository.getProject(), "Can't Update", getNoTrackedBranchError(repository, branchName));
return null;
if (myCheckForTrackedBranchExistance) {
notifyImportantError(repository.getProject(), "Can't Update", getNoTrackedBranchError(repository, branch.getName()));
return null;
}
}
else {
trackedBranches.put(root, new GitBranchPair(branch, trackInfo.getRemoteBranch()));
}
trackedBranches.put(root, new GitBranchPair(branch, trackInfo.getRemoteBranch()));
}
return trackedBranches;
}
@@ -16,7 +16,7 @@
package git4idea.push
import com.intellij.dvcs.push.PushSpec
import com.intellij.openapi.vcs.Executor
import com.intellij.openapi.vcs.Executor.cd
import com.intellij.util.containers.ContainerUtil
import git4idea.commands.GitCommandResult
import git4idea.repo.GitRepository
@@ -47,7 +47,7 @@ class GitPushOperationMultiRepoTest : GitPushOperationBaseTest() {
community = enclosingRepo.projectRepo
brommunity = enclosingRepo.bro
Executor.cd(myProjectPath)
cd(myProjectPath)
refresh()
updateRepositories()
}
@@ -80,10 +80,10 @@ class GitPushOperationMultiRepoTest : GitPushOperationBaseTest() {
}
fun test_update_all_roots_on_reject_when_needed_even_if_only_one_in_push_spec() {
Executor.cd(brultimate)
cd(brultimate)
val broHash = makeCommit("bro.txt")
git("push")
Executor.cd(brommunity)
cd(brommunity)
val broCommunityHash = makeCommit("bro_com.txt")
git("push")
@@ -108,7 +108,51 @@ class GitPushOperationMultiRepoTest : GitPushOperationBaseTest() {
cd(ultimate)
val lastCommitParents = git("log -1 --pretty=%P").split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
assertEquals("Merge didn't happen in main repository", 2, lastCommitParents.size)
assertEquals("Commit from bro repository didn't arrive", broHash, git("log --no-walk HEAD^2 --pretty=%H"))
assertRemoteCommitMerged("Commit from bro repository didn't arrive", broHash)
}
// IDEA-169877
fun `test push rejected in one repo when branch is deleted in another, should finally succeed`() {
listOf(brultimate, brommunity).forEach {
cd(it)
git("checkout -b feature")
git("push -u origin feature")
}
listOf(ultimate, community).forEach {
cd(it)
git("pull")
git("checkout -b feature origin/feature")
}
// commit in one repo to reject the push
cd(brultimate)
val broHash = tac("bro.txt")
git("push")
// remove branch in another repo
cd(brommunity)
git("push origin :feature")
cd(ultimate)
val commitToPush = tac("file.txt")
listOf(ultimate, community).forEach { it.update() }
agreeToUpdate(GitRejectedPushUpdateDialog.MERGE_EXIT_CODE) // auto-update-all-roots is selected by default
// push only to 1 repo, otherwise the push would recreate the deleted branch, and the error won't reproduce
val pushSpecs = mapOf(ultimate to makePushSpec(ultimate, "feature", "origin/feature"))
val result = GitPushOperation(myProject, pushSupport, pushSpecs, null, false).execute()
val result1 = result.results[ultimate]!!
assertResult(GitPushRepoResult.Type.SUCCESS, 2, "feature", "origin/feature", GitUpdateResult.SUCCESS, result1)
assertRemoteCommitMerged("Commit from bro repository didn't arrive", broHash)
cd(brultimate)
git("pull origin feature")
assertEquals("Commit from ultimate repository wasn't pushed", commitToPush, git("log --no-walk HEAD^1 --pretty=%H"))
}
private fun assertRemoteCommitMerged(message: String, expectedHash: String) {
assertEquals(message, expectedHash, git("log --no-walk HEAD^2 --pretty=%H"))
}
}
@@ -85,7 +85,7 @@ class GitMultiRepoUpdateTest : GitUpdateBaseTest() {
cd(bromunity)
git("push origin :feature")
val updateProcess = GitUpdateProcess(myProject, EmptyProgressIndicator(), repositories(), UpdatedFiles.create(), false)
val updateProcess = GitUpdateProcess(myProject, EmptyProgressIndicator(), repositories(), UpdatedFiles.create(), false, true)
val result = updateProcess.update(UpdateMethod.MERGE)
assertEquals("Update result is incorrect", GitUpdateResult.NOT_READY, result)
@@ -93,7 +93,7 @@ class GitMultiRepoUpdateTest : GitUpdateBaseTest() {
}
private fun updateWithMerge(): GitUpdateResult {
return GitUpdateProcess(myProject, EmptyProgressIndicator(), repositories(), UpdatedFiles.create(), false).update(UpdateMethod.MERGE)
return GitUpdateProcess(myProject, EmptyProgressIndicator(), repositories(), UpdatedFiles.create(), false, true).update(UpdateMethod.MERGE)
}
private fun repositories() = listOf(repository, community)
@@ -96,7 +96,7 @@ class GitSingleRepoUpdateTest : GitUpdateBaseTest() {
}
private fun updateWithRebase(): GitUpdateResult {
return GitUpdateProcess(myProject, EmptyProgressIndicator(), listOf(repo), UpdatedFiles.create(), false).update(UpdateMethod.REBASE)
return GitUpdateProcess(myProject, EmptyProgressIndicator(), listOf(repo), UpdatedFiles.create(), false, true).update(UpdateMethod.REBASE)
}
private fun File.commitAndPush() {
@@ -81,7 +81,7 @@ class GitSubmoduleTest : GitPlatformTest() {
reposInActualOrder.add(it)
}
val updateProcess = GitUpdateProcess(myProject, EmptyProgressIndicator(), allRepositories(), UpdatedFiles.create(), false)
val updateProcess = GitUpdateProcess(myProject, EmptyProgressIndicator(), allRepositories(), UpdatedFiles.create(), false, true)
val result = updateProcess.update(UpdateMethod.MERGE)
assertEquals("Incorrect update result", GitUpdateResult.SUCCESS, result)
assertOrder(reposInActualOrder)