Newer
Older
bool GitPlugin::submitEditorAboutToClose()
if (!isCommitEditorOpen())
return false;
GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(submitEditor());
QTC_ASSERT(editor, return true);
Core::IDocument *editorDocument = editor->document();
QTC_ASSERT(editorDocument, return true);
// Submit editor closing. Make it write out the commit message
// and retrieve files
const QFileInfo editorFile(editorDocument->fileName());
const QFileInfo changeFile(m_commitMessageFileName);
// Paranoia!
if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
return true;
// Prompt user. Force a prompt unless submit was actually invoked (that
// is, the editor was closed or shutdown).
bool *promptData = m_settings.boolPointer(GitSettings::promptOnSubmitKey);
VcsBase::VcsBaseSubmitEditor::PromptSubmitResult answer;
if (editor->forceClose()) {
answer = VcsBase::VcsBaseSubmitEditor::SubmitDiscarded;
} else {
answer = editor->promptSubmit(tr("Closing Git Editor"),
tr("Do you want to commit the change?"),
tr("Git will not accept this commit. Do you want to continue to edit it?"),
promptData, !m_submitActionTriggered, false);
}
m_submitActionTriggered = false;
cleanCommitMessageFile();
VcsBase::SubmitFileModel *model = qobject_cast<VcsBase::SubmitFileModel *>(editor->fileModel());
bool closeEditor = true;
CommitType commitType = editor->commitType();
QString amendSHA1 = editor->amendSHA1();
if (model->hasCheckedFiles() || !amendSHA1.isEmpty()) {
if (!Core::DocumentManager::saveDocument(editorDocument))
closeEditor = m_gitClient->addAndCommit(m_submitRepository, editor->panelData(),
commitType, amendSHA1,
m_commitMessageFileName, model);
cleanCommitMessageFile();
if (!m_gitClient->beginStashScope(m_submitRepository, QLatin1String("Rebase-fixup"), NoPrompt))
m_gitClient->interactiveRebase(m_submitRepository, amendSHA1, true);
} else {
m_gitClient->continueCommandIfNeeded(m_submitRepository);
}
return closeEditor;
m_gitClient->fetch(currentState().topLevel(), QString());
QString topLevel = state.topLevel();
bool rebase = m_gitClient->settings()->boolValue(GitSettings::pullRebaseKey);
if (!rebase) {
QString currentBranch = m_gitClient->synchronousCurrentLocalBranch(topLevel);
if (!currentBranch.isEmpty()) {
currentBranch.prepend(QLatin1String("branch."));
currentBranch.append(QLatin1String(".rebase"));
rebase = (m_gitClient->readConfigValue(topLevel, currentBranch) == QLatin1String("true"));

Friedemann Kleint
committed
if (!m_gitClient->beginStashScope(topLevel, QLatin1String("Pull"), rebase ? Default : AllowUnstashed))
m_gitClient->synchronousPull(topLevel, rebase);
QTC_ASSERT(state.hasTopLevel(), return);
}
void GitPlugin::startMergeTool()
{
const VcsBase::VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
m_gitClient->merge(state.topLevel());
}
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
void GitPlugin::continueOrAbortCommand()
{
const VcsBase::VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
QObject *action = QObject::sender();
if (action == m_abortMergeAction)
m_gitClient->synchronousMerge(state.topLevel(), QLatin1String("--abort"));
else if (action == m_abortRebaseAction)
m_gitClient->synchronousRebase(state.topLevel(), QLatin1String("--abort"));
else if (action == m_abortCherryPickAction)
m_gitClient->synchronousCherryPick(state.topLevel(), QLatin1String("--abort"));
else if (action == m_abortRevertAction)
m_gitClient->synchronousRevert(state.topLevel(), QLatin1String("--abort"));
else if (action == m_continueRebaseAction)
m_gitClient->synchronousRebase(state.topLevel(), QLatin1String("--continue"));
else if (action == m_continueCherryPickAction)
m_gitClient->synchronousCherryPick(state.topLevel(), QLatin1String("--continue"));
else if (action == m_continueRevertAction)
m_gitClient->synchronousRevert(state.topLevel(), QLatin1String("--continue"));
updateContinueAndAbortCommands();
}
// Retrieve member function of git client stored as user data of action
static inline GitClientMemberFunc memberFunctionFromAction(const QObject *o)
{
if (o) {
if (const QAction *action = qobject_cast<const QAction *>(o)) {
const QVariant v = action->data();
if (v.canConvert<GitClientMemberFunc>())
return qvariant_cast<GitClientMemberFunc>(v);
}
}
return 0;
}
void GitPlugin::gitClientMemberFuncRepositoryAction()
{
QTC_ASSERT(state.hasTopLevel(), return);
// Retrieve member function and invoke on repository
GitClientMemberFunc func = memberFunctionFromAction(sender());
QTC_ASSERT(func, return);
(m_gitClient->*func)(state.topLevel());
}
void GitPlugin::cleanProject()
{
QTC_ASSERT(state.hasProject(), return);
cleanRepository(state.currentProjectPath());
}
void GitPlugin::cleanRepository()
{
QTC_ASSERT(state.hasTopLevel(), return);
cleanRepository(state.topLevel());
}
void GitPlugin::cleanRepository(const QString &directory)
{
// Find files to be deleted
QString errorMessage;
QStringList files;
QApplication::setOverrideCursor(Qt::WaitCursor);
const bool gotFiles = m_gitClient->synchronousCleanList(directory, &files, &ignoredFiles, &errorMessage);
QApplication::restoreOverrideCursor();
QMessageBox::warning(parent, tr("Unable to retrieve file list"), errorMessage);
if (files.isEmpty() && ignoredFiles.isEmpty()) {
QMessageBox::information(parent, tr("Repository Clean"),
tr("The repository is clean."));
return;
}
// Show in dialog
dialog.setFileList(directory, files, ignoredFiles);
dialog.exec();
}
void GitPlugin::updateSubmodules()
{
const VcsBase::VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
m_gitClient->submoduleUpdate(state.topLevel());
}
// If the file is modified in an editor, make sure it is saved.
static bool ensureFileSaved(const QString &fileName)
{
const QList<Core::IEditor*> editors = Core::EditorManager::instance()->editorsForFileName(fileName);
if (editors.isEmpty())
return true;
Core::IDocument *document = editors.front()->document();
if (!document || !document->isModified())
return true;
bool canceled;
QList<Core::IDocument *> documents;
documents << document;
Core::DocumentManager::saveModifiedDocuments(documents, &canceled);
return !canceled;
}
void GitPlugin::applyCurrentFilePatch()
{
QTC_ASSERT(state.hasPatchFile() && state.hasTopLevel(), return);
const QString patchFile = state.currentPatchFile();
if (!ensureFileSaved(patchFile))
return;
applyPatch(state.topLevel(), patchFile);
}
void GitPlugin::promptApplyPatch()
{
QTC_ASSERT(state.hasTopLevel(), return);
applyPatch(state.topLevel(), QString());
}
void GitPlugin::applyPatch(const QString &workingDirectory, QString file)
{
// Ensure user has been notified about pending changes
if (!m_gitClient->beginStashScope(workingDirectory, QLatin1String("Apply-Patch"), AllowUnstashed))
return;
// Prompt for file
if (file.isEmpty()) {
const QString filter = tr("Patches (*.patch *.diff)");
file = QFileDialog::getOpenFileName(Core::ICore::mainWindow(),
QString(), filter);
if (file.isEmpty()) {
m_gitClient->endStashScope(workingDirectory);
}
// Run!
VcsBase::VcsBaseOutputWindow *outwin = VcsBase::VcsBaseOutputWindow::instance();
QString errorMessage;
if (m_gitClient->synchronousApplyPatch(workingDirectory, file, &errorMessage)) {
if (errorMessage.isEmpty())
outwin->append(tr("Patch %1 successfully applied to %2").arg(file, workingDirectory));
outwin->append(errorMessage);
} else {
outwin->appendError(errorMessage);
}
m_gitClient->endStashScope(workingDirectory);
void GitPlugin::stash()
{
// Simple stash without prompt, reset repo.
QTC_ASSERT(state.hasTopLevel(), return);
QString topLevel = state.topLevel();
if (!m_gitClient->beginStashScope(topLevel, QString(), NoPrompt))
if (m_gitClient->stashInfo(topLevel).result() == GitClient::StashInfo::Stashed && m_stashDialog)
m_stashDialog->refresh(state.topLevel(), true);
}
void GitPlugin::stashSnapshot()
{
// Prompt for description, restore immediately and keep on working.
QTC_ASSERT(state.hasTopLevel(), return);
const QString id = m_gitClient->synchronousStash(state.topLevel(), QString(),
GitClient::StashImmediateRestore|GitClient::StashPromptDescription);
if (!id.isEmpty() && m_stashDialog)
m_stashDialog->refresh(state.topLevel(), true);
}
// Create a non-modal dialog with refresh method or raise if it exists
template <class NonModalDialog>
inline void showNonModalDialog(const QString &topLevel,
QPointer<NonModalDialog> &dialog)
{
if (dialog) {
dialog->show();
dialog->raise();
} else {
dialog = new NonModalDialog(Core::ICore::mainWindow());
dialog->refresh(topLevel, true);
dialog->show();
}
}
void GitPlugin::branchList()
{
showNonModalDialog(currentState().topLevel(), m_branchDialog);
}
void GitPlugin::remoteList()
{
showNonModalDialog(currentState().topLevel(), m_remoteDialog);
}
void GitPlugin::stashList()
{
showNonModalDialog(currentState().topLevel(), m_stashDialog);
void GitPlugin::updateActions(VcsBase::VcsBasePlugin::ActionState as)
const bool repositoryEnabled = currentState().hasTopLevel();
if (m_stashDialog)
m_stashDialog->refresh(currentState().topLevel(), false);
if (m_branchDialog)
m_branchDialog->refresh(currentState().topLevel(), false);
if (m_remoteDialog)
m_remoteDialog->refresh(currentState().topLevel(), false);
m_commandLocator->setEnabled(repositoryEnabled);
if (!enableMenuAction(as, m_menuAction))
// Note: This menu is visible if there is no repository. Only
// 'Create Repository'/'Show' actions should be available.
const QString fileName = currentState().currentFileName();
foreach (Utils::ParameterAction *fileAction, m_fileActions)
fileAction->setParameter(fileName);
// If the current file looks like a patch, offer to apply
m_applyCurrentFilePatchAction->setParameter(currentState().currentPatchFileDisplayName());
const QString projectName = currentState().currentProjectName();
foreach (Utils::ParameterAction *projectAction, m_projectActions)
projectAction->setParameter(projectName);
foreach (QAction *repositoryAction, m_repositoryActions)
repositoryAction->setEnabled(repositoryEnabled);
m_submoduleUpdateAction->setVisible(repositoryEnabled
&& !m_gitClient->submoduleList(currentState().topLevel()).isEmpty());
updateRepositoryBrowserAction();
m_gerritPlugin->updateActions(repositoryEnabled);
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
void GitPlugin::updateContinueAndAbortCommands()
{
if (currentState().hasTopLevel()) {
GitClient::CommandInProgress gitCommandInProgress =
m_gitClient->checkCommandInProgress(currentState().topLevel());
m_mergeToolAction->setVisible(gitCommandInProgress != GitClient::NoCommand);
m_abortMergeAction->setVisible(gitCommandInProgress == GitClient::Merge);
m_abortCherryPickAction->setVisible(gitCommandInProgress == GitClient::CherryPick);
m_abortRevertAction->setVisible(gitCommandInProgress == GitClient::Revert);
m_abortRebaseAction->setVisible(gitCommandInProgress == GitClient::Rebase
|| gitCommandInProgress == GitClient::RebaseMerge);
m_continueCherryPickAction->setVisible(gitCommandInProgress == GitClient::CherryPick);
m_continueRevertAction->setVisible(gitCommandInProgress == GitClient::Revert);
m_continueRebaseAction->setVisible(gitCommandInProgress == GitClient::Rebase
|| gitCommandInProgress == GitClient::RebaseMerge);
} else {
m_mergeToolAction->setVisible(false);
m_abortMergeAction->setVisible(false);
m_abortCherryPickAction->setVisible(false);
m_abortRevertAction->setVisible(false);
m_abortRebaseAction->setVisible(false);
m_continueCherryPickAction->setVisible(false);
m_continueRevertAction->setVisible(false);
m_continueRebaseAction->setVisible(false);
}
}
void GitPlugin::updateRepositoryBrowserAction()
{
const bool repositoryEnabled = currentState().hasTopLevel();
const bool hasRepositoryBrowserCmd = !settings().stringValue(GitSettings::repositoryBrowserCmd).isEmpty();
m_repositoryBrowserAction->setEnabled(repositoryEnabled && hasRepositoryBrowserCmd);
}
const GitSettings &GitPlugin::settings() const
}
void GitPlugin::setSettings(const GitSettings &s)
{
if (s == m_settings)
return;
m_settings = s;
static_cast<GitVersionControl *>(versionControl())->emitConfigurationChanged();
updateRepositoryBrowserAction();
}
GitClient *GitPlugin::gitClient() const
{
return m_gitClient;
}
void GitPlugin::testStatusParsing_data()
{
QTest::addColumn<FileStates>("first");
QTest::addColumn<FileStates>("second");
QTest::newRow(" M") << FileStates(ModifiedFile) << FileStates(UnknownFileState);
QTest::newRow(" D") << FileStates(DeletedFile) << FileStates(UnknownFileState);
QTest::newRow("M ") << (ModifiedFile | StagedFile) << FileStates(UnknownFileState);
QTest::newRow("MM") << (ModifiedFile | StagedFile) << FileStates(ModifiedFile);
QTest::newRow("MD") << (ModifiedFile | StagedFile) << FileStates(DeletedFile);
QTest::newRow("A ") << (AddedFile | StagedFile) << FileStates(UnknownFileState);
QTest::newRow("AM") << (AddedFile | StagedFile) << FileStates(ModifiedFile);
QTest::newRow("AD") << (AddedFile | StagedFile) << FileStates(DeletedFile);
QTest::newRow("D ") << (DeletedFile | StagedFile) << FileStates(UnknownFileState);
QTest::newRow("DM") << (DeletedFile | StagedFile) << FileStates(ModifiedFile);
QTest::newRow("R ") << (RenamedFile | StagedFile) << FileStates(UnknownFileState);
QTest::newRow("RM") << (RenamedFile | StagedFile) << FileStates(ModifiedFile);
QTest::newRow("RD") << (RenamedFile | StagedFile) << FileStates(DeletedFile);
QTest::newRow("C ") << (CopiedFile | StagedFile) << FileStates(UnknownFileState);
QTest::newRow("CM") << (CopiedFile | StagedFile) << FileStates(ModifiedFile);
QTest::newRow("CD") << (CopiedFile | StagedFile) << FileStates(DeletedFile);
QTest::newRow("??") << FileStates(UntrackedFile) << FileStates(UnknownFileState);
// Merges
QTest::newRow("DD") << (DeletedFile | UnmergedFile | UnmergedUs | UnmergedThem) << FileStates(UnknownFileState);
QTest::newRow("AA") << (AddedFile | UnmergedFile | UnmergedUs | UnmergedThem) << FileStates(UnknownFileState);
QTest::newRow("UU") << (ModifiedFile | UnmergedFile | UnmergedUs | UnmergedThem) << FileStates(UnknownFileState);
QTest::newRow("AU") << (AddedFile | UnmergedFile | UnmergedUs) << FileStates(UnknownFileState);
QTest::newRow("UD") << (DeletedFile | UnmergedFile | UnmergedThem) << FileStates(UnknownFileState);
QTest::newRow("UA") << (AddedFile | UnmergedFile | UnmergedThem) << FileStates(UnknownFileState);
QTest::newRow("DU") << (DeletedFile | UnmergedFile | UnmergedUs) << FileStates(UnknownFileState);
}
void GitPlugin::testStatusParsing()
{
CommitData data;
QFETCH(FileStates, first);
QFETCH(FileStates, second);
QString output = QLatin1String("## master...origin/master [ahead 1]\n");
output += QString::fromLatin1(QTest::currentDataTag()) + QLatin1String(" main.cpp\n");
data.parseFilesFromStatus(output);
QCOMPARE(data.files.at(0).first, first);
if (second == UnknownFileState)
QCOMPARE(data.files.size(), 1);
else
QCOMPARE(data.files.at(1).first, second);
}
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
void GitPlugin::testDiffFileResolving_data()
{
QTest::addColumn<QByteArray>("header");
QTest::addColumn<QByteArray>("fileName");
QTest::newRow("New") << QByteArray(
"diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp\n"
"new file mode 100644\n"
"index 0000000..40997ff\n"
"--- /dev/null\n"
"+++ b/src/plugins/git/giteditor.cpp\n"
"@@ -0,0 +1,281 @@\n\n")
<< QByteArray("src/plugins/git/giteditor.cpp");
QTest::newRow("Deleted") << QByteArray(
"diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp\n"
"deleted file mode 100644\n"
"index 40997ff..0000000\n"
"--- a/src/plugins/git/giteditor.cpp\n"
"+++ /dev/null\n"
"@@ -1,281 +0,0 @@\n\n")
<< QByteArray("src/plugins/git/giteditor.cpp");
QTest::newRow("Normal") << QByteArray(
"diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp\n"
"index 69e0b52..8fc974d 100644\n"
"--- a/src/plugins/git/giteditor.cpp\n"
"+++ b/src/plugins/git/giteditor.cpp\n"
"@@ -49,6 +49,8 @@\n\n")
<< QByteArray("src/plugins/git/giteditor.cpp");
}
void GitPlugin::testDiffFileResolving()
{
GitEditor editor(editorParameters + 3, 0);
void GitPlugin::testLogResolving()
{
QByteArray data(
"commit 50a6b54c03219ad74b9f3f839e0321be18daeaf6 (HEAD, origin/master)\n"
"Merge: 3587b51 bc93ceb\n"
"Author: Junio C Hamano <gitster@pobox.com>\n"
"Date: Fri Jan 25 12:53:31 2013 -0800\n"
"\n"
" Merge branch 'for-junio' of git://bogomips.org/git-svn\n"
" \n"
" * 'for-junio' of git://bogomips.org/git-svn:\n"
" git-svn: Simplify calculation of GIT_DIR\n"
" git-svn: cleanup sprintf usage for uppercasing hex\n"
"\n"
"commit 3587b513bafd7a83d8c816ac1deed72b5e3a27e9\n"
"Author: Junio C Hamano <gitster@pobox.com>\n"
"Date: Fri Jan 25 12:52:55 2013 -0800\n"
"\n"
" Update draft release notes to 1.8.2\n"
" \n"
" Signed-off-by: Junio C Hamano <gitster@pobox.com>\n"
);
GitEditor editor(editorParameters + 1, 0);
editor.testLogResolving(data,
"50a6b54c - Merge branch 'for-junio' of git://bogomips.org/git-svn",
"3587b513 - Update draft release notes to 1.8.2");