Newer
Older
*errorMessage = message;
else
outputWindow()->appendError(message);
}
static inline void msgCannotRun(const QStringList &args, const QString &workingDirectory,
const QByteArray &error, QString *errorMessage)
{
const QString message = GitClient::tr("Cannot run \"%1 %2\" in \"%2\": %3")
.arg(QLatin1String("git ") + args.join(QLatin1String(" ")),
QDir::toNativeSeparators(workingDirectory),
commandOutputFromLocal8Bit(error));
msgCannotRun(message, errorMessage);
}
// ---------------- GitClient
const char *GitClient::stashNamePrefix = "stash@{";
GitClient::GitClient(GitSettings *settings) :
m_cachedGitVersion(0),
m_settings(settings),
m_disableEditor(false)
connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), this, SLOT(saveSettings()));
m_gitQtcEditor = QString::fromLatin1("\"%1\" -client -block -pid %2")
.arg(QCoreApplication::applicationFilePath())
.arg(QCoreApplication::applicationPid());
}
GitClient::~GitClient()
{
}
QString GitClient::findRepositoryForDirectory(const QString &dir)
{
if (dir.isEmpty() || dir.endsWith(QLatin1String("/.git"))
|| dir.contains(QLatin1String("/.git/"))) {
QString dotGit = QLatin1String(GIT_DIRECTORY);
// QFileInfo is outside loop, because it is faster this way
QFileInfo fileInfo;
if (directory.exists(dotGit)) {
fileInfo.setFile(directory, dotGit);
if (fileInfo.isFile())
return directory.absolutePath();
else if (directory.exists(QLatin1String(".git/config")))
return directory.absolutePath();
}
QString GitClient::findGitDirForRepository(const QString &repositoryDir) const
static QHash<QString, QString> repoDirCache;
QString &res = repoDirCache[repositoryDir];
if (!res.isEmpty())
return res;
synchronousRevParseCmd(repositoryDir, QLatin1String("--git-dir"), &res);
if (!QDir(res).isAbsolute())
res.prepend(repositoryDir + QLatin1Char('/'));
return res;
bool GitClient::managesFile(const QString &workingDirectory, const QString &fileName) const
{
QByteArray output;
QStringList arguments;
arguments << QLatin1String("ls-files") << QLatin1String("--error-unmatch") << fileName;
return fullySynchronousGit(workingDirectory, arguments, &output, 0,
VcsBasePlugin::SuppressCommandLogging);
}
VcsBase::VcsBaseEditorWidget *GitClient::findExistingVCSEditor(const char *registerDynamicProperty,
Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
Core::EditorManager::activateEditor(outputEditor);
outputEditor->document()->setContents(m_msgWait.toUtf8());
rc = VcsBase::VcsBaseEditorWidget::getVcsBaseEditor(outputEditor);
DiffEditor::DiffEditorDocument *GitClient::createDiffEditor(const QString documentId,
DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::findOrCreate(documentId, title);
QTC_ASSERT(diffEditorDocument, return 0);
VcsBasePlugin::setSource(diffEditorDocument, source);
return diffEditorDocument;
/* Create an editor associated to VCS output of a source file/directory
* (using the file's codec). Makes use of a dynamic property to find an
* existing instance and to reuse it (in case, say, 'git diff foo' is
* already open). */
VcsBase::VcsBaseEditorWidget *GitClient::createVcsEditor(
const Core::Id &id,
QString title,
const QString &source, // Source file or directory
CodecType codecType,
const char *registerDynamicProperty, // Dynamic property and value to identify that editor
const QString &dynamicPropertyValue,
VcsBase::VcsBaseEditorParameterWidget *configWidget) const
QTC_CHECK(!findExistingVCSEditor(registerDynamicProperty, dynamicPropertyValue));
// Create new, set wait message, set up with source and codec
Core::IEditor *outputEditor
= Core::EditorManager::openEditorWithContents(id, &title, m_msgWait.toUtf8(),
(Core::EditorManager::OpenInOtherSplit
| Core::EditorManager::NoNewSplits));
outputEditor->document()->setProperty(registerDynamicProperty, dynamicPropertyValue);
rc = VcsBase::VcsBaseEditorWidget::getVcsBaseEditor(outputEditor);
connect(rc, SIGNAL(annotateRevisionRequested(QString,QString,QString,int)),
this, SLOT(slotBlameRevisionRequested(QString,QString,QString,int)));
QTC_ASSERT(rc, return 0);
rc->setSource(source);
if (codecType == CodecSource)
rc->setCodec(getSourceCodec(source));
else if (codecType == CodecLogOutput)
rc->setCodec(encoding(source, "i18n.logOutputEncoding"));
rc->setForceReadOnly(true);
if (configWidget)
rc->setConfigurationWidget(configWidget);

Friedemann Kleint
committed
void GitClient::diff(const QString &workingDirectory,
const QStringList &unstagedFileNames,
const QStringList &stagedFileNames)
const QString title = tr("Git Diff");
const int timeout = settings()->intValue(GitSettings::timeoutKey);
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const QString documentId = QLatin1String("sideBySideOriginalFileName") + workingDirectory;
DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::find(documentId);
if (!diffEditorDocument)
newDocument = diffEditorDocument = createDiffEditor(documentId, workingDirectory, title);
Core::EditorManager::activateEditorForDocument(diffEditorDocument);
GitDiffHandler *handler = new GitDiffHandler(diffEditorDocument->controller(),
gitBinaryPath(),
workingDirectory,
processEnvironment(),
timeout);
if (unstagedFileNames.empty() && stagedFileNames.empty()) {
// local repository diff
handler->diffRepository();
} else if (!stagedFileNames.empty()) {
// diff of selected files only with --cached option, used in commit editor
handler->diffFiles(stagedFileNames, unstagedFileNames);
// current project diff
handler->diffProjects(unstagedFileNames);
} else {
const QString binary = settings()->stringValue(GitSettings::binaryPathKey);
const char *propertyName = "originalFileName";
VcsBase::VcsBaseEditorWidget *vcsEditor = findExistingVCSEditor(propertyName, workingDirectory);
if (!vcsEditor) {
GitCommitDiffArgumentsWidget *argWidget =
new GitCommitDiffArgumentsWidget(this,
workingDirectory,
unstagedFileNames,
stagedFileNames);
vcsEditor = createVcsEditor(Git::Constants::GIT_DIFF_EDITOR_ID,
title,
workingDirectory,
CodecSource,
propertyName,
workingDirectory,
argWidget);
newDocument = vcsEditor->editor()->document();
connect(vcsEditor, SIGNAL(diffChunkApplied(VcsBase::DiffChunk)),
argWidget, SLOT(executeCommand()));
connect(vcsEditor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)),
argWidget, SLOT(executeCommand()));
GitCommitDiffArgumentsWidget *argWidget = qobject_cast<GitCommitDiffArgumentsWidget *>(
vcsEditor->configurationWidget());
argWidget->setFileNames(unstagedFileNames, stagedFileNames);
QStringList userDiffArgs = argWidget->arguments();
vcsEditor->setWorkingDirectory(workingDirectory);
// Create a batch of 2 commands to be run after each other in case
// we have a mixture of staged/unstaged files as is the case
// when using the submit dialog.
VcsBase::Command *command = createCommand(workingDirectory, vcsEditor);
// Directory diff?
QStringList cmdArgs;
cmdArgs << QLatin1String("diff")
<< QLatin1String(noColorOption);
if (unstagedFileNames.empty() && stagedFileNames.empty()) {
QStringList arguments(cmdArgs);
arguments << userDiffArgs;
outputWindow()->appendCommand(workingDirectory, binary, arguments);
command->addJob(arguments, timeout);
} else {
// Files diff.
if (!unstagedFileNames.empty()) {
QStringList arguments(cmdArgs);
arguments << userDiffArgs
<< QLatin1String("--")
<< unstagedFileNames;
outputWindow()->appendCommand(workingDirectory, binary, arguments);
command->addJob(arguments, timeout);
}
if (!stagedFileNames.empty()) {
QStringList arguments(cmdArgs);
arguments << userDiffArgs
<< QLatin1String("--cached")
<< QLatin1String("--")
<< stagedFileNames;
outputWindow()->appendCommand(workingDirectory, binary, arguments);
command->addJob(arguments, timeout);
}
command->addFlags(diffExecutionFlags());
if (newDocument) {
GitDiffSwitcher *switcher = new GitDiffSwitcher(newDocument, this);
switcher->setWorkingDirectory(workingDirectory);
if (unstagedFileNames.empty() && stagedFileNames.empty()) {
// local repository diff
switcher->setDiffType(GitDiffSwitcher::DiffRepository);
} else if (!stagedFileNames.empty()) {
// diff of selected files only with --cached option, used in commit editor
switcher->setDiffType(GitDiffSwitcher::DiffFileList);
switcher->setFileList(stagedFileNames, unstagedFileNames);
} else {
// current project diff
switcher->setDiffType(GitDiffSwitcher::DiffProjectList);
switcher->setProjectList(unstagedFileNames);
}
}
void GitClient::diff(const QString &workingDirectory, const QString &fileName)
const QString title = tr("Git Diff \"%1\"").arg(fileName);
const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const QString documentId = QLatin1String("sideBySideOriginalFileName") + sourceFile;
DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::find(documentId);
if (!diffEditorDocument)
newDocument = diffEditorDocument = createDiffEditor(documentId, sourceFile, title);
Core::EditorManager::activateEditorForDocument(diffEditorDocument);
GitDiffHandler *handler = new GitDiffHandler(diffEditorDocument->controller(),
gitBinaryPath(),
workingDirectory,
processEnvironment(),
settings()->intValue(GitSettings::timeoutKey));
handler->diffFile(fileName);
const char *propertyName = "originalFileName";
VcsBase::VcsBaseEditorWidget *vcsEditor = findExistingVCSEditor(propertyName, sourceFile);
if (!vcsEditor) {
GitFileDiffArgumentsWidget *argWidget =
new GitFileDiffArgumentsWidget(this, workingDirectory, fileName);
vcsEditor = createVcsEditor(Git::Constants::GIT_DIFF_EDITOR_ID,
title,
sourceFile,
CodecSource,
propertyName,
sourceFile,
argWidget);
newDocument = vcsEditor->editor()->document();
connect(vcsEditor, SIGNAL(diffChunkApplied(VcsBase::DiffChunk)),
argWidget, SLOT(executeCommand()));
connect(vcsEditor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)),
argWidget, SLOT(executeCommand()));
vcsEditor->setWorkingDirectory(workingDirectory);
cmdArgs << QLatin1String("diff")
<< QLatin1String(noColorOption)
<< vcsEditor->configurationWidget()->arguments();
if (!fileName.isEmpty())
cmdArgs << QLatin1String("--") << fileName;
executeGit(workingDirectory, cmdArgs, vcsEditor, false, diffExecutionFlags());
if (newDocument) {
GitDiffSwitcher *switcher = new GitDiffSwitcher(newDocument, this);
switcher->setWorkingDirectory(workingDirectory);
switcher->setDiffType(GitDiffSwitcher::DiffFile);
switcher->setFileName(fileName);
void GitClient::diffBranch(const QString &workingDirectory,
const QStringList &diffArgs,
const QString title = tr("Git Diff Branch \"%1\"").arg(branchName);
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const QString documentId = QLatin1String("sideBySideBranchName") + branchName;
DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::find(documentId);
if (!diffEditorDocument)
newDocument = diffEditorDocument = createDiffEditor(documentId, workingDirectory, title);
Core::EditorManager::activateEditorForDocument(diffEditorDocument);
GitDiffHandler *handler = new GitDiffHandler(diffEditorDocument->controller(),
gitBinaryPath(),
workingDirectory,
processEnvironment(),
settings()->intValue(GitSettings::timeoutKey));
handler->diffBranch(branchName);
} else {
const char *propertyName = "BranchName";
const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, QStringList());
VcsBase::VcsBaseEditorWidget *vcsEditor = findExistingVCSEditor(propertyName, branchName);
if (!vcsEditor) {
vcsEditor = createVcsEditor(Git::Constants::GIT_DIFF_EDITOR_ID,
title,
sourceFile,
CodecSource,
propertyName,
branchName,
new GitBranchDiffArgumentsWidget(this,
workingDirectory,
diffArgs,
branchName));
newDocument = vcsEditor->editor()->document();
vcsEditor->setWorkingDirectory(workingDirectory);
cmdArgs << QLatin1String("diff")
<< QLatin1String(noColorOption)
executeGit(workingDirectory, cmdArgs, vcsEditor, false, diffExecutionFlags());
if (newDocument) {
GitDiffSwitcher *switcher = new GitDiffSwitcher(newDocument, this);
switcher->setWorkingDirectory(workingDirectory);
switcher->setDiffType(GitDiffSwitcher::DiffBranch);
switcher->setBaseArguments(diffArgs);
switcher->setBranchName(branchName);
void GitClient::merge(const QString &workingDirectory, const QStringList &unmergedFileNames)
{
MergeTool *mergeTool = new MergeTool(this);
if (!mergeTool->start(workingDirectory, unmergedFileNames))
delete mergeTool;
}
QStringList statusArgs = statusArguments();
statusArgs << QLatin1String("-u");
outwin->setRepository(workingDirectory);
VcsBase::Command *command = executeGit(workingDirectory, statusArgs, 0, true);
connect(command, SIGNAL(finished(bool,int,QVariant)), outwin, SLOT(clearRepository()),
Qt::QueuedConnection);
void GitClient::log(const QString &workingDirectory, const QString &fileName,
bool enableAnnotationContextMenu, const QStringList &args)
const QString msgArg = fileName.isEmpty() ? workingDirectory : fileName;
const QString title = tr("Git Log \"%1\"").arg(msgArg);
const Core::Id editorId = Git::Constants::GIT_LOG_EDITOR_ID;
const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("logFileName", sourceFile);
editor = createVcsEditor(editorId, title, sourceFile, CodecLogOutput, "logFileName", sourceFile,
new GitLogArgumentsWidget(this, workingDirectory,
enableAnnotationContextMenu,
editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu);
editor->setWorkingDirectory(workingDirectory);

Friedemann Kleint
committed
QStringList arguments;
arguments << QLatin1String("log") << QLatin1String(noColorOption)
<< QLatin1String(decorateOption);
int logCount = settings()->intValue(GitSettings::logCountKey);
if (logCount > 0)
arguments << QLatin1String("-n") << QString::number(logCount);
GitLogArgumentsWidget *argWidget = qobject_cast<GitLogArgumentsWidget *>(editor->configurationWidget());
argWidget->setBaseArguments(args);
QStringList userArgs = argWidget->arguments();
arguments.append(userArgs);
arguments << QLatin1String("--follow") << QLatin1String("--") << fileName;
executeGit(workingDirectory, arguments, editor);
void GitClient::reflog(const QString &workingDirectory)
{
const QString title = tr("Git Reflog \"%1\"").arg(workingDirectory);
const Core::Id editorId = Git::Constants::GIT_LOG_EDITOR_ID;
VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("reflogRepository", workingDirectory);
if (!editor) {
editor = createVcsEditor(editorId, title, workingDirectory, CodecLogOutput,
"reflogRepository", workingDirectory, 0);
}
editor->setWorkingDirectory(workingDirectory);
QStringList arguments;
arguments << QLatin1String("reflog") << QLatin1String(noColorOption)
<< QLatin1String(decorateOption);
int logCount = settings()->intValue(GitSettings::logCountKey);
if (logCount > 0)
arguments << QLatin1String("-n") << QString::number(logCount);
executeGit(workingDirectory, arguments, editor);
}
// Do not show "0000" or "^32ae4"
static inline bool canShow(const QString &sha)
{
if (sha.startsWith(QLatin1Char('^')))
return false;
if (sha.count(QLatin1Char('0')) == sha.size())
return false;
return true;
}
static inline QString msgCannotShow(const QString &sha)
{
return GitClient::tr("Cannot describe \"%1\".").arg(sha);
void GitClient::show(const QString &source, const QString &id,
const QStringList &args, const QString &name)
outputWindow()->appendError(msgCannotShow(id));
const QString title = tr("Git Show \"%1\"").arg(name.isEmpty() ? id : name);
const QFileInfo sourceFi(source);
const QString workingDirectory = sourceFi.isDir() ? sourceFi.absoluteFilePath() : sourceFi.absolutePath();
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const QString documentId = QLatin1String("sideBySideShow") + id;
DiffEditor::DiffEditorDocument *diffEditorDocument = DiffEditor::DiffEditorManager::find(documentId);
if (!diffEditorDocument)
newDocument = diffEditorDocument = createDiffEditor(documentId, source, title);
diffEditorDocument->controller()->setDescriptionEnabled(true);
Core::EditorManager::activateEditorForDocument(diffEditorDocument);
GitDiffHandler *handler = new GitDiffHandler(diffEditorDocument->controller(),
findRepositoryForDirectory(workingDirectory),
processEnvironment(),
settings()->intValue(GitSettings::timeoutKey));
handler->show(id);
} else {
const char *propertyName = "show";
const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
VcsBase::VcsBaseEditorWidget *vcsEditor = findExistingVCSEditor(propertyName, id);
if (!vcsEditor) {
vcsEditor = createVcsEditor(editorId,
title,
source,
CodecSource,
propertyName,
id,
new GitShowArgumentsWidget(this,
source,
args,
id));
newDocument = vcsEditor->editor()->document();
QStringList arguments;
arguments << QLatin1String("show")
<< QLatin1String(noColorOption)
<< QLatin1String(decorateOption)
vcsEditor->setWorkingDirectory(workingDirectory);
executeGit(workingDirectory, arguments, vcsEditor);
}
if (newDocument) {
GitDiffSwitcher *switcher = new GitDiffSwitcher(newDocument, this);
switcher->setDiffType(GitDiffSwitcher::DiffShow);
switcher->setFileName(source);
switcher->setBaseArguments(args);
switcher->setId(id);
settings()->writeSettings(Core::ICore::settings());
void GitClient::slotBlameRevisionRequested(const QString &workingDirectory, const QString &file,
QString change, int lineNumber)
{
// This might be invoked with a verbose revision description
// "SHA1 author subject" from the annotation context menu. Strip the rest.
const int blankPos = change.indexOf(QLatin1Char(' '));
if (blankPos != -1)
change.truncate(blankPos);
blame(workingDirectory, QStringList(), file, change, lineNumber);
}
QTextCodec *GitClient::getSourceCodec(const QString &file) const
{
return QFileInfo(file).isFile() ? VcsBase::VcsBaseEditorWidget::getCodec(file)
: encoding(file, "gui.encoding");
void GitClient::blame(const QString &workingDirectory,
const QString &fileName,
const Core::Id editorId = Git::Constants::GIT_BLAME_EDITOR_ID;
const QString id = VcsBase::VcsBaseEditorWidget::getTitleId(workingDirectory, QStringList(fileName), revision);
const QString title = tr("Git Blame \"%1\"").arg(id);
const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("blameFileName", id);
if (!editor) {
GitBlameArgumentsWidget *argWidget =
new GitBlameArgumentsWidget(this, workingDirectory, args,
editor = createVcsEditor(editorId, title, sourceFile, CodecSource, "blameFileName", id, argWidget);
argWidget->setEditor(editor);
}
editor->setWorkingDirectory(workingDirectory);
QStringList arguments(QLatin1String("blame"));
arguments << QLatin1String("--root");
arguments.append(editor->configurationWidget()->arguments());
arguments << QLatin1String("--") << fileName;
if (!revision.isEmpty())
arguments << revision;
executeGit(workingDirectory, arguments, editor, false, 0, lineNumber);
bool GitClient::synchronousCheckout(const QString &workingDirectory,
const QString &ref,
{
QByteArray outputText;
QByteArray errorText;
QStringList arguments = setupCheckoutArguments(workingDirectory, ref);
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText,
VcsBasePlugin::ExpectRepoChanges);
outputWindow()->append(commandOutputFromLocal8Bit(outputText));
msgCannotRun(arguments, workingDirectory, errorText, errorMessage);
updateSubmodulesIfNeeded(workingDirectory, true);
/* method used to setup arguments for checkout, in case user wants to create local branch */
QStringList GitClient::setupCheckoutArguments(const QString &workingDirectory,
const QString &ref)
{
QStringList arguments(QLatin1String("checkout"));
arguments << ref;
QStringList localBranches = synchronousRepositoryBranches(workingDirectory);
if (localBranches.contains(ref))
return arguments;
if (QMessageBox::question(Core::ICore::mainWindow(), tr("Create Local Branch"),
tr("Would you like to create a local branch?"),
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
return arguments;
}
if (synchronousCurrentLocalBranch(workingDirectory).isEmpty())
localBranches.removeFirst();
QString refSha;
if (!synchronousRevParseCmd(workingDirectory, ref, &refSha))
return arguments;
QString output;
QStringList forEachRefArgs(QLatin1String("refs/remotes/"));
forEachRefArgs << QLatin1String("--format=%(objectname) %(refname:short)");
if (!synchronousForEachRefCmd(workingDirectory, forEachRefArgs, &output))
return arguments;
QString remoteBranch;
const QString head(QLatin1String("/HEAD"));
foreach (const QString &singleRef, output.split(QLatin1Char('\n'))) {
if (singleRef.startsWith(refSha)) {
// branch name might be origin/foo/HEAD
if (!singleRef.endsWith(head) || singleRef.count(QLatin1Char('/')) > 1) {
remoteBranch = singleRef.mid(refSha.length() + 1);
if (remoteBranch == ref)
break;
}
}
}
BranchAddDialog branchAddDialog(localBranches, true, Core::ICore::mainWindow());
branchAddDialog.setTrackedBranchName(remoteBranch, true);
if (branchAddDialog.exec() != QDialog::Accepted)
return arguments;
arguments.removeLast();
arguments << QLatin1String("-b") << branchAddDialog.branchName();
if (branchAddDialog.track())
arguments << QLatin1String("--track") << remoteBranch;
else
arguments << QLatin1String("--no-track") << ref;
return arguments;
}
void GitClient::reset(const QString &workingDirectory, const QString &argument, const QString &commit)
unsigned flags = 0;
if (argument == QLatin1String("--hard"))
flags |= VcsBasePlugin::ExpectRepoChanges;
executeGit(workingDirectory, arguments, 0, true, flags);
void GitClient::addFile(const QString &workingDirectory, const QString &fileName)
{
QStringList arguments;
arguments << QLatin1String("add") << fileName;
executeGit(workingDirectory, arguments, 0);
bool GitClient::synchronousLog(const QString &workingDirectory, const QStringList &arguments,
QString *output, QString *errorMessageIn, unsigned flags)
{
QByteArray outputText;
QByteArray errorText;
QStringList allArguments;
allArguments << QLatin1String("log") << QLatin1String(noColorOption);
allArguments.append(arguments);
const bool rc = fullySynchronousGit(workingDirectory, allArguments, &outputText, &errorText,
flags);
if (QTextCodec *codec = encoding(workingDirectory, "i18n.logOutputEncoding"))
*output = codec->toUnicode(outputText);
else
*output = commandOutputFromLocal8Bit(outputText);
msgCannotRun(tr("Cannot obtain log of \"%1\": %2")
.arg(QDir::toNativeSeparators(workingDirectory),
commandOutputFromLocal8Bit(errorText)), errorMessageIn);
}
return rc;
}
bool GitClient::synchronousAdd(const QString &workingDirectory, const QStringList &files)
{
QByteArray outputText;
QByteArray errorText;
QStringList arguments;
arguments << QLatin1String("add") << files;
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText);
msgCannotRun(tr("Cannot add %n file(s) to \"%1\": %2", 0, files.size())
.arg(QDir::toNativeSeparators(workingDirectory),
commandOutputFromLocal8Bit(errorText)), 0);
bool GitClient::synchronousDelete(const QString &workingDirectory,
bool force,
const QStringList &files)
{
QByteArray outputText;
QByteArray errorText;
QStringList arguments;
arguments << QLatin1String("rm");
if (force)
arguments << QLatin1String("--force");
arguments.append(files);
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText);
if (!rc) {
msgCannotRun(tr("Cannot remove %n file(s) from \"%1\": %2", 0, files.size())
.arg(QDir::toNativeSeparators(workingDirectory),
commandOutputFromLocal8Bit(errorText)), 0);
}
return rc;
}
bool GitClient::synchronousMove(const QString &workingDirectory,
const QString &from,
const QString &to)
{
QByteArray outputText;
QByteArray errorText;
QStringList arguments;
arguments << QLatin1String("mv");
arguments << (from);
arguments << (to);
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText);
msgCannotRun(tr("Cannot move from \"%1\" to \"%2\": %3")
.arg(from, to, commandOutputFromLocal8Bit(errorText)), 0);
bool GitClient::synchronousReset(const QString &workingDirectory,
const QStringList &files,
QString *errorMessage)
{
QByteArray outputText;
QByteArray errorText;
QStringList arguments;
arguments << QLatin1String("reset");
arguments << QLatin1String("--hard");
arguments << QLatin1String(HEAD) << QLatin1String("--") << files;
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText);
const QString output = commandOutputFromLocal8Bit(outputText);
outputWindow()->append(output);
// Note that git exits with 1 even if the operation is successful
// Assume real failure if the output does not contain "foo.cpp modified"
// or "Unstaged changes after reset" (git 1.7.0).
if (!rc && (!output.contains(QLatin1String("modified"))
&& !output.contains(QLatin1String("Unstaged changes after reset")))) {
if (files.isEmpty()) {
msgCannotRun(arguments, workingDirectory, errorText, errorMessage);
} else {
msgCannotRun(tr("Cannot reset %n file(s) in \"%1\": %2", 0, files.size())
.arg(QDir::toNativeSeparators(workingDirectory),
commandOutputFromLocal8Bit(errorText)),
errorMessage);
}
return false;
}
return true;
}
// Initialize repository
bool GitClient::synchronousInit(const QString &workingDirectory)
{
QByteArray outputText;
QByteArray errorText;
const QStringList arguments(QLatin1String("init"));
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText);
// '[Re]Initialized...'
outputWindow()->append(commandOutputFromLocal8Bit(outputText));
outputWindow()->appendError(commandOutputFromLocal8Bit(errorText));
// TODO: Turn this into a VcsBaseClient and use resetCachedVcsInfo(...)
Core::VcsManager::resetVersionControlForDirectory(workingDirectory);
return rc;
}
/* Checkout, supports:
* git checkout -- <files>
* git checkout revision -- <files>
* git checkout revision -- . */
bool GitClient::synchronousCheckoutFiles(const QString &workingDirectory,
QStringList files /* = QStringList() */,
QString revision /* = QString() */,
QString *errorMessage /* = 0 */,
bool revertStaging /* = true */)
{
if (revertStaging && revision.isEmpty())
revision = QLatin1String(HEAD);
if (files.isEmpty())
files = QStringList(QString(QLatin1Char('.')));
QByteArray outputText;
QByteArray errorText;
QStringList arguments;
arguments << QLatin1String("checkout");
if (revertStaging)
arguments << revision;
arguments << QLatin1String("--") << files;
const bool rc = fullySynchronousGit(workingDirectory, arguments, &outputText, &errorText,
VcsBasePlugin::ExpectRepoChanges);
if (!rc) {
const QString fileArg = files.join(QLatin1String(", "));
//: Meaning of the arguments: %1: revision, %2: files, %3: repository,
//: %4: Error message
msgCannotRun(tr("Cannot checkout \"%1\" of %2 in \"%3\": %4")
.arg(revision, fileArg, workingDirectory,
commandOutputFromLocal8Bit(errorText)),
errorMessage);
return false;
}
return true;
}
bool GitClient::stashAndCheckout(const QString &workingDirectory, const QString &ref)
{
if (!beginStashScope(workingDirectory, QLatin1String("Checkout")))
return false;
if (!synchronousCheckout(workingDirectory, ref))
return false;
endStashScope(workingDirectory);
return true;
}
static inline QString msgParentRevisionFailed(const QString &workingDirectory,
const QString &revision,
const QString &why)
{
//: Failed to find parent revisions of a SHA1 for "annotate previous"
return GitClient::tr("Cannot find parent revisions of \"%1\" in \"%2\": %3").arg(revision, workingDirectory, why);
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
}
static inline QString msgInvalidRevision()
{
return GitClient::tr("Invalid revision");
}
// Split a line of "<commit> <parent1> ..." to obtain parents from "rev-list" or "log".
static inline bool splitCommitParents(const QString &line,
QString *commit = 0,
QStringList *parents = 0)
{
if (commit)
commit->clear();
if (parents)
parents->clear();
QStringList tokens = line.trimmed().split(QLatin1Char(' '));
if (tokens.size() < 2)
return false;
if (commit)
*commit = tokens.front();
tokens.pop_front();
if (parents)
*parents = tokens;
return true;
}
bool GitClient::synchronousRevListCmd(const QString &workingDirectory, const QStringList &arguments,
QString *output, QString *errorMessage)
{
QByteArray outputTextData;
QByteArray errorText;
QStringList args(QLatin1String("rev-list"));
args << QLatin1String(noColorOption) << arguments;
const bool rc = fullySynchronousGit(workingDirectory, args, &outputTextData, &errorText,
VcsBasePlugin::SuppressCommandLogging);
msgCannotRun(args, workingDirectory, errorText, errorMessage);
return false;
}
*output = commandOutputFromLocal8Bit(outputTextData);
return true;
}
// Find out the immediate parent revisions of a revision of the repository.
// Might be several in case of merges.
bool GitClient::synchronousParentRevisions(const QString &workingDirectory,
const QStringList &files /* = QStringList() */,
const QString &revision,
QStringList *parents,
QString *errorMessage)
{
QString outputText;
QString errorText;
QStringList arguments;
if (parents && !isValidRevision(revision)) { // Not Committed Yet
*parents = QStringList(QLatin1String(HEAD));
arguments << QLatin1String("--parents") << QLatin1String("--max-count=1") << revision;
if (!files.isEmpty()) {
arguments.append(QLatin1String("--"));
arguments.append(files);
}
if (!synchronousRevListCmd(workingDirectory, arguments, &outputText, &errorText)) {
*errorMessage = msgParentRevisionFailed(workingDirectory, revision, errorText);
return false;
}
// Should result in one line of blank-delimited revisions, specifying current first
// unless it is top.
outputText.remove(QLatin1Char('\n'));
if (!splitCommitParents(outputText, 0, parents)) {
*errorMessage = msgParentRevisionFailed(workingDirectory, revision, msgInvalidRevision());
return false;
}
return true;
}
// Short SHA1, author, subject
static const char defaultShortLogFormatC[] = "%h (%an \"%s";
static const int maxShortLogLength = 120;
QString GitClient::synchronousShortDescription(const QString &workingDirectory, const QString &revision)
{
// Short SHA 1, author, subject
QString output = synchronousShortDescription(workingDirectory, revision,
QLatin1String(defaultShortLogFormatC));
if (output != revision) {
if (output.length() > maxShortLogLength) {
output.truncate(maxShortLogLength);
output.append(QLatin1String("..."));
}
output.append(QLatin1String("\")"));
}
return output;
}
QString GitClient::synchronousCurrentLocalBranch(const QString &workingDirectory)
{
QByteArray outputTextData;
QStringList arguments;
arguments << QLatin1String("symbolic-ref") << QLatin1String(HEAD);
if (fullySynchronousGit(workingDirectory, arguments, &outputTextData, 0,
VcsBasePlugin::SuppressCommandLogging)) {
QString branch = commandOutputFromLocal8Bit(outputTextData.trimmed());
const QString refsHeadsPrefix = QLatin1String("refs/heads/");
if (branch.startsWith(refsHeadsPrefix)) {
branch.remove(0, refsHeadsPrefix.count());
return branch;
}
}
return QString();
}
bool GitClient::synchronousHeadRefs(const QString &workingDirectory, QStringList *output,
QString *errorMessage)
{
QStringList args;
args << QLatin1String("show-ref") << QLatin1String("--head")
<< QLatin1String("--abbrev=10") << QLatin1String("--dereference");
QByteArray outputText;
QByteArray errorText;
const bool rc = fullySynchronousGit(workingDirectory, args, &outputText, &errorText,
VcsBasePlugin::SuppressCommandLogging);
msgCannotRun(args, workingDirectory, errorText, errorMessage);