"src/plugins/git@git.qt.io:mabubke/flatpak-qt-creator.git" did not exist on "2ef79dca1cd017ecada24430e15a0b8be6b27cb0"
Newer
Older
const bool rc = fullySynchronousGit(repositoryDirectory, args, &outputText, &errorText);
outputWindow()->append(msgCommitted(amendSHA1, commitCount));
outputWindow()->appendError(tr("Cannot commit %n file(s): %1\n", 0, commitCount).arg(commandOutputFromLocal8Bit(errorText)));
/* Revert: This function can be called with a file list (to revert single
* files) or a single directory (revert all). Qt Creator currently has only
* 'revert single' in its VCS menus, but the code is prepared to deal with
* reverting a directory pending a sophisticated selection dialog in the
GitClient::RevertResult GitClient::revertI(QStringList files,
bool *ptrToIsDirectory,
QString *errorMessage,
bool revertStaging)
{
if (files.empty())
return RevertCanceled;
// Figure out the working directory
const QFileInfo firstFile(files.front());
const bool isDirectory = firstFile.isDir();
if (ptrToIsDirectory)
*ptrToIsDirectory = isDirectory;
const QString workingDirectory = isDirectory ? firstFile.absoluteFilePath() : firstFile.absolutePath();
const QString repoDirectory = GitClient::findRepositoryForDirectory(workingDirectory);
if (repoDirectory.isEmpty()) {
*errorMessage = msgRepositoryNotFound(workingDirectory);
return RevertFailed;
}
// Check for changes
QString output;
switch (gitStatus(repoDirectory, StatusMode(NoUntracked | NoSubmodules), &output, errorMessage)) {
case StatusChanged:
break;
case StatusUnchanged:
return RevertUnchanged;
case StatusFailed:
return RevertFailed;
}

Friedemann Kleint
committed
if (!data.parseFilesFromStatus(output)) {
*errorMessage = msgParseFilesFailed();
return RevertFailed;
}
// If we are looking at files, make them relative to the repository
// directory to match them in the status output list.
if (!isDirectory) {
const QDir repoDir(repoDirectory);
const QStringList::iterator cend = files.end();
for (QStringList::iterator it = files.begin(); it != cend; ++it)
*it = repoDir.relativeFilePath(*it);
}
// From the status output, determine all modified [un]staged files.
const QStringList allStagedFiles = data.filterFiles(StagedFile | ModifiedFile);
const QStringList allUnstagedFiles = data.filterFiles(ModifiedFile);
// Unless a directory was passed, filter all modified files for the
// argument file list.
QStringList stagedFiles = allStagedFiles;
QStringList unstagedFiles = allUnstagedFiles;
if (!isDirectory) {
const QSet<QString> filesSet = files.toSet();
stagedFiles = allStagedFiles.toSet().intersect(filesSet).toList();
unstagedFiles = allUnstagedFiles.toSet().intersect(filesSet).toList();
}
if ((!revertStaging || stagedFiles.empty()) && unstagedFiles.empty())
return RevertUnchanged;
// Ask to revert (to do: Handle lists with a selection dialog)
const QMessageBox::StandardButton answer
= QMessageBox::question(Core::ICore::mainWindow(),
tr("Revert"),
tr("The file has been changed. Do you want to revert it?"),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No);
if (answer == QMessageBox::No)
return RevertCanceled;
// Unstage the staged files
if (revertStaging && !stagedFiles.empty() && !synchronousReset(repoDirectory, stagedFiles, errorMessage))
return RevertFailed;
QStringList filesToRevert = unstagedFiles;
if (revertStaging)
filesToRevert += stagedFiles;
// Finally revert!
if (!synchronousCheckoutFiles(repoDirectory, filesToRevert, QString(), errorMessage, revertStaging))
return RevertFailed;
return RevertOk;
}
void GitClient::revert(const QStringList &files, bool revertStaging)
{
bool isDirectory;
QString errorMessage;
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.");
outputWindow()->append(msg);
}
break;
case RevertFailed:
outputWindow()->append(errorMessage);
break;
}
}
bool GitClient::synchronousFetch(const QString &workingDirectory, const QString &remote)
{
QStringList arguments(QLatin1String("fetch"));
if (!remote.isEmpty())
arguments << remote;
// Disable UNIX terminals to suppress SSH prompting.
const unsigned flags = VcsBase::VcsBasePlugin::SshPasswordPrompt|VcsBase::VcsBasePlugin::ShowStdOutInLogWindow
|VcsBase::VcsBasePlugin::ShowSuccessMessage;
const Utils::SynchronousProcessResponse resp = synchronousGit(workingDirectory, arguments, flags);
return resp.result == Utils::SynchronousProcessResponse::Finished;
}
bool GitClient::executeAndHandleConflicts(const QString &workingDirectory,
const QStringList &arguments,
const QString &abortCommand)
// Disable UNIX terminals to suppress SSH prompting.
const unsigned flags = VcsBase::VcsBasePlugin::SshPasswordPrompt|VcsBase::VcsBasePlugin::ShowStdOutInLogWindow;
const Utils::SynchronousProcessResponse resp = synchronousGit(workingDirectory, arguments, flags);
// Notify about changed files or abort the rebase.
const bool ok = resp.result == Utils::SynchronousProcessResponse::Finished;
GitPlugin::instance()->gitVersionControl()->emitRepositoryChanged(workingDirectory);
} else if (resp.stdOut.contains(QLatin1String("CONFLICT"))) {
// rebase conflict is output to stdOut
QRegExp conflictedCommit(QLatin1String("Patch failed at ([^\\n]*)"));
conflictedCommit.indexIn(resp.stdOut);
handleMergeConflicts(workingDirectory, conflictedCommit.cap(1), abortCommand);
} else if (resp.stdErr.contains(QLatin1String("conflict"))) {
// cherry-pick/revert conflict is output to stdErr
QRegExp conflictedCommit(QLatin1String("could not (?:apply|revert) ([^\\n]*)"));
conflictedCommit.indexIn(resp.stdErr);
handleMergeConflicts(workingDirectory, conflictedCommit.cap(1), abortCommand);
}
bool GitClient::synchronousPull(const QString &workingDirectory, bool rebase)
{
QString abortCommand;
if (rebase) {
arguments << QLatin1String("--rebase");
abortCommand = QLatin1String("rebase");
} else {
abortCommand = QLatin1String("merge");
}
return executeAndHandleConflicts(workingDirectory, arguments, abortCommand);
bool GitClient::synchronousCommandContinue(const QString &workingDirectory, const QString &command, bool hasChanges)
QStringList arguments;
arguments << command << QLatin1String(hasChanges ? "--continue" : "--skip");
return executeAndHandleConflicts(workingDirectory, arguments, command);
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, true);
outwin->append(commandOutputFromLocal8Bit(stdOut));
if (!rc)
outwin->appendError(commandOutputFromLocal8Bit(stdErr));
}
void GitClient::handleMergeConflicts(const QString &workingDir, const QString &commit, const QString &abortCommand)
QString message = commit.isEmpty() ? tr("Conflicts detected")
: tr("Conflicts detected with commit %1").arg(commit);
QMessageBox mergeOrAbort(QMessageBox::Question, tr("Conflicts Detected"),
message, QMessageBox::Ignore | QMessageBox::Abort);
QPushButton *mergeToolButton = mergeOrAbort.addButton(tr("Run &Merge Tool"),
QMessageBox::ActionRole);
if (abortCommand == QLatin1String("rebase"))
mergeOrAbort.addButton(tr("&Skip"), QMessageBox::ActionRole);
switch (mergeOrAbort.exec()) {
synchronousAbortCommand(workingDir, abortCommand);
break;
case QMessageBox::Ignore:
break;
default: // Merge or Skip
if (mergeOrAbort.clickedButton() == mergeToolButton) {
merge(workingDir);
} else {
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 = VcsBase::VcsBasePlugin::SshPasswordPrompt|VcsBase::VcsBasePlugin::ShowStdOutInLogWindow
|VcsBase::VcsBasePlugin::ShowSuccessMessage;
const Utils::SynchronousProcessResponse resp = synchronousGit(workingDirectory, args, flags);
// Notify about changes.
if (resp.result == Utils::SynchronousProcessResponse::Finished)
GitPlugin::instance()->gitVersionControl()->emitRepositoryChanged(workingDirectory);
}
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);
}
bool GitClient::synchronousPush(const QString &workingDirectory, const QString &remote)
// Disable UNIX terminals to suppress SSH prompting.
const unsigned flags = VcsBase::VcsBasePlugin::SshPasswordPrompt|VcsBase::VcsBasePlugin::ShowStdOutInLogWindow
|VcsBase::VcsBasePlugin::ShowSuccessMessage;
QStringList arguments(QLatin1String("push"));
if (!remote.isEmpty())
arguments << remote;
const Utils::SynchronousProcessResponse resp =
synchronousGit(workingDirectory, arguments, flags);
return resp.result == Utils::SynchronousProcessResponse::Finished;
}
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::synchronousRebase(const QString &workingDirectory, const QString &baseBranch,
const QString &topicBranch)
{
QString command = QLatin1String("rebase");
QStringList arguments;
arguments << command << baseBranch;
if (!topicBranch.isEmpty())
arguments << topicBranch;
return executeAndHandleConflicts(workingDirectory, arguments, command);
bool GitClient::revertCommit(const QString &workingDirectory, const QString &commit)
{
QStringList arguments;
QString command = QLatin1String("revert");
arguments << command << QLatin1String("--no-edit") << commit;
return executeAndHandleConflicts(workingDirectory, arguments, command);
}
bool GitClient::cherryPickCommit(const QString &workingDirectory, const QString &commit)
{
QStringList arguments;
QString command = QLatin1String("cherry-pick");
arguments << command << commit;
return executeAndHandleConflicts(workingDirectory, arguments, command);
}
QString GitClient::msgNoChangedFiles()
{
return tr("There are no modified files.");
}
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);
connectRepositoryChanged(workingDirectory, cmd);
}
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);
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);
outputWindow()->append(msg);
return false;
}
QString output = commandOutputFromLocal8Bit(outputText);
if (!output.isEmpty())
outputWindow()->append(output);
GitPlugin::instance()->gitVersionControl()->emitRepositoryChanged(workingDirectory);
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);
outputWindow()->append(msg);
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));
outputWindow()->append(msg);
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, false))
return QString();
if (Utils::HostOsInfo::isWindowsHost())
return QString::fromUtf8(outputText).remove(QLatin1Char('\r'));
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 = VcsBase::VcsBasePlugin::SshPasswordPrompt |
VcsBase::VcsBasePlugin::ShowStdOutInLogWindow|
VcsBase::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, 0, true))
return false;

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, 0, true))

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

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, false))

Tuomas Puranen
committed
return commandOutputFromLocal8Bit(outputText);
return QString();
}
GitSettings *GitClient::settings() const
{
return m_settings;
}
void GitClient::connectRepositoryChanged(const QString & repository, VcsBase::Command *cmd)
{
// Bind command success termination with repository to changed signal
if (!m_repositoryChangedSignalMapper) {
m_repositoryChangedSignalMapper = new QSignalMapper(this);
connect(m_repositoryChangedSignalMapper, SIGNAL(mapped(QString)),
GitPlugin::instance()->gitVersionControl(), SIGNAL(repositoryChanged(QString)));
}
m_repositoryChangedSignalMapper->setMapping(cmd, repository);
connect(cmd, SIGNAL(success(QVariant)), m_repositoryChangedSignalMapper, SLOT(map()),
Qt::QueuedConnection);
}
// 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, false);
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::StashGuard::StashGuard(const QString &workingDirectory, const QString &keyword, bool askUser) :
pop(true),
workingDir(workingDirectory)
{
client = GitPlugin::instance()->gitClient();
QString errorMessage;
stashResult = client->ensureStash(workingDir, keyword, askUser, &message, &errorMessage);
if (stashResult == GitClient::StashFailed)
VcsBase::VcsBaseOutputWindow::instance()->appendError(errorMessage);
}
GitClient::StashGuard::~StashGuard()
{
if (pop && stashResult == GitClient::Stashed) {
QString stashName;
if (client->stashNameFromMessage(workingDir, message, &stashName))
client->stashPop(workingDir, stashName);
}
}
void GitClient::StashGuard::preventPop()
{
pop = false;
}
bool GitClient::StashGuard::stashingFailed(bool includeNotStashed) const
{
switch (stashResult) {
case GitClient::StashCanceled:
case GitClient::StashFailed:
return true;
case GitClient::NotStashed:
return includeNotStashed;
default:
return false;
}
}
} // namespace Internal
} // namespace Git