Newer
Older
bool GitClient::synchronousApplyPatch(const QString &workingDirectory,
const QString &file, QString *errorMessage)
{
if (Git::Constants::debug)
qDebug() << Q_FUNC_INFO << workingDirectory;
QStringList args;
args << QLatin1String("apply") << QLatin1String("--whitespace=fix") << file;
QByteArray outputText;
QByteArray errorText;
const bool rc = synchronousGit(workingDirectory, args, &outputText, &errorText);
if (rc) {
if (!errorText.isEmpty())
*errorMessage = tr("There were warnings while applying %1 to %2:\n%3").arg(file, workingDirectory, commandOutputFromLocal8Bit(errorText));
} else {
*errorMessage = tr("Unable apply patch %1 to %2: %3").arg(file, workingDirectory, commandOutputFromLocal8Bit(errorText));
return false;
}
return true;
}
// Factory function to create an asynchronous command
GitCommand *GitClient::createCommand(const QString &workingDirectory,
VCSBase::VCSBaseEditor* editor,
bool outputToWindow,
int editorLineNumber)
qDebug() << Q_FUNC_INFO << workingDirectory << editor;
VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
GitCommand* command = new GitCommand(binary(), workingDirectory, processEnvironment(), QVariant(editorLineNumber));
if (editor)
connect(command, SIGNAL(finished(bool,int,QVariant)), editor, SLOT(commandFinishedGotoLine(bool,int,QVariant)));
if (editor) { // assume that the commands output is the important thing
connect(command, SIGNAL(outputData(QByteArray)), outputWindow, SLOT(appendDataSilently(QByteArray)));
} else {
connect(command, SIGNAL(outputData(QByteArray)), outputWindow, SLOT(appendData(QByteArray)));
}
connect(command, SIGNAL(outputData(QByteArray)), editor, SLOT(setPlainTextDataFiltered(QByteArray)));
connect(command, SIGNAL(errorText(QString)), outputWindow, SLOT(appendError(QString)));
return command;
}
// Execute a single command
GitCommand *GitClient::executeGit(const QString &workingDirectory,
const QStringList &arguments,
VCSBase::VCSBaseEditor* editor,
bool outputToWindow,
GitCommand::TerminationReportMode tm,
int editorLineNumber,
bool unixTerminalDisabled)
outputWindow()->appendCommand(workingDirectory, QLatin1String(Constants::GIT_BINARY), arguments);
GitCommand *command = createCommand(workingDirectory, editor, outputToWindow, editorLineNumber);
command->addJob(arguments, m_settings.timeoutSeconds);
command->setTerminationReportMode(tm);
command->setUnixTerminalDisabled(unixTerminalDisabled);
command->execute();
return command;
// Return fixed arguments required to run
QStringList GitClient::binary() const
{
#ifdef Q_OS_WIN
QStringList args;
args << QLatin1String("cmd.exe") << QLatin1String("/c") << m_binaryPath;
return args;
#else
return QStringList(m_binaryPath);
#endif
}
QProcessEnvironment GitClient::processEnvironment() const
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
if (m_settings.adoptPath)
environment.insert(QLatin1String("PATH"), m_settings.path);
// Set up SSH and C locale (required by git using perl).
VCSBase::VCSBasePlugin::setProcessEnvironment(&environment);
return environment;
}
bool GitClient::synchronousGit(const QString &workingDirectory,
QByteArray* outputText,
QByteArray* errorText,
bool logCommandToWindow)
qDebug() << "synchronousGit" << workingDirectory << gitArguments;
if (logCommandToWindow)
outputWindow()->appendCommand(workingDirectory, m_binaryPath, gitArguments);
QProcess process;
process.setProcessEnvironment(processEnvironment());
QStringList args = binary(); // "cmd /c git" on Windows
const QString executable = args.front();
args.pop_front();
args.append(gitArguments);
process.start(executable, args);
process.closeWriteChannel();
if (!process.waitForStarted()) {
if (errorText) {
const QString msg = QString::fromLatin1("Unable to execute '%1': %2:")
.arg(binary().join(QString(QLatin1Char(' '))), process.errorString());
*errorText = msg.toLocal8Bit();
}
return false;
}
if (!Utils::SynchronousProcess::readDataFromProcess(process, m_settings.timeoutSeconds * 1000,
outputText, errorText, true)) {
*errorText->append(GitCommand::msgTimeout(m_settings.timeoutSeconds).toLocal8Bit());
Utils::SynchronousProcess::stopProcess(process);
qDebug() << "synchronousGit ex=" << process.exitStatus() << process.exitCode();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;

Friedemann Kleint
committed
static inline int
askWithDetailedText(QWidget *parent,
const QString &title, const QString &msg,
const QString &inf,
QMessageBox::StandardButton defaultButton,
QMessageBox::StandardButtons buttons = QMessageBox::Yes|QMessageBox::No)
{
QMessageBox msgBox(QMessageBox::Question, title, msg, buttons, parent);
msgBox.setDetailedText(inf);
msgBox.setDefaultButton(defaultButton);
return msgBox.exec();
}
// Convenience that pops up an msg box.
GitClient::StashResult GitClient::ensureStash(const QString &workingDirectory)
{
QString errorMessage;
const StashResult sr = ensureStash(workingDirectory, &errorMessage);
if (sr == StashFailed)
outputWindow()->appendError(errorMessage);

Friedemann Kleint
committed
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
return sr;
}
// Ensure that changed files are stashed before a pull or similar
GitClient::StashResult GitClient::ensureStash(const QString &workingDirectory, QString *errorMessage)
{
QString statusOutput;
switch (gitStatus(workingDirectory, false, &statusOutput, errorMessage)) {
case StatusChanged:
break;
case StatusUnchanged:
return StashUnchanged;
case StatusFailed:
return StashFailed;
}
const int answer = askWithDetailedText(m_core->mainWindow(), tr("Changes"),
tr("You have modified files. Would you like to stash your changes?"),
statusOutput, QMessageBox::Yes, QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel);
switch (answer) {
case QMessageBox::Cancel:
return StashCanceled;
case QMessageBox::Yes:
if (!executeSynchronousStash(workingDirectory, creatorStashMessage(QLatin1String("push")), errorMessage))

Friedemann Kleint
committed
return StashFailed;
break;
case QMessageBox::No: // At your own risk, so.
return NotStashed;
}
return Stashed;
}
// Trim a git status file spec: "modified: foo .cpp" -> "modified: foo .cpp"
static inline QString trimFileSpecification(QString fileSpec)
{
const int colonIndex = fileSpec.indexOf(QLatin1Char(':'));
if (colonIndex != -1) {
// Collapse the sequence of spaces
const int filePos = colonIndex + 2;
int nonBlankPos = filePos;
for ( ; fileSpec.at(nonBlankPos).isSpace(); nonBlankPos++) ;
if (nonBlankPos > filePos)
fileSpec.remove(filePos, nonBlankPos - filePos);
}
return fileSpec;
}
GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory,
bool untracked,
QString *output,

Robert Loehning
committed
QString *errorMessage,
bool *onBranch)
{
// Run 'status'. Note that git returns exitcode 1 if there are no added files.
QByteArray outputText;
QByteArray errorText;

Friedemann Kleint
committed
// @TODO: Use "--no-color" once it is supported
QStringList statusArgs(QLatin1String("status"));
if (untracked)
statusArgs << QLatin1String("-u");
const bool statusRc = synchronousGit(workingDirectory, statusArgs, &outputText, &errorText);

Friedemann Kleint
committed
GitCommand::removeColorCodes(&outputText);
if (output)
*output = commandOutputFromLocal8Bit(outputText);

Robert Loehning
committed
const bool branchKnown = outputText.contains(kBranchIndicatorC);
if (onBranch)
*onBranch = branchKnown;
// Is it something really fatal?

Robert Loehning
committed
if (!statusRc && !branchKnown && !outputText.contains("# Not currently on any branch.")) {
if (errorMessage) {
const QString error = commandOutputFromLocal8Bit(errorText);
*errorMessage = tr("Unable to obtain the status: %1").arg(error);
}
return StatusFailed;
}
// Unchanged (output text depending on whether -u was passed)
if (outputText.contains("nothing to commit")
|| outputText.contains("nothing added to commit but untracked files present"))
return StatusUnchanged;
return StatusChanged;
}
bool GitClient::getCommitData(const QString &workingDirectory,
QString *commitTemplate,
CommitData *d,
QString *errorMessage)
{
if (Git::Constants::debug)
qDebug() << Q_FUNC_INFO << workingDirectory;
d->clear();
// Find repo
const QString repoDirectory = GitClient::findRepositoryForDirectory(workingDirectory);
if (repoDirectory.isEmpty()) {
*errorMessage = msgRepositoryNotFound(workingDirectory);
return false;
}
d->panelInfo.repository = repoDirectory;
QDir gitDir(repoDirectory);
if (!gitDir.cd(QLatin1String(kGitDirectoryC))) {
*errorMessage = tr("The repository %1 is not initialized yet.").arg(repoDirectory);
return false;
}
// Read description
const QString descriptionFile = gitDir.absoluteFilePath(QLatin1String("description"));
if (QFileInfo(descriptionFile).isFile()) {
QFile file(descriptionFile);
if (file.open(QIODevice::ReadOnly|QIODevice::Text))
d->panelInfo.description = commandOutputFromLocal8Bit(file.readAll()).trimmed();
}
// Run status. Note that it has exitcode 1 if there are no added files.

Robert Loehning
committed
bool onBranch;
QString output;

Robert Loehning
committed
switch (gitStatus(repoDirectory, true, &output, errorMessage, &onBranch)) {
case StatusChanged:

Robert Loehning
committed
if (!onBranch) {
*errorMessage = tr("You did not checkout a branch.");
return false;
}
break;
case StatusUnchanged:
*errorMessage = msgNoChangedFiles();
return false;
case StatusFailed:
return false;
}
// Output looks like:
// # On branch [branchname]
// # Changes to be committed:
// # (use "git reset HEAD <file>..." to unstage)
// #
// # modified: somefile.cpp
// # new File: somenew.h
// #
// # Changed but not updated:
// # (use "git add <file>..." to update what will be committed)
// #
// # modified: someother.cpp
// #
// # Untracked files:
// # (use "git add <file>..." to include in what will be committed)
// #
// # list of files...

Friedemann Kleint
committed
if (!d->parseFilesFromStatus(output)) {
*errorMessage = msgParseFilesFailed();

Friedemann Kleint
committed
// Filter out untracked files that are not part of the project
VCSBase::VCSBaseSubmitEditor::filterUntrackedFilesOfProject(repoDirectory, &d->untrackedFiles);

Friedemann Kleint
committed
if (d->filesEmpty()) {
*errorMessage = msgNoChangedFiles();
return false;
}
d->panelData.author = readConfigValue(workingDirectory, QLatin1String("user.name"));
d->panelData.email = readConfigValue(workingDirectory, QLatin1String("user.email"));
// Get the commit template
QString templateFilename = readConfigValue(workingDirectory, QLatin1String("commit.template"));
// Make relative to repository
const QFileInfo templateFileInfo(templateFilename);
if (templateFileInfo.isRelative())
templateFilename = repoDirectory + QLatin1Char('/') + templateFilename;
QFile templateFile(templateFilename);
if (templateFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
*commitTemplate = QString::fromLocal8Bit(templateFile.readAll());
} else {
qWarning("Unable to read commit template %s: %s",
qPrintable(templateFilename),
qPrintable(templateFile.errorString()));
}
}
return true;
}
bool GitClient::addAndCommit(const QString &repositoryDirectory,
const GitSubmitEditorPanelData &data,
const QString &messageFile,
const QStringList &checkedFiles,
const QStringList &origCommitFiles,
const QStringList &origDeletedFiles)
qDebug() << "GitClient::addAndCommit:" << repositoryDirectory << checkedFiles << origCommitFiles;
// Do we need to reset any files that had been added before
// (did the user uncheck any previously added files)
const QSet<QString> resetFiles = origCommitFiles.toSet().subtract(checkedFiles.toSet());
if (!resetFiles.empty())
if (!synchronousReset(repositoryDirectory, resetFiles.toList()))
// Re-add all to make sure we have the latest changes, but only add those that aren't marked
// for deletion
QStringList addFiles = checkedFiles.toSet().subtract(origDeletedFiles.toSet()).toList();
if (!addFiles.isEmpty())
if (!synchronousAdd(repositoryDirectory, false, addFiles))
return false;
// Do the final commit
QStringList args;
args << QLatin1String("commit")
<< QLatin1String("-F") << QDir::toNativeSeparators(messageFile);
const QString &authorString = data.authorString();
if (!authorString.isEmpty())
args << QLatin1String("--author") << authorString;
const bool rc = synchronousGit(repositoryDirectory, args, &outputText, &errorText);
if (rc) {
outputWindow()->append(tr("Committed %n file(s).\n", 0, checkedFiles.size()));
} else {
outputWindow()->appendError(tr("Unable to commit %n file(s): %1\n", 0, checkedFiles.size()).arg(commandOutputFromLocal8Bit(errorText)));
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
/* 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
* VCSBase plugin. */
GitClient::RevertResult GitClient::revertI(QStringList files, bool *ptrToIsDirectory, QString *errorMessage)
{
if (Git::Constants::debug)
qDebug() << Q_FUNC_INFO << files;
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, false, &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.

Friedemann Kleint
committed
const QString modifiedState = QLatin1String("modified");
const QStringList allStagedFiles = data.stagedFileNames(modifiedState);
const QStringList allUnstagedFiles = data.unstagedFileNames(modifiedState);
// 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 (Git::Constants::debug)
qDebug() << Q_FUNC_INFO << data.stagedFiles << data.unstagedFiles << allStagedFiles << allUnstagedFiles << stagedFiles << unstagedFiles;
if (stagedFiles.empty() && unstagedFiles.empty())
return RevertUnchanged;
// Ask to revert (to do: Handle lists with a selection dialog)
const QMessageBox::StandardButton answer
= QMessageBox::question(m_core->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 (!stagedFiles.empty() && !synchronousReset(repoDirectory, stagedFiles, errorMessage))
return RevertFailed;
// Finally revert!
if (!synchronousCheckoutFiles(repoDirectory, stagedFiles + unstagedFiles, QString(), errorMessage))
return RevertFailed;
return RevertOk;
}
void GitClient::revert(const QStringList &files)
{
bool isDirectory;
QString errorMessage;
switch (revertI(files, &isDirectory, &errorMessage)) {
case RevertOk:
m_plugin->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;
}
}
pull(workingDirectory, m_settings.pullRebase);
}
void GitClient::pull(const QString &workingDirectory, bool rebase)
{
QStringList arguments(QLatin1String("pull"));
if (rebase)
arguments << QLatin1String("--rebase");
// Disable UNIX terminals to suppress SSH prompting.
GitCommand *cmd = executeGit(workingDirectory, arguments, 0, true,
GitCommand::ReportStderr, -1,
VCSBase::VCSBasePlugin::isSshPromptConfigured());
connectRepositoryChanged(workingDirectory, cmd);
// Need to clean up if something goes wrong
if (rebase) {
cmd->setCookie(QVariant(workingDirectory));
connect(cmd, SIGNAL(finished(bool,int,QVariant)), this, SLOT(slotPullRebaseFinished(bool,int,QVariant)),
Qt::QueuedConnection);
}
}
void GitClient::slotPullRebaseFinished(bool ok, int exitCode, const QVariant &cookie)
{
if (ok && exitCode == 0)
return;
// Abort rebase to clean if something goes wrong
VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance();
outwin->appendError(tr("The command 'git pull --rebase' failed, aborting rebase."));
const QString workingDir = cookie.toString();
QStringList arguments;
arguments << QLatin1String("rebase") << QLatin1String("--abort");
QByteArray stdOut;
QByteArray stdErr;
const bool rc = synchronousGit(workingDir, arguments, &stdOut, &stdErr, true);
outwin->append(commandOutputFromLocal8Bit(stdOut));
if (!rc)
outwin->appendError(commandOutputFromLocal8Bit(stdErr));
// Subversion: git svn
void GitClient::subversionFetch(const QString &workingDirectory)
{
QStringList args;
args << QLatin1String("svn") << QLatin1String("fetch");
// Disable UNIX terminals to suppress SSH prompting.
GitCommand *cmd = executeGit(workingDirectory, args, 0, true, GitCommand::ReportStderr,
-1, true);
// Enable SSH prompting
cmd->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured());
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
connectRepositoryChanged(workingDirectory, cmd);
}
void GitClient::subversionLog(const QString &workingDirectory)
{
if (Git::Constants::debug)
qDebug() << "subversionLog" << workingDirectory;
QStringList arguments;
arguments << QLatin1String("svn") << QLatin1String("log");
if (m_settings.logCount > 0)
arguments << (QLatin1String("--limit=") + QString::number(m_settings.logCount));
// Create a command editor, no highlighting or interaction.
const QString title = tr("Git SVN Log");
const QString editorId = QLatin1String(Git::Constants::C_GIT_COMMAND_LOG_EDITOR);
const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, QStringList());
VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, sourceFile, false, "svnLog", sourceFile);
executeGit(workingDirectory, arguments, editor);
}
// Disable UNIX terminals to suppress SSH prompting.
executeGit(workingDirectory, QStringList(QLatin1String("push")), 0,
true, GitCommand::ReportStderr,
VCSBase::VCSBasePlugin::isSshPromptConfigured());
}
QString GitClient::msgNoChangedFiles()
{
return tr("There are no modified files.");
}
void GitClient::stashPop(const QString &workingDirectory)
{
QStringList arguments(QLatin1String("stash"));
arguments << QLatin1String("pop");
GitCommand *cmd = executeGit(workingDirectory, arguments, 0, true);
connectRepositoryChanged(workingDirectory, cmd);
}
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
bool GitClient::synchronousStashRestore(const QString &workingDirectory,
const QString &stash,
const QString &branch /* = QString()*/,
QString *errorMessage)
{
QStringList arguments(QLatin1String("stash"));
if (branch.isEmpty()) {
arguments << QLatin1String("apply") << stash;
} else {
arguments << QLatin1String("branch") << branch << stash;
}
QByteArray outputText;
QByteArray errorText;
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
if (!rc) {
const QString stdErr = commandOutputFromLocal8Bit(errorText);
const QString msg = branch.isEmpty() ?
tr("Unable to restore stash %1: %2").arg(workingDirectory, stdErr) :
tr("Unable to restore stash %1 to branch %2: %3").arg(workingDirectory, branch, stdErr);
if (errorMessage) {
*errorMessage = msg;
} else {
outputWindow()->append(msg);
}
return false;
}
QString output = commandOutputFromLocal8Bit(outputText);
if (!output.isEmpty())
outputWindow()->append(output);
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
GitPlugin::instance()->gitVersionControl()->emitRepositoryChanged(workingDirectory);
return true;
}
bool GitClient::synchronousStashRemove(const QString &workingDirectory,
const QString &stash /* = QString() */,
QString *errorMessage /* = 0 */)
{
QStringList arguments(QLatin1String("stash"));
if (stash.isEmpty()) {
arguments << QLatin1String("clear");
} else {
arguments << QLatin1String("drop") << stash;
}
QByteArray outputText;
QByteArray errorText;
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
if (!rc) {
const QString stdErr = commandOutputFromLocal8Bit(errorText);
const QString msg = stash.isEmpty() ?
tr("Unable to remove stashes of %1: %2").arg(workingDirectory, stdErr) :
tr("Unable to remove stash %1 of %2: %3").arg(stash, workingDirectory, stdErr);
if (errorMessage) {
*errorMessage = msg;
} else {
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 = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
if (!rc) {
const QString msg = tr("Unable retrieve stash list of %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
if (errorMessage) {
*errorMessage = msg;
} else {
outputWindow()->append(msg);
}
return false;
}
Stash stash;
foreach(const QString &line, commandOutputLinesFromLocal8Bit(outputText))
if (stash.parseStashLine(line))
stashes->push_back(stash);
if (Git::Constants::debug)
qDebug() << Q_FUNC_INFO << *stashes;
return true;
}
QString GitClient::readConfig(const QString &workingDirectory, const QStringList &configVar)
{
QStringList arguments;
arguments << QLatin1String("config") << configVar;
QByteArray outputText;
QByteArray errorText;
if (synchronousGit(workingDirectory, arguments, &outputText, &errorText, false))
return commandOutputFromLocal8Bit(outputText);
return QString();
}
// Read a single-line config value, return trimmed
QString GitClient::readConfigValue(const QString &workingDirectory, const QString &configVar)
{
return readConfig(workingDirectory, QStringList(configVar)).remove(QLatin1Char('\n'));
}
GitSettings GitClient::settings() const
{
return m_settings;
}
void GitClient::setSettings(const GitSettings &s)
{
if (s != m_settings) {
m_settings = s;
if (QSettings *s = m_core->settings())
m_settings.toSettings(s);

Friedemann Kleint
committed
m_binaryPath = m_settings.gitBinaryPath();
m_cachedGitVersion = 0u;
m_hasCachedGitVersion = false;
}
}
void GitClient::connectRepositoryChanged(const QString & repository, GitCommand *cmd)
{
// Bind command success termination with repository to changed signal
if (!m_repositoryChangedSignalMapper) {
m_repositoryChangedSignalMapper = new QSignalMapper(this);
connect(m_repositoryChangedSignalMapper, SIGNAL(mapped(QString)),
m_plugin->gitVersionControl(), SIGNAL(repositoryChanged(QString)));
}
m_repositoryChangedSignalMapper->setMapping(cmd, repository);
connect(cmd, SIGNAL(success()), m_repositoryChangedSignalMapper, SLOT(map()),
Qt::QueuedConnection);
}
// determine version as '(major << 16) + (minor << 8) + patch' or 0.
unsigned GitClient::gitVersion(bool silent, QString *errorMessage /* = 0 */)
{
if (!m_hasCachedGitVersion) {
// Do not execute repeatedly if that fails (due to git
// not being installed) until settings are changed.
m_cachedGitVersion = synchronousGitVersion(silent, errorMessage);
m_hasCachedGitVersion = true;
}
return m_cachedGitVersion;
QString GitClient::gitVersionString(bool silent, QString *errorMessage)
{
if (const unsigned version = gitVersion(silent, errorMessage)) {
QString rc;
QTextStream(&rc) << (version >> 16) << '.'
<< (0xFF & (version >> 8)) << '.'
<< (version & 0xFF);
return rc;
}
return QString();
}
// determine version as '(major << 16) + (minor << 8) + patch' or 0.
unsigned GitClient::synchronousGitVersion(bool silent, QString *errorMessage /* = 0 */)
{
// run git --version
QByteArray outputText;
QByteArray errorText;
const bool rc = synchronousGit(QString(), QStringList("--version"), &outputText, &errorText);
if (!rc) {
const QString msg = tr("Unable to determine git version: %1").arg(commandOutputFromLocal8Bit(errorText));
if (errorMessage) {
*errorMessage = msg;
} else {
outputWindow()->append(msg);
outputWindow()->appendError(msg);
}
return 0;
}
// cut 'git version 1.6.5.1.sha'
const QString output = commandOutputFromLocal8Bit(outputText);
const 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();
const unsigned minor = versionPattern.cap(2).toUInt();
const unsigned patch = versionPattern.cap(3).toUInt();
return version(major, minor, patch);
} // namespace Internal
} // namespace Git