blob: 9a2bad2ad6636e7349116838f3ee1c2c00cb6e30 [file] [log] [blame]
/*
* Copyright 2000-2012 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 com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.util.ProgressIndicatorBase
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vcs.changes.Change
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.Function
import com.intellij.util.LineSeparator
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.text.CharArrayUtil
import git4idea.GitCommit
import git4idea.config.GitVersion
import git4idea.config.GitVersionSpecialty
import git4idea.repo.GitRepository
import git4idea.test.GitPlatformTest
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import java.util.regex.Matcher
import static git4idea.test.GitExecutor.*
import static git4idea.test.GitScenarios.*
class GitBranchWorkerTest extends GitPlatformTest {
private GitRepository myUltimate
private GitRepository myCommunity
private GitRepository myContrib
private List<GitRepository> myRepositories
public void setUp() {
super.setUp();
try {
cd(myProjectRoot)
File community = mkdir("community")
File contrib = mkdir("contrib")
myUltimate = createRepository(myProjectRoot.path)
myCommunity = createRepository(community.path)
myContrib = createRepository(contrib.path)
myRepositories = [ myUltimate, myCommunity, myContrib ]
cd(myProjectRoot)
touch(".gitignore", "community\ncontrib")
git("add .gitignore")
git("commit -m gitignore")
}
catch (Throwable e) {
tearDown()
throw e
}
}
public void "test create new branch without problems"() {
checkoutNewBranch "feature", [ ]
assertCurrentBranch("feature")
assertEquals("Notification about successful branch creation is incorrect",
"Branch ${bcode("feature")} was created", myVcsNotifier.lastNotification.content)
}
static String bcode(def s) {
"<b><code>${s}</code></b>"
}
public void "test create new branch with unmerged files in first repo should show notification"() {
unmergedFiles(myUltimate)
boolean notificationShown = false;
checkoutNewBranch("feature", [ showUnmergedFilesNotification: { String s, List l -> notificationShown = true } ])
assertTrue("Unmerged files notification was not shown", notificationShown)
}
public void "test create new branch with unmerged files in second repo should propose to rollback"() {
unmergedFiles(myCommunity)
boolean rollbackProposed = false;
checkoutNewBranch "feature", [ showUnmergedFilesMessageWithRollback: { String s1, String s2 -> rollbackProposed = true ; false } ]
assertTrue("Rollback was not proposed if unmerged files prevented checkout in the second repository", rollbackProposed)
}
public void "test rollback create new branch should delete branch"() {
unmergedFiles(myCommunity)
checkoutNewBranch "feature", [
showUnmergedFilesMessageWithRollback: { String s1, String s2 -> true },
]
assertCurrentBranch("master");
assertBranchDeleted(myUltimate, "feature")
}
public void "test deny rollback create new branch should leave new branch"() {
unmergedFiles(myCommunity)
checkoutNewBranch "feature", [ showUnmergedFilesMessageWithRollback: { String s1, String s2 -> false } ]
assertCurrentBranch(myUltimate, "feature")
assertCurrentBranch(myCommunity, "master")
assertCurrentBranch(myContrib, "master")
}
public void "test checkout without problems"() {
branchWithCommit(myRepositories, "feature")
checkoutBranch "feature", []
assertCurrentBranch("feature")
assertEquals("Notification about successful branch checkout is incorrect", "Checked out ${bcode("feature")}",
myVcsNotifier.lastNotification.content)
}
public void "test checkout_with_unmerged_files_in_first_repo_should_show_notification"() {
branchWithCommit(myRepositories, "feature")
unmergedFiles(myUltimate)
boolean notificationShown = false;
checkoutBranch("feature", [ showUnmergedFilesNotification: { String s, List l -> notificationShown = true } ])
assertTrue("Unmerged files notification was not shown", notificationShown)
}
public void test_checkout_with_unmerged_file_in_second_repo_should_propose_to_rollback() {
branchWithCommit(myRepositories, "feature")
unmergedFiles(myCommunity)
boolean rollbackProposed = false;
checkoutBranch "feature", [ showUnmergedFilesMessageWithRollback: { String s1, String s2 -> rollbackProposed = true ; false } ]
assertTrue("Rollback was not proposed if unmerged files prevented checkout in the second repository", rollbackProposed)
}
public void test_rollback_checkout_should_return_to_previous_branch() {
branchWithCommit(myRepositories, "feature")
unmergedFiles(myCommunity)
checkoutBranch "feature", [
showUnmergedFilesMessageWithRollback: { String s1, String s2 -> true },
]
assertCurrentBranch("master");
}
public void test_deny_rollback_checkout_should_do_nothing() {
branchWithCommit(myRepositories, "feature")
unmergedFiles(myCommunity)
checkoutBranch "feature", [ showUnmergedFilesMessageWithRollback: { String s1, String s2 -> false } ]
assertCurrentBranch(myUltimate, "feature")
assertCurrentBranch(myCommunity, "master")
assertCurrentBranch(myContrib, "master")
}
public void "test checkout with untracked files overwritten by checkout in first repo should show notification"() {
test_untracked_files_overwritten_by_in_first_repo("checkout");
}
public void "test checkout with several untracked files overwritten by checkout in first repo should show notification"() {
// note that in old Git versions only one file is listed in the error.
test_untracked_files_overwritten_by_in_first_repo("checkout", 3);
}
public void "test merge with untracked files overwritten by checkout in first repo should show notification"() {
test_untracked_files_overwritten_by_in_first_repo("merge");
}
def test_untracked_files_overwritten_by_in_first_repo(String operation, int untrackedFiles = 1) {
branchWithCommit(myRepositories, "feature")
def files = []
for (int i = 0; i < untrackedFiles; i++) {
files.add("untracked${i}.txt")
}
untrackedFileOverwrittenBy(myUltimate, "feature", files)
boolean notificationShown = false;
checkoutOrMerge operation, "feature", [
showUntrackedFilesNotification: { String s, VirtualFile root, Collection c -> notificationShown = true }
]
assertTrue "Untracked files notification was not shown", notificationShown
}
public void "test checkout with untracked files overwritten by checkout in second repo should show rollback proposal with file list"() {
check_checkout_with_untracked_files_overwritten_by_in_second_repo("checkout");
}
public void "test merge with untracked files overwritten by checkout in second repo should show rollback proposal with file list"() {
check_checkout_with_untracked_files_overwritten_by_in_second_repo("merge");
}
def check_checkout_with_untracked_files_overwritten_by_in_second_repo(String operation) {
branchWithCommit(myRepositories, "feature")
def untracked = ["untracked.txt"]
untrackedFileOverwrittenBy(myCommunity, "feature", untracked)
Collection<String> untrackedPaths = null;
checkoutOrMerge operation, "feature", [
showUntrackedFilesDialogWithRollback: {
String s, String p, VirtualFile root, Collection<String> files -> untrackedPaths = files; false
}
]
assertTrue "Untracked files dialog was not shown", untrackedPaths != null
assertEquals "Incorrect set of untracked files was shown in the dialog", untracked.asList(), untrackedPaths.asList()
}
public void "test checkout with local changes overwritten by checkout should show smart checkout dialog"() {
check_operation_with_local_changes_overwritten_by_should_show_smart_checkout_dialog("checkout");
}
public void "test checkout with several local changes overwritten by checkout should show smart checkout dialog"() {
check_operation_with_local_changes_overwritten_by_should_show_smart_checkout_dialog("checkout", 3);
}
public void "test merge with local changes overwritten by merge should show smart merge dialog"() {
check_operation_with_local_changes_overwritten_by_should_show_smart_checkout_dialog("merge");
}
def check_operation_with_local_changes_overwritten_by_should_show_smart_checkout_dialog(String operation, int numFiles = 1) {
Collection<String> expectedChanges = prepareLocalChangesOverwrittenBy(myUltimate, numFiles)
List<Change> changes = null;
checkoutOrMerge(operation, "feature", [
showSmartOperationDialog: { Project p, List<Change> cs, Collection<String> paths, String op, String force ->
changes = cs
DialogWrapper.CANCEL_EXIT_CODE
}
])
assertNotNull "Local changes were not shown in the dialog", changes
if (newGitVersion()) {
Iterable<String> actualChanges = ContainerUtil.map(changes, new Function<Change, String>() {
@Override
public String fun(Change change) {
return FileUtil.getRelativePath(myUltimate.root.path, change.afterRevision.file.path, '/'.toCharacter());
}
})
assertSameElements "Incorrect set of local changes was shown in the dialog", actualChanges, expectedChanges;
}
}
static boolean newGitVersion() {
return !GitVersionSpecialty.OLD_STYLE_OF_UNTRACKED_AND_LOCAL_CHANGES_WOULD_BE_OVERWRITTEN.existsIn(GitVersion.parse(git("version")));
}
public void "test agree to smart checkout should smart checkout"() {
def localChanges = agree_to_smart_operation("checkout", "Checked out <b><code>feature</code></b>")
assertCurrentBranch("feature");
cd myUltimate
def actual = cat(localChanges[0])
def expectedContent = LOCAL_CHANGES_OVERWRITTEN_BY.branchLine +
LOCAL_CHANGES_OVERWRITTEN_BY.initial +
LOCAL_CHANGES_OVERWRITTEN_BY.masterLine;
assertContent(expectedContent, actual)
}
public void "test agree to smart merge should smart merge"() {
def localChanges = agree_to_smart_operation("merge",
"Merged <b><code>feature</code></b> to <b><code>master</code></b><br/><a href='delete'>Delete feature</a>")
cd myUltimate
def actual = cat(localChanges[0])
def expectedContent = LOCAL_CHANGES_OVERWRITTEN_BY.branchLine +
LOCAL_CHANGES_OVERWRITTEN_BY.initial +
LOCAL_CHANGES_OVERWRITTEN_BY.masterLine;
assertContent(expectedContent, actual)
}
Collection<String> agree_to_smart_operation(String operation, String expectedSuccessMessage) {
def localChanges = prepareLocalChangesOverwrittenBy(myUltimate)
AgreeToSmartOperationTestUiHandler handler = new AgreeToSmartOperationTestUiHandler()
checkoutOrMerge(operation, "feature", handler)
assertNotNull "No success notification was shown", myVcsNotifier.lastNotification
assertEquals "Success message is incorrect", expectedSuccessMessage, myVcsNotifier.lastNotification.content
localChanges
}
Collection<String> prepareLocalChangesOverwrittenBy(GitRepository repository, int numFiles = 1) {
Collection<String> localChanges = ContainerUtil.newArrayList();
for (int i = 0; i < numFiles; i++) {
localChanges.add(String.format("local%d.txt", i));
}
localChangesOverwrittenByWithoutConflict(repository, "feature", localChanges)
updateChangeListManager()
myRepositories.each {
if (it != repository) {
branchWithCommit(it, "feature")
}
}
return localChanges;
}
public void "test deny to smart checkout in first repo should show nothing"() {
check_deny_to_smart_operation_in_first_repo_should_show_nothing("checkout");
}
public void "test deny to smart merge in first repo should show nothing"() {
check_deny_to_smart_operation_in_first_repo_should_show_nothing("merge");
}
public void check_deny_to_smart_operation_in_first_repo_should_show_nothing(String operation) {
prepareLocalChangesOverwrittenBy(myUltimate)
checkoutOrMerge(operation, "feature", [
showSmartOperationDialog: { Project p, List<Change> cs, Collection<String> paths, String op, String force
-> GitSmartOperationDialog.CANCEL_EXIT_CODE
},
] as GitBranchUiHandler )
assertNull "Notification was unexpectedly shown:" + myVcsNotifier.lastNotification, myVcsNotifier.lastNotification
assertCurrentBranch("master");
}
public void "test deny to smart checkout in second repo should show rollback proposal"() {
check_deny_to_smart_operation_in_second_repo_should_show_rollback_proposal("checkout");
assertCurrentBranch(myUltimate, "feature")
assertCurrentBranch(myCommunity, "master")
assertCurrentBranch(myContrib, "master")
}
public void "test deny to smart merge in second repo should show rollback proposal"() {
check_deny_to_smart_operation_in_second_repo_should_show_rollback_proposal("merge");
}
public void check_deny_to_smart_operation_in_second_repo_should_show_rollback_proposal(String operation) {
prepareLocalChangesOverwrittenBy(myCommunity)
def rollbackMsg = null
checkoutOrMerge(operation, "feature", [
showSmartOperationDialog : {
Project p, List<Change> cs, Collection<String> paths, String op, String f -> GitSmartOperationDialog.CANCEL_EXIT_CODE
},
notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m; false }
] as GitBranchUiHandler )
assertNotNull "Rollback proposal was not shown", rollbackMsg
}
void "test force checkout in case of local changes that would be overwritten by checkout"() {
// IDEA-99849
prepareLocalChangesOverwrittenBy(myUltimate)
def uiHandler = [
showSmartOperationDialog: { Project p, List<Change> cs, Collection<String> paths, String op, String force ->
GitSmartOperationDialog.FORCE_EXIT_CODE;
},
] as GitBranchUiHandler
GitBranchWorker brancher = new GitBranchWorker(myProject, myPlatformFacade, myGit, uiHandler)
brancher.checkoutNewBranchStartingFrom("new_branch", "feature", myRepositories)
assertEquals("Notification about successful branch creation is incorrect",
"Checked out new branch <b><code>new_branch</code></b> from <b><code>feature</code></b>",
myVcsNotifier.lastNotification.content)
assertCurrentBranch("new_branch")
}
public void "test rollback of 'checkout branch as new branch' should delete branches"() {
branchWithCommit(myRepositories, "feature")
touch("feature.txt", "feature_content")
git("add feature.txt")
git("commit -m feature_changes")
git("checkout master")
unmergedFiles(myCommunity)
boolean rollbackProposed = false;
GitBranchWorker brancher = new GitBranchWorker(myProject, myPlatformFacade, myGit, [
showUnmergedFilesMessageWithRollback: { String s1, String s2 -> rollbackProposed = true ; true }
] as GitBranchUiHandler)
brancher.checkoutNewBranchStartingFrom("newBranch", "feature", myRepositories)
assertTrue("Rollback was not proposed if unmerged files prevented checkout in the second repository", rollbackProposed)
assertCurrentBranch("master");
myRepositories.each {
assertTrue "Branch 'newBranch' should have been deleted on rollback",
git(it, "branch").split("\n").grep( { it.contains("newBranch") }).isEmpty()
}
}
public void "test delete branch that is fully merged should go without problems"() {
myRepositories.each { cd it ; git("branch todelete") }
deleteBranch("todelete", []);
assertNotNull "Successful notification was not shown", myVcsNotifier.lastNotification
assertEquals "Successful notification is incorrect", "Deleted branch ${bcode("todelete")}", myVcsNotifier.lastNotification.content
}
public void "test delete unmerged branch should show dialog"() {
prepareUnmergedBranch(myCommunity)
boolean dialogShown = false
deleteBranch("todelete", [
showBranchIsNotFullyMergedDialog : { Project p, Map h, String ub, List mb, String bb -> dialogShown = true ; false },
notifyErrorWithRollbackProposal: { String t, String m, String rp -> false }
]);
assertTrue "'Branch is not fully merged' dialog was not shown", dialogShown
}
public void "test ok in unmerged branch dialog should force delete branch"() {
prepareUnmergedBranch(myUltimate)
deleteBranch("todelete", [
showBranchIsNotFullyMergedDialog : { Project p, Map h, String ub, List mb, String bb -> true },
]);
assertBranchDeleted("todelete")
}
public void "test cancel in unmerged branch dialog in not first repository should show rollback proposal"() {
prepareUnmergedBranch(myCommunity)
def rollbackMsg = null
deleteBranch("todelete", [
showBranchIsNotFullyMergedDialog : { Project p, Map h, String ub, List mb, String bb -> false },
notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m ; false }
]);
assertNotNull "Rollback messages was not shown", rollbackMsg
}
public void "test rollback delete branch should recreate branches"() {
prepareUnmergedBranch(myCommunity)
def rollbackMsg = null
deleteBranch("todelete", [
showBranchIsNotFullyMergedDialog : { Project p, Map h, String ub, List mb, String bb -> false },
notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m ; true }
]);
assertNotNull "Rollback messages was not shown", rollbackMsg
assertBranchExists(myUltimate, "todelete")
assertBranchExists(myCommunity, "todelete")
assertBranchExists(myContrib, "todelete")
}
public void "test deny rollback delete branch should do nothing"() {
prepareUnmergedBranch(myCommunity)
def rollbackMsg = null
deleteBranch("todelete", [
showBranchIsNotFullyMergedDialog : { Project p, Map h, String ub, List mb, String bb -> false },
notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m ; false }
]);
assertNotNull "Rollback messages was not shown", rollbackMsg
assertBranchDeleted(myUltimate, "todelete")
assertBranchExists(myCommunity, "todelete")
assertBranchExists(myContrib, "todelete")
}
public void "test delete branch merged to head but unmerged to upstream should show dialog"() {
// inspired by IDEA-83604
// for the sake of simplicity we deal with a single myCommunity repository for remote operations
prepareRemoteRepo(myCommunity)
cd myCommunity
git("checkout -b feature");
git("push -u origin feature")
// create a commit and merge it to master, but not to feature's upstream
touch("feature.txt", "feature content")
git("add feature.txt")
git("commit -m feature_branch")
git("checkout master")
git("merge feature")
// delete feature fully merged to current HEAD, but not to the upstream
boolean dialogShown = false;
GitBranchWorker brancher = new GitBranchWorker(myProject, myPlatformFacade, myGit, [
showBranchIsNotFullyMergedDialog : { Project p, Map h, String ub, List mb, String bb -> dialogShown = true; false }
] as GitBranchUiHandler)
brancher.deleteBranch("feature", [myCommunity])
assertTrue "'Branch is not fully merged' dialog was not shown", dialogShown
}
public void "test simple merge without problems"() {
branchWithCommit(myRepositories, "master2", "branch_file.txt", "branch content")
mergeBranch("master2", []);
assertNotNull "Success message wasn't shown", myVcsNotifier.lastNotification
assertEquals "Success message is incorrect",
"Merged ${bcode("master2")} to ${bcode("master")}<br/><a href='delete'>Delete master2</a>",
myVcsNotifier.lastNotification.content
assertFile(myUltimate, "branch_file.txt", "branch content");
assertFile(myCommunity, "branch_file.txt", "branch content");
assertFile(myContrib, "branch_file.txt", "branch content");
}
public void "test merge branch that is up-to-date"() {
myRepositories.each { cd it ; git("branch master2") }
mergeBranch("master2", []);
assertNotNull "Success message wasn't shown", myVcsNotifier.lastNotification
assertEquals "Success message is incorrect", "Already up-to-date<br/><a href='delete'>Delete master2</a>",
myVcsNotifier.lastNotification.content
}
public void "test merge one simple and other up to date"() {
branchWithCommit(myCommunity, "master2", "branch_file.txt", "branch content")
[myUltimate, myContrib].each { cd it ; git("branch master2") }
mergeBranch("master2", []);
assertNotNull "Success message wasn't shown", myVcsNotifier.lastNotification
assertEquals "Success message is incorrect",
"Merged ${bcode("master2")} to ${bcode("master")}<br/><a href='delete'>Delete master2</a>",
myVcsNotifier.lastNotification.content
assertFile(myCommunity, "branch_file.txt", "branch content");
}
public void "test merge with unmerged files in first repo should show notification"() {
branchWithCommit(myRepositories, "feature")
unmergedFiles(myUltimate)
boolean notificationShown = false;
mergeBranch("feature", [
showUnmergedFilesNotification: { String s, List l -> notificationShown = true }
])
assertTrue("Unmerged files notification was not shown", notificationShown)
}
public void "test merge with unmerged files in second repo should propose to rollback"() {
branchWithCommit(myRepositories, "feature")
unmergedFiles(myCommunity)
boolean rollbackProposed = false;
mergeBranch "feature", [
showUnmergedFilesMessageWithRollback: { String s1, String s2 -> rollbackProposed = true ; false }
]
assertTrue("Rollback was not proposed if unmerged files prevented checkout in the second repository", rollbackProposed)
}
public void "test rollback merge should reset merge"() {
branchWithCommit(myRepositories, "feature")
String ultimateTip = tip(myUltimate)
unmergedFiles(myCommunity)
mergeBranch "feature", [
showUnmergedFilesMessageWithRollback: { String s1, String s2 -> true },
getProgressIndicator: { new ProgressIndicatorBase() }
]
assertEquals "Merge in ultimate should have been reset", ultimateTip, tip(myUltimate)
}
private static String tip(GitRepository repo) {
cd repo
git("rev-list -1 HEAD")
}
public void "test deny rollback merge should leave as is"() {
branchWithCommit(myRepositories, "feature")
cd myUltimate
String ultimateTipAfterMerge = git("rev-list -1 feature")
unmergedFiles(myCommunity)
mergeBranch "feature", [
showUnmergedFilesMessageWithRollback: { String s1, String s2 -> false }
]
assertEquals "Merge in ultimate should have been reset", ultimateTipAfterMerge, tip(myUltimate)
}
public void test_checkout_in_detached_head() {
cd(myCommunity);
touch("file.txt", "some content");
add("file.txt");
commit("msg");
git(myCommunity, "checkout HEAD^");
checkoutBranch("master", []);
assertCurrentBranch("master");
}
// inspired by IDEA-127472
public void test_checkout_to_common_branch_when_branches_have_diverged() {
branchWithCommit(myUltimate, "feature", "feature-file.txt", "feature_content", false);
branchWithCommit(myCommunity, "newbranch", "newbranch-file.txt", "newbranch_content", false);
checkoutBranch("master", [])
assertCurrentBranch("master");
}
public void test_rollback_checkout_from_diverged_branches_should_return_to_proper_branches() {
branchWithCommit(myUltimate, "feature", "feature-file.txt", "feature_content", false);
branchWithCommit(myCommunity, "newbranch", "newbranch-file.txt", "newbranch_content", false);
unmergedFiles(myContrib)
checkoutBranch "master", [
showUnmergedFilesMessageWithRollback: { String s1, String s2 -> true },
]
assertCurrentBranch(myUltimate, "feature");
assertCurrentBranch(myCommunity, "newbranch");
assertCurrentBranch(myContrib, "master");
}
static def assertCurrentBranch(GitRepository repository, String name) {
def curBranch = git(repository, "branch").split("\n").find { it -> it.contains("*") }.replace('*', ' ').trim()
assertEquals("Current branch is incorrect in ${repository}", name, curBranch)
}
def assertCurrentBranch(String name) {
myRepositories.each { assertCurrentBranch(it, name) }
}
def checkoutNewBranch(String name, def uiHandler) {
GitBranchWorker brancher = new GitBranchWorker(myProject, myPlatformFacade, myGit, uiHandler as GitBranchUiHandler)
brancher.checkoutNewBranch(name, myRepositories)
}
def checkoutBranch(String name, def uiHandler) {
GitBranchWorker brancher = new GitBranchWorker(myProject, myPlatformFacade, myGit, uiHandler as GitBranchUiHandler)
brancher.checkout(name, myRepositories)
}
def mergeBranch(String name, def uiHandler) {
GitBranchWorker brancher = new GitBranchWorker(myProject, myPlatformFacade, myGit, uiHandler as GitBranchUiHandler)
brancher.merge(name, GitBrancher.DeleteOnMergeOption.PROPOSE, myRepositories)
}
def deleteBranch(String name, def uiHandler) {
GitBranchWorker brancher = new GitBranchWorker(myProject, myPlatformFacade, myGit, uiHandler as GitBranchUiHandler)
brancher.deleteBranch(name, myRepositories)
}
def checkoutOrMerge(def operation, String name, def uiHandler) {
if (operation == "checkout") {
checkoutBranch(name, uiHandler)
}
else {
mergeBranch(name, uiHandler)
}
}
private void prepareUnmergedBranch(GitRepository unmergedRepo) {
myRepositories.each {
git(it, "branch todelete")
}
cd unmergedRepo
git("checkout todelete")
touch("afile.txt", "content")
git("add afile.txt")
git("commit -m unmerged_commit")
git("checkout master")
}
void assertBranchDeleted(String name) {
myRepositories.each { assertBranchDeleted(it, name) }
}
static def assertBranchDeleted(GitRepository repo, String branch) {
assertFalse("Branch $branch should have been deleted from $repo", git(repo, "branch").contains(branch))
}
static def assertBranchExists(GitRepository repo, String branch) {
assertTrue("Branch $branch should exist in $repo", branchExists(repo, branch))
}
private static void assertFile(GitRepository repository, String path, String content) throws IOException {
cd repository
assertEquals "Content doesn't match", content, cat(path)
}
private static void assertContent(String expectedContent, String actual) {
expectedContent = StringUtil.convertLineSeparators(expectedContent, detectLineSeparators(actual).separatorString).trim()
actual = actual.trim()
assertEquals String.format("Content doesn't match.%nExpected:%n%s%nActual:%n%s%n",
substWhitespaces(expectedContent), substWhitespaces(actual)), expectedContent, actual
}
private static LineSeparator detectLineSeparators(String actual) {
char[] chars = CharArrayUtil.fromSequence(actual);
for (char c : chars) {
if (c == '\r') {
return LineSeparator.CRLF;
}
else if (c == '\n') { // if we are here, there was no \r before
return LineSeparator.LF;
}
}
return LineSeparator.LF;
}
private static String substWhitespaces(String s) {
return s.replaceAll("\r", Matcher.quoteReplacement("\\r")).replaceAll("\n", Matcher.quoteReplacement("\\n")).replaceAll(" ", "_")
}
class AgreeToSmartOperationTestUiHandler implements GitBranchUiHandler {
@NotNull
@Override
ProgressIndicator getProgressIndicator() {
new ProgressIndicatorBase()
}
@Override
int showSmartOperationDialog(
@NotNull Project project,
@NotNull List<Change> changes,
@NotNull Collection<String> paths,
@NotNull String operation,
@Nullable String forceButton) {
GitSmartOperationDialog.SMART_EXIT_CODE
}
@Override
boolean showBranchIsNotFullyMergedDialog(
Project project,
Map<GitRepository, List<GitCommit>> history,
String unmergedBranch,
List<String> mergedToBranches,
String baseBranch) {
throw new UnsupportedOperationException()
}
@Override
boolean notifyErrorWithRollbackProposal(@NotNull String title, @NotNull String message, @NotNull String rollbackProposal) {
throw new UnsupportedOperationException()
}
@Override
void showUnmergedFilesNotification(@NotNull String operationName, @NotNull Collection<GitRepository> repositories) {
throw new UnsupportedOperationException()
}
@Override
boolean showUnmergedFilesMessageWithRollback(@NotNull String operationName, @NotNull String rollbackProposal) {
throw new UnsupportedOperationException()
}
@Override
void showUntrackedFilesNotification(@NotNull String operationName, @NotNull VirtualFile root, @NotNull Collection<String> relativePaths) {
throw new UnsupportedOperationException()
}
@Override
boolean showUntrackedFilesDialogWithRollback(
@NotNull String operationName, @NotNull String rollbackProposal, @NotNull VirtualFile root, @NotNull Collection<String> relativePaths) {
throw new UnsupportedOperationException()
}
}
}