Newer
Older
switch (revertI(files, &isDirectory, &errorMessage, revertStaging)) {
case RevertOk:
GitPlugin::instance()->gitVersionControl()->emitFilesChanged(files);
case RevertCanceled:
break;
case RevertUnchanged: {
const QString msg = (isDirectory || files.size() > 1) ? msgNoChangedFiles() : tr("The file is not modified.");
}
break;
case RevertFailed:
outputWindow()->appendError(errorMessage);
break;
}
}
void GitClient::fetch(const QString &workingDirectory, const QString &remote)
{
QStringList arguments(QLatin1String("fetch"));
arguments << (remote.isEmpty() ? QLatin1String("--all") : remote);
VcsBase::Command *command = executeGit(workingDirectory, arguments, 0, true);
command->setCookie(workingDirectory);
connect(command, SIGNAL(success(QVariant)), this, SLOT(fetchFinished(QVariant)));
bool GitClient::executeAndHandleConflicts(const QString &workingDirectory,
const QStringList &arguments,
const QString &abortCommand)
// Disable UNIX terminals to suppress SSH prompting.
const unsigned flags = VcsBasePlugin::SshPasswordPrompt
| VcsBasePlugin::ShowStdOutInLogWindow
| VcsBasePlugin::ExpectRepoChanges;
const Utils::SynchronousProcessResponse resp = synchronousGit(workingDirectory, arguments, flags);
ConflictHandler conflictHandler(0, workingDirectory, abortCommand);
// Notify about changed files or abort the rebase.
const bool ok = resp.result == Utils::SynchronousProcessResponse::Finished;
conflictHandler.readStdOut(resp.stdOut);
bool GitClient::synchronousPull(const QString &workingDirectory, bool rebase)
{
QString abortCommand;
if (rebase) {
arguments << QLatin1String("--rebase");
abortCommand = QLatin1String("rebase");
} else {
abortCommand = QLatin1String("merge");
}
bool ok = executeAndHandleConflicts(workingDirectory, arguments, abortCommand);
if (ok)
updateSubmodulesIfNeeded(workingDirectory, true);
void GitClient::synchronousAbortCommand(const QString &workingDir, const QString &abortCommand)
{
// Abort to clean if something goes wrong
if (abortCommand.isEmpty()) {
// no abort command - checkout index to clean working copy.
synchronousCheckoutFiles(findRepositoryForDirectory(workingDir), QStringList(), QString(), 0, false);
return;
}
VcsBase::VcsBaseOutputWindow *outwin = VcsBase::VcsBaseOutputWindow::instance();
QStringList arguments;
arguments << abortCommand << QLatin1String("--abort");
QByteArray stdOut;
QByteArray stdErr;
const bool rc = fullySynchronousGit(workingDir, arguments, &stdOut, &stdErr,
VcsBasePlugin::ExpectRepoChanges);
outwin->append(commandOutputFromLocal8Bit(stdOut));
if (!rc)
outwin->appendError(commandOutputFromLocal8Bit(stdErr));
}
QString GitClient::synchronousTrackingBranch(const QString &workingDirectory, const QString &branch)
{
QString remote;
QString localBranch = branch.isEmpty() ? synchronousCurrentLocalBranch(workingDirectory) : branch;
if (localBranch.isEmpty())
return QString();
localBranch.prepend(QLatin1String("branch."));
remote = readConfigValue(workingDirectory, localBranch + QLatin1String(".remote"));
if (remote.isEmpty())
return QString();
const QString rBranch = readConfigValue(workingDirectory, localBranch + QLatin1String(".merge"))
.replace(QLatin1String("refs/heads/"), QString());
if (rBranch.isEmpty())
return QString();
return remote + QLatin1Char('/') + rBranch;
}
bool GitClient::synchronousSetTrackingBranch(const QString &workingDirectory,
const QString &branch, const QString &tracking)
{
QByteArray outputText;
QByteArray errorText;
QStringList arguments;
arguments << QLatin1String("branch");
if (gitVersion() >= 0x010800)
arguments << (QLatin1String("--set-upstream-to=") + tracking) << branch;
else
arguments << QLatin1String("--set-upstream") << branch << tracking;
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText);
if (!rc) {
const QString errorMessage = tr("Cannot set tracking branch: %1")
.arg(commandOutputFromLocal8Bit(errorText));
outputWindow()->appendError(errorMessage);
}
return rc;
}
void GitClient::handleMergeConflicts(const QString &workingDir, const QString &commit,
const QStringList &files, const QString &abortCommand)
QString message;
if (!commit.isEmpty())
message = tr("Conflicts detected with commit %1.").arg(commit);
else if (!files.isEmpty())
message = tr("Conflicts detected with files:\n%1").arg(files.join(QLatin1String("\n")));
QMessageBox mergeOrAbort(QMessageBox::Question, tr("Conflicts Detected"), message,
QMessageBox::NoButton, Core::ICore::mainWindow());
QPushButton *mergeToolButton = mergeOrAbort.addButton(tr("Run &Merge Tool"),
QMessageBox::AcceptRole);
mergeOrAbort.addButton(QMessageBox::Ignore);
if (abortCommand == QLatin1String("rebase"))
mergeOrAbort.addButton(tr("&Skip"), QMessageBox::RejectRole);
if (!abortCommand.isEmpty())
mergeOrAbort.addButton(QMessageBox::Abort);
switch (mergeOrAbort.exec()) {
synchronousAbortCommand(workingDir, abortCommand);
break;
case QMessageBox::Ignore:
break;
default: // Merge or Skip
if (mergeOrAbort.clickedButton() == mergeToolButton) {
merge(workingDir);
} else if (!abortCommand.isEmpty()) {
QStringList arguments = QStringList() << abortCommand << QLatin1String("--skip");
executeAndHandleConflicts(workingDir, arguments, abortCommand);
}
// Subversion: git svn
void GitClient::synchronousSubversionFetch(const QString &workingDirectory)
{
QStringList args;
args << QLatin1String("svn") << QLatin1String("fetch");
// Disable UNIX terminals to suppress SSH prompting.
const unsigned flags = VcsBasePlugin::SshPasswordPrompt
| VcsBasePlugin::ShowStdOutInLogWindow
| VcsBasePlugin::ShowSuccessMessage;
synchronousGit(workingDirectory, args, flags);
}
void GitClient::subversionLog(const QString &workingDirectory)
{
QStringList arguments;
arguments << QLatin1String("svn") << QLatin1String("log");
int logCount = settings()->intValue(GitSettings::logCountKey);
if (logCount > 0)
arguments << (QLatin1String("--limit=") + QString::number(logCount));
// Create a command editor, no highlighting or interaction.
const QString title = tr("Git SVN Log");
const Core::Id editorId = Git::Constants::C_GIT_COMMAND_LOG_EDITOR;
const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, QStringList());
VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("svnLog", sourceFile);
editor = createVcsEditor(editorId, title, sourceFile, CodecNone, "svnLog", sourceFile, 0);
executeGit(workingDirectory, arguments, editor);
}
void GitClient::push(const QString &workingDirectory, const QStringList &pushArgs)
QStringList arguments(QLatin1String("push"));
if (!pushArgs.isEmpty())
arguments += pushArgs;
executeGit(workingDirectory, arguments, 0, true);
}
bool GitClient::synchronousMerge(const QString &workingDirectory, const QString &branch)
{
QString command = QLatin1String("merge");
QStringList arguments;
arguments << command << branch;
return executeAndHandleConflicts(workingDirectory, arguments, command);
bool GitClient::canRebase(const QString &workingDirectory) const
{
const QString gitDir = findGitDirForRepository(workingDirectory);
if (QFileInfo(gitDir + QLatin1String("/rebase-apply")).exists()
|| QFileInfo(gitDir + QLatin1String("/rebase-merge")).exists()) {
VcsBase::VcsBaseOutputWindow::instance()->appendError(
tr("Rebase, merge or am is in progress. Finish "
"or abort it and then try again."));
return false;
}
return true;
}
void GitClient::rebase(const QString &workingDirectory, const QString &baseBranch)
// Git might request an editor, so this must be done asynchronously
// and without timeout
QString gitCommand = QLatin1String("rebase");
QStringList arguments;
arguments << gitCommand << baseBranch;
outputWindow()->appendCommand(workingDirectory,
settings()->stringValue(GitSettings::binaryPathKey),
arguments);
VcsBase::Command *command = createCommand(workingDirectory, 0, true);
new ConflictHandler(command, workingDirectory, gitCommand);
command->addJob(arguments, -1);
command->execute();
bool GitClient::synchronousRevert(const QString &workingDirectory, const QString &commit)
const QString command = QLatin1String("revert");
// Do not stash if --continue or --abort is given as the commit
if (!commit.startsWith(QLatin1Char('-')) && !beginStashScope(workingDirectory, command))
return false;
arguments << command << QLatin1String("--no-edit") << commit;
return executeAndHandleConflicts(workingDirectory, arguments, command);
}
bool GitClient::synchronousCherryPick(const QString &workingDirectory, const QString &commit)
const QString command = QLatin1String("cherry-pick");
// "commit" might be --continue or --abort
const bool isRealCommit = !commit.startsWith(QLatin1Char('-'));
if (isRealCommit && !beginStashScope(workingDirectory, command))
QStringList arguments(command);
if (isRealCommit && isRemoteCommit(workingDirectory, commit))
arguments << QLatin1String("-x");
arguments << commit;
return executeAndHandleConflicts(workingDirectory, arguments, command);
}
void GitClient::interactiveRebase(const QString &workingDirectory, const QString &commit, bool fixup)
arguments << QLatin1String("rebase") << QLatin1String("-i");
if (fixup)
arguments << QLatin1String("--autosquash");
arguments << commit + QLatin1Char('^');
outputWindow()->appendCommand(workingDirectory, settings()->stringValue(GitSettings::binaryPathKey), arguments);
if (fixup)
m_disableEditor = true;
VcsBase::Command *command = createCommand(workingDirectory, 0, true);
new ConflictHandler(command, workingDirectory, QLatin1String("rebase"));
command->addJob(arguments, -1);
command->execute();
command->setCookie(workingDirectory);
if (fixup)
m_disableEditor = false;
QString GitClient::msgNoChangedFiles()
{
return tr("There are no modified files.");
}
QString GitClient::msgNoCommits(bool includeRemote)
{
return includeRemote ? tr("No commits were found") : tr("No local commits were found");
}
void GitClient::stashPop(const QString &workingDirectory, const QString &stash)
{
QStringList arguments(QLatin1String("stash"));
arguments << QLatin1String("pop");
if (!stash.isEmpty())
arguments << stash;
VcsBase::Command *cmd = executeGit(workingDirectory, arguments, 0, true, true);
new ConflictHandler(cmd, workingDirectory);
}
void GitClient::stashPop(const QString &workingDirectory)
{
stashPop(workingDirectory, QString());
}
bool GitClient::synchronousStashRestore(const QString &workingDirectory,
const QString &stash,
const QString &branch /* = QString()*/,
QString *errorMessage)
{
QStringList arguments(QLatin1String("stash"));
arguments << QLatin1String(pop ? "pop" : "apply") << stash;
arguments << QLatin1String("branch") << branch << stash;
QByteArray outputText;
QByteArray errorText;
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText,
VcsBasePlugin::ExpectRepoChanges);
if (!rc) {
const QString stdErr = commandOutputFromLocal8Bit(errorText);
const QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory);
const QString msg = branch.isEmpty() ?
arg(nativeWorkingDir, stdErr) :
tr("Cannot restore stash \"%1\" to branch \"%2\": %3").
arg(nativeWorkingDir, branch, stdErr);
return false;
}
QString output = commandOutputFromLocal8Bit(outputText);
if (!output.isEmpty())
outputWindow()->append(output);
return true;
}
bool GitClient::synchronousStashRemove(const QString &workingDirectory,
const QString &stash /* = QString() */,
QString *errorMessage /* = 0 */)
{
QStringList arguments(QLatin1String("stash"));
arguments << QLatin1String("clear");
arguments << QLatin1String("drop") << stash;
QByteArray outputText;
QByteArray errorText;
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText);
if (!rc) {
const QString stdErr = commandOutputFromLocal8Bit(errorText);
const QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory);
const QString msg = stash.isEmpty() ?
arg(nativeWorkingDir, stdErr) :
arg(stash, nativeWorkingDir, stdErr);
return false;
}
QString output = commandOutputFromLocal8Bit(outputText);
if (!output.isEmpty())
outputWindow()->append(output);
void GitClient::branchList(const QString &workingDirectory)
{
QStringList arguments(QLatin1String("branch"));

Friedemann Kleint
committed
arguments << QLatin1String("-r") << QLatin1String(noColorOption);
executeGit(workingDirectory, arguments, 0, true);
}
void GitClient::stashList(const QString &workingDirectory)
{
QStringList arguments(QLatin1String("stash"));

Friedemann Kleint
committed
arguments << QLatin1String("list") << QLatin1String(noColorOption);
executeGit(workingDirectory, arguments, 0, true);
bool GitClient::synchronousStashList(const QString &workingDirectory,
QList<Stash> *stashes,
QString *errorMessage /* = 0 */)
{
stashes->clear();
QStringList arguments(QLatin1String("stash"));
arguments << QLatin1String("list") << QLatin1String(noColorOption);
QByteArray outputText;
QByteArray errorText;
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText);
const QString msg = tr("Cannot retrieve stash list of \"%1\": %2").
arg(QDir::toNativeSeparators(workingDirectory),
commandOutputFromLocal8Bit(errorText));
return false;
}
Stash stash;
foreach (const QString &line, commandOutputLinesFromLocal8Bit(outputText))
if (stash.parseStashLine(line))
stashes->push_back(stash);
return true;
}
QString GitClient::readConfig(const QString &workingDirectory, const QStringList &configVar) const
{
QStringList arguments;
arguments << QLatin1String("config") << configVar;
QByteArray outputText;
QByteArray errorText;
if (!fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText,
VcsBasePlugin::SuppressCommandLogging))
return QString();
if (Utils::HostOsInfo::isWindowsHost())
return Utils::SynchronousProcess::normalizeNewlines(QString::fromUtf8(outputText));
return commandOutputFromLocal8Bit(outputText);
QString GitClient::readConfigValue(const QString &workingDirectory, const QString &configVar) const
{
return readConfig(workingDirectory, QStringList(configVar)).remove(QLatin1Char('\n'));
}

Tuomas Puranen
committed
bool GitClient::cloneRepository(const QString &directory,const QByteArray &url)
{
QDir workingDirectory(directory);
const unsigned flags = VcsBasePlugin::SshPasswordPrompt
| VcsBasePlugin::ShowStdOutInLogWindow
| VcsBasePlugin::ShowSuccessMessage;

Tuomas Puranen
committed
if (workingDirectory.exists()) {
if (!synchronousInit(workingDirectory.path()))
return false;
QStringList arguments(QLatin1String("remote"));
arguments << QLatin1String("add") << QLatin1String("origin") << QLatin1String(url);
if (!fullySynchronousGit(workingDirectory.path(), arguments, 0))

Tuomas Puranen
committed
arguments.clear();
arguments << QLatin1String("fetch");
const Utils::SynchronousProcessResponse resp =
synchronousGit(workingDirectory.path(), arguments, flags);

Tuomas Puranen
committed
if (resp.result != Utils::SynchronousProcessResponse::Finished)
return false;
arguments.clear();
arguments << QLatin1String("config")
<< QLatin1String("branch.master.remote")
<< QLatin1String("origin");
if (!fullySynchronousGit(workingDirectory.path(), arguments, 0))

Tuomas Puranen
committed
return false;
arguments.clear();
arguments << QLatin1String("config")
<< QLatin1String("branch.master.merge")
<< QLatin1String("refs/heads/master");
if (!fullySynchronousGit(workingDirectory.path(), arguments, 0))

Tuomas Puranen
committed
return false;
return true;
} else {
QStringList arguments(QLatin1String("clone"));
arguments << QLatin1String(url) << workingDirectory.dirName();

Tuomas Puranen
committed
workingDirectory.cdUp();
const Utils::SynchronousProcessResponse resp =
synchronousGit(workingDirectory.path(), arguments, flags);
// TODO: Turn this into a VcsBaseClient and use resetCachedVcsInfo(...)
Core::ICore::vcsManager()->resetVersionControlForDirectory(workingDirectory.absolutePath());
return (resp.result == Utils::SynchronousProcessResponse::Finished);

Tuomas Puranen
committed
}
}
QString GitClient::vcsGetRepositoryURL(const QString &directory)
{
QStringList arguments(QLatin1String("config"));
QByteArray outputText;
arguments << QLatin1String("remote.origin.url");
if (fullySynchronousGit(directory, arguments, &outputText, 0,
VcsBasePlugin::SuppressCommandLogging)) {

Tuomas Puranen
committed
return commandOutputFromLocal8Bit(outputText);

Tuomas Puranen
committed
return QString();
}
GitSettings *GitClient::settings() const
{
return m_settings;
}
// determine version as '(major << 16) + (minor << 8) + patch' or 0.
unsigned GitClient::gitVersion(QString *errorMessage) const
{
const QString newGitBinary = gitBinaryPath();
if (m_gitVersionForBinary != newGitBinary && !newGitBinary.isEmpty()) {
// Do not execute repeatedly if that fails (due to git
// not being installed) until settings are changed.
m_gitVersionForBinary = newGitBinary;
return m_cachedGitVersion;
// determine version as '(major << 16) + (minor << 8) + patch' or 0.
unsigned GitClient::synchronousGitVersion(QString *errorMessage) const
{
if (gitBinaryPath().isEmpty())
return 0;
// run git --version
QByteArray outputText;
QByteArray errorText;
const bool rc = fullySynchronousGit(QString(), QStringList(QLatin1String("--version")),
&outputText, &errorText,
VcsBasePlugin::SuppressCommandLogging);
if (!rc) {
const QString msg = tr("Cannot determine git version: %1").arg(commandOutputFromLocal8Bit(errorText));
*errorMessage = msg;
return 0;
}
// cut 'git version 1.6.5.1.sha'
const QString output = commandOutputFromLocal8Bit(outputText);
QRegExp versionPattern(QLatin1String("^[^\\d]+(\\d+)\\.(\\d+)\\.(\\d+).*$"));
QTC_ASSERT(versionPattern.isValid(), return 0);
QTC_ASSERT(versionPattern.exactMatch(output), return 0);
const unsigned major = versionPattern.cap(1).toUInt(0, 16);
const unsigned minor = versionPattern.cap(2).toUInt(0, 16);
const unsigned patch = versionPattern.cap(3).toUInt(0, 16);
return version(major, minor, patch);
GitClient::StashInfo::StashInfo() :
m_client(GitPlugin::instance()->gitClient())
bool GitClient::StashInfo::init(const QString &workingDirectory, const QString &command,
StashFlag flag)
{
m_workingDir = workingDirectory;
m_flags = flag;
QString statusOutput;
switch (m_client->gitStatus(m_workingDir, StatusMode(NoUntracked | NoSubmodules),
&statusOutput, &errorMessage)) {
case GitClient::StatusChanged:
if (m_flags & NoPrompt)
executeStash(command, &errorMessage);
stashPrompt(command, statusOutput, &errorMessage);
case GitClient::StatusUnchanged:
m_stashResult = StashUnchanged;
break;
case GitClient::StatusFailed:
m_stashResult = StashFailed;
break;
}
if (m_stashResult == StashFailed)
VcsBase::VcsBaseOutputWindow::instance()->appendError(errorMessage);
void GitClient::StashInfo::stashPrompt(const QString &command, const QString &statusOutput,
QMessageBox msgBox(QMessageBox::Question, tr("Uncommitted Changes Found"),
tr("What would you like to do with local changes in:")
+ QLatin1String("\n\n\"") + m_workingDir + QLatin1Char('\"'),
QMessageBox::NoButton, Core::ICore::mainWindow());
QPushButton *stashAndPopButton = msgBox.addButton(tr("Stash && Pop"), QMessageBox::AcceptRole);
stashAndPopButton->setToolTip(tr("Stash local changes and pop when %1 finishes.").arg(command));
QPushButton *stashButton = msgBox.addButton(tr("Stash"), QMessageBox::AcceptRole);
stashButton->setToolTip(tr("Stash local changes and execute %1.").arg(command));
QPushButton *discardButton = msgBox.addButton(tr("Discard"), QMessageBox::AcceptRole);
discardButton->setToolTip(tr("Discard (reset) local changes and execute %1.").arg(command));
QPushButton *ignoreButton = 0;
if (m_flags & AllowUnstashed) {
ignoreButton = msgBox.addButton(QMessageBox::Ignore);
ignoreButton->setToolTip(tr("Execute %1 with local changes in working directory.")
.arg(command));
QPushButton *cancelButton = msgBox.addButton(QMessageBox::Cancel);
cancelButton->setToolTip(tr("Cancel %1.").arg(command));
m_stashResult = m_client->synchronousReset(m_workingDir, QStringList(), errorMessage) ?
StashUnchanged : StashFailed;
} else if (msgBox.clickedButton() == ignoreButton) { // At your own risk, so.
} else if (msgBox.clickedButton() == cancelButton) {
} else if (msgBox.clickedButton() == stashButton) {
m_stashResult = m_client->executeSynchronousStash(m_workingDir,
creatorStashMessage(command), errorMessage) ? StashUnchanged : StashFailed;
} else if (msgBox.clickedButton() == stashAndPopButton) {
executeStash(command, errorMessage);
void GitClient::StashInfo::executeStash(const QString &command, QString *errorMessage)
m_message = creatorStashMessage(command);
if (!m_client->executeSynchronousStash(m_workingDir, m_message, errorMessage))
m_stashResult = StashFailed;
bool GitClient::StashInfo::stashingFailed() const
case StashCanceled:
case StashFailed:
return !(m_flags & AllowUnstashed);
void GitClient::StashInfo::end()
{
if (m_stashResult == Stashed) {
QString stashName;
if (m_client->stashNameFromMessage(m_workingDir, m_message, &stashName))
m_client->stashPop(m_workingDir, stashName);
}
m_stashResult = NotStashed;
}
} // namespace Internal
} // namespace Git