Newer
Older
{
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::DiffEditor *GitClient::findExistingDiffEditor(const char *registerDynamicProperty,
const QString &dynamicPropertyValue) const
DiffEditor::DiffEditor *diffEditor = qobject_cast<DiffEditor::DiffEditor *>(
locateEditor(registerDynamicProperty, dynamicPropertyValue));
if (diffEditor) {
diffEditor->document()->setContents(m_msgWait.toUtf8());
Core::EditorManager::activateEditor(diffEditor);
DiffEditor::DiffEditor *GitClient::createDiffEditor(const char *registerDynamicProperty,
const QString &dynamicPropertyValue,
const QString &source,
const QString &titlePattern,
const Core::Id editorId) const
{
QString title = titlePattern;
DiffEditor::DiffEditor *diffEditor = qobject_cast<DiffEditor::DiffEditor *>(
Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait.toUtf8()));
QTC_ASSERT(diffEditor, return 0);
diffEditor->document()->setProperty(registerDynamicProperty, dynamicPropertyValue);
Core::EditorManager::activateEditor(diffEditor);
return diffEditor;
}
/* 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());
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) {
QString encodingName = readConfigValue(source, QLatin1String("i18n.logOutputEncoding"));
if (encodingName.isEmpty())
encodingName = QLatin1String("utf-8");
rc->setCodec(QTextCodec::codecForName(encodingName.toLocal8Bit()));
}
rc->setForceReadOnly(true);
Core::EditorManager::activateEditor(outputEditor);
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 char *propertyName = "sideBySideOriginalFileName";
DiffEditor::DiffEditor *diffEditor = findExistingDiffEditor(propertyName, workingDirectory);
if (!diffEditor) {
newEditor = diffEditor = createDiffEditor(propertyName,
workingDirectory,
title,
DiffEditor::Constants::DIFF_EDITOR_ID);
}
GitDiffHandler *handler = new GitDiffHandler(diffEditor,
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);
newEditor = vcsEditor->editor();
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());
GitDiffSwitcher *switcher = new GitDiffSwitcher(newEditor, 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);
Core::IEditor *newEditor = 0;
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const char *propertyName = "sideBySideOriginalFileName";
DiffEditor::DiffEditor *diffEditor = findExistingDiffEditor(propertyName, sourceFile);
if (!diffEditor) {
newEditor = diffEditor = createDiffEditor(propertyName,
sourceFile,
title,
DiffEditor::Constants::DIFF_EDITOR_ID);
GitDiffHandler *handler = new GitDiffHandler(diffEditor,
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);
newEditor = vcsEditor->editor();
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());
GitDiffSwitcher *switcher = new GitDiffSwitcher(newEditor, 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 char *propertyName = "sideBySideBranchName";
DiffEditor::DiffEditor *diffEditor = findExistingDiffEditor(propertyName, branchName);
if (!diffEditor) {
newEditor = diffEditor = createDiffEditor(propertyName,
branchName,
title,
DiffEditor::Constants::DIFF_EDITOR_ID);
}
GitDiffHandler *handler = new GitDiffHandler(diffEditor,
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));
newEditor = vcsEditor->editor();
}
vcsEditor->setWorkingDirectory(workingDirectory);
cmdArgs << QLatin1String("diff")
<< QLatin1String(noColorOption)
executeGit(workingDirectory, cmdArgs, vcsEditor, false, diffExecutionFlags());
GitDiffSwitcher *switcher = new GitDiffSwitcher(newEditor, 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();
Core::IEditor *newEditor = 0;
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const char *propertyName = "sideBySideShow";
DiffEditor::DiffEditor *diffEditor = findExistingDiffEditor(propertyName, id);
if (!diffEditor) {
newEditor = diffEditor = createDiffEditor(propertyName,
id,
title,
DiffEditor::Constants::DIFF_SHOW_EDITOR_ID);
}
GitDiffHandler *handler = new GitDiffHandler(diffEditor,
gitBinaryPath(),
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));
newEditor = vcsEditor->editor();
}
QStringList arguments;
arguments << QLatin1String("show")
<< QLatin1String(noColorOption)
<< QLatin1String(decorateOption)
vcsEditor->setWorkingDirectory(workingDirectory);
executeGit(workingDirectory, arguments, vcsEditor);
}
if (newEditor) {
GitDiffSwitcher *switcher = new GitDiffSwitcher(newEditor, 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
{
if (QFileInfo(file).isFile())
return VcsBase::VcsBaseEditorWidget::getCodec(file);
QString encodingName = readConfigValue(file, QLatin1String("gui.encoding"));
if (encodingName.isEmpty())
encodingName = QLatin1String("utf-8");
return QTextCodec::codecForName(encodingName.toLocal8Bit());
}
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);
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
/* 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 local branch?"),
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)
{
QByteArray outputText;
QByteArray errorText;
QStringList allArguments;
allArguments << QLatin1String("log") << QLatin1String(noColorOption);
allArguments.append(arguments);
const bool rc = fullySynchronousGit(workingDirectory, allArguments, &outputText, &errorText);
if (rc) {
QString encodingName = readConfigValue(workingDirectory, QLatin1String("i18n.logOutputEncoding"));
if (encodingName.isEmpty())
encodingName = QLatin1String("utf-8");
QTextCodec *codec = QTextCodec::codecForName(encodingName.toLocal8Bit());
if (codec)
*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);
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
}
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);
if (!rc) {
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);
if (!rc) {
msgCannotRun(args, workingDirectory, errorText, errorMessage);
return false;
}
QByteArray headSha = outputText.left(10);
QByteArray newLine("\n");
int currentIndex = 15;
while (true) {
currentIndex = outputText.indexOf(headSha, currentIndex);
if (currentIndex < 0)
break;
currentIndex += 11;
output->append(QString::fromLocal8Bit(outputText.mid(currentIndex,
outputText.indexOf(newLine, currentIndex) - currentIndex)));
}
return true;
}
struct TopicData
{
QDateTime timeStamp;
QString topic;
};
// Retrieve topic (branch, tag or HEAD hash)
QString GitClient::synchronousTopic(const QString &workingDirectory)
static QHash<QString, TopicData> topicCache;
QString gitDir = findGitDirForRepository(workingDirectory);
if (gitDir.isEmpty())
return QString();
TopicData &data = topicCache[gitDir];
QDateTime lastModified = QFileInfo(gitDir + QLatin1String("/HEAD")).lastModified();
if (lastModified == data.timeStamp)
return data.topic;
data.timeStamp = lastModified;
// First try to find branch
QString branch = synchronousCurrentLocalBranch(workingDirectory);
if (!branch.isEmpty())