2022-02-08 23:38:29 +08:00
|
|
|
#include "gitmanager.h"
|
2022-02-09 14:58:39 +08:00
|
|
|
#include "../utils.h"
|
2022-02-14 00:13:00 +08:00
|
|
|
#include "../settings.h"
|
2022-02-09 14:58:39 +08:00
|
|
|
|
2022-02-15 00:01:50 +08:00
|
|
|
#include <QDir>
|
2022-02-09 14:58:39 +08:00
|
|
|
#include <QFileInfo>
|
2022-02-08 23:38:29 +08:00
|
|
|
|
2022-02-14 00:13:00 +08:00
|
|
|
GitManager::GitManager(QObject *parent) : QObject(parent)
|
2022-02-08 23:38:29 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-02-09 14:58:39 +08:00
|
|
|
void GitManager::createRepository(const QString &folder)
|
|
|
|
{
|
2022-02-14 00:13:00 +08:00
|
|
|
QString currentBranch;
|
|
|
|
if (hasRepository(folder,currentBranch))
|
2022-02-09 14:58:39 +08:00
|
|
|
throw GitError(tr("Folder \"%1\" already has a repository!"));
|
|
|
|
QStringList args;
|
|
|
|
args.append("init");
|
|
|
|
runGit(folder,args);
|
2022-02-15 00:01:50 +08:00
|
|
|
|
|
|
|
QStringList contents;
|
|
|
|
contents.append(".git");
|
|
|
|
contents.append("*.o");
|
|
|
|
contents.append("*.exe");
|
|
|
|
contents.append("*.");
|
|
|
|
|
|
|
|
QDir dir(folder);
|
|
|
|
stringsToFile(contents,dir.filePath(".gitignore"));
|
|
|
|
add(folder,".gitignore");
|
2022-02-09 14:58:39 +08:00
|
|
|
}
|
|
|
|
|
2022-02-14 00:13:00 +08:00
|
|
|
bool GitManager::hasRepository(const QString &folder, QString& currentBranch)
|
2022-02-09 14:58:39 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
QStringList args;
|
|
|
|
args.append("status");
|
2022-02-14 00:13:00 +08:00
|
|
|
args.append("-b");
|
|
|
|
args.append("-u");
|
|
|
|
args.append("no");
|
|
|
|
args.append("--ignored=no");
|
2022-02-09 14:58:39 +08:00
|
|
|
QString output = runGit(folder,args);
|
2022-02-14 00:13:00 +08:00
|
|
|
bool result = output.startsWith("On branch");
|
|
|
|
if (result) {
|
|
|
|
int pos = QString("On branch").length();
|
|
|
|
while (pos<output.length() && output[pos].isSpace())
|
|
|
|
pos++;
|
|
|
|
int endPos = pos;
|
|
|
|
while (endPos<output.length() && !output[endPos].isSpace())
|
|
|
|
endPos++;
|
|
|
|
currentBranch = output.mid(pos,endPos-pos);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-02-15 17:22:44 +08:00
|
|
|
QString GitManager::rootFolder(const QString &folder)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("rev-parse");
|
|
|
|
args.append("--show-toplevel");
|
|
|
|
return runGit(folder,args).trimmed();
|
|
|
|
}
|
|
|
|
|
2022-02-14 00:13:00 +08:00
|
|
|
bool GitManager::isFileInRepository(const QFileInfo& fileInfo)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("ls-files");
|
|
|
|
args.append(fileInfo.fileName());
|
|
|
|
QString output = runGit(fileInfo.absolutePath(),args);
|
|
|
|
return output.trimmed() == fileInfo.fileName();
|
|
|
|
}
|
|
|
|
|
2022-02-15 00:01:50 +08:00
|
|
|
bool GitManager::isFileStaged(const QFileInfo &fileInfo)
|
2022-02-14 00:13:00 +08:00
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("diff");
|
|
|
|
args.append("--staged");
|
|
|
|
args.append("--name-only");
|
|
|
|
args.append(fileInfo.fileName());
|
|
|
|
QString output = runGit(fileInfo.absolutePath(),args);
|
|
|
|
return output.trimmed() == fileInfo.fileName();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GitManager::isFileChanged(const QFileInfo &fileInfo)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("diff");
|
|
|
|
args.append("--name-only");
|
|
|
|
args.append(fileInfo.fileName());
|
|
|
|
QString output = runGit(fileInfo.absolutePath(),args);
|
|
|
|
return output.trimmed() == fileInfo.fileName();
|
2022-02-09 14:58:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void GitManager::add(const QString &folder, const QString &path)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("add");
|
|
|
|
args.append(path);
|
|
|
|
runGit(folder,args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GitManager::remove(const QString &folder, const QString &path)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("rm");
|
|
|
|
args.append(path);
|
|
|
|
runGit(folder,args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GitManager::rename(const QString &folder, const QString &oldName, const QString &newName)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("mv");
|
|
|
|
args.append(oldName);
|
|
|
|
args.append(newName);
|
|
|
|
runGit(folder,args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GitManager::restore(const QString &folder, const QString &path)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("restore");
|
2022-02-15 21:39:17 +08:00
|
|
|
if (path.isEmpty())
|
|
|
|
args.append(".");
|
|
|
|
else
|
|
|
|
args.append(path);
|
2022-02-09 14:58:39 +08:00
|
|
|
runGit(folder,args);
|
|
|
|
}
|
|
|
|
|
2022-02-21 23:35:28 +08:00
|
|
|
int GitManager::logCounts(const QString &folder, const QString &branch)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("rev-list");
|
|
|
|
args.append("--count");
|
|
|
|
if (branch.isEmpty())
|
|
|
|
args.append("HEAD");
|
|
|
|
else
|
|
|
|
args.append(branch);
|
|
|
|
QString s = runGit(folder,args).trimmed();
|
|
|
|
bool ok;
|
|
|
|
int result = s.toInt(&ok);
|
|
|
|
if (!ok)
|
|
|
|
result = 0;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<PGitCommitInfo> GitManager::log(const QString &folder, int start, int count, const QString &branch)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("log");
|
|
|
|
args.append("--skip");
|
2022-02-22 17:12:54 +08:00
|
|
|
args.append(QString("%1").arg(start));
|
2022-02-21 23:35:28 +08:00
|
|
|
args.append("-n");
|
|
|
|
args.append(QString("%1").arg(count));
|
|
|
|
args.append("--format=medium");
|
|
|
|
args.append("--date=iso-strict");
|
|
|
|
if (branch.isEmpty())
|
|
|
|
args.append("HEAD");
|
|
|
|
else
|
|
|
|
args.append(branch);
|
|
|
|
QString output = runGit(folder,args);
|
|
|
|
QStringList lines = textToLines(output);
|
|
|
|
QList<PGitCommitInfo> result;
|
|
|
|
int pos = 0;
|
|
|
|
PGitCommitInfo commitInfo;
|
|
|
|
while (pos<lines.length()) {
|
|
|
|
if (lines[pos].startsWith("commit ")) {
|
|
|
|
commitInfo = std::make_shared<GitCommitInfo>();
|
|
|
|
commitInfo->commitHash=lines[pos].mid(QString("commit ").length()).trimmed();
|
|
|
|
result.append(commitInfo);
|
|
|
|
} else if(!commitInfo) {
|
|
|
|
break;
|
|
|
|
} else if (lines[pos].startsWith("Author:")) {
|
|
|
|
commitInfo->author=lines[pos].mid(QString("Author:").length()).trimmed();
|
|
|
|
} else if (lines[pos].startsWith("Date:")) {
|
|
|
|
commitInfo->authorDate=QDateTime::fromString(lines[pos].mid(QString("Date:").length()).trimmed(),Qt::ISODate);
|
|
|
|
} else if (!lines[pos].trimmed().isEmpty()) {
|
|
|
|
if (commitInfo->title.isEmpty()) {
|
|
|
|
commitInfo->title = lines[pos].trimmed();
|
|
|
|
} else {
|
|
|
|
commitInfo->fullCommitMessage.append(lines[pos].trimmed()+"\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-02-09 14:58:39 +08:00
|
|
|
QStringList GitManager::listFiles(const QString &folder)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("ls-files");
|
|
|
|
return textToLines(runGit(folder,args));
|
|
|
|
}
|
|
|
|
|
2022-02-15 00:01:50 +08:00
|
|
|
QStringList GitManager::listStagedFiles(const QString &folder)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("diff");
|
|
|
|
args.append("--staged");
|
|
|
|
args.append("--name-only");
|
|
|
|
return textToLines(runGit(folder,args));
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList GitManager::listChangedFiles(const QString &folder)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("diff");
|
|
|
|
args.append("--name-only");
|
|
|
|
return textToLines(runGit(folder,args));
|
|
|
|
}
|
|
|
|
|
2022-02-21 11:37:29 +08:00
|
|
|
QStringList GitManager::listConflicts(const QString &folder)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("diff");
|
|
|
|
args.append("--name-only");
|
|
|
|
args.append("--diff-filter=U");
|
|
|
|
return textToLines(runGit(folder,args));
|
|
|
|
}
|
|
|
|
|
2022-02-19 20:38:08 +08:00
|
|
|
QStringList GitManager::listBranches(const QString &folder, int ¤t)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("branch");
|
|
|
|
args.append("-a");
|
|
|
|
args.append("-l");
|
|
|
|
QStringList temp = textToLines(runGit(folder,args));
|
|
|
|
current = -1;
|
|
|
|
for (int i=0;i<temp.length();i++) {
|
|
|
|
QString s = temp[i];
|
|
|
|
if (s.startsWith('*')) {
|
|
|
|
current = i;
|
|
|
|
temp[i] = s.mid(1).trimmed();
|
|
|
|
} else if (s.startsWith('+')) {
|
|
|
|
temp[i] = s.mid(1).trimmed();
|
2022-02-21 11:37:29 +08:00
|
|
|
} else {
|
|
|
|
temp[i] = s.trimmed();
|
2022-02-19 20:38:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return temp;
|
|
|
|
}
|
|
|
|
|
2022-02-20 13:26:15 +08:00
|
|
|
bool GitManager::switchToBranch(const QString &folder, const QString &branch,
|
|
|
|
bool create, bool force, bool merge, bool track,
|
|
|
|
bool noTrack, bool forceCreation, QString& output)
|
2022-02-19 20:38:08 +08:00
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("switch");
|
|
|
|
if (forceCreation)
|
|
|
|
args.append("-C");
|
|
|
|
else if (create)
|
|
|
|
args.append("-c");
|
|
|
|
if (merge)
|
|
|
|
args.append("-m");
|
|
|
|
if (force)
|
|
|
|
args.append("-f");
|
|
|
|
if (track)
|
|
|
|
args.append("--track");
|
|
|
|
else if (noTrack)
|
|
|
|
args.append("--no-track");
|
|
|
|
args.append(branch);
|
2022-02-20 13:26:15 +08:00
|
|
|
output = runGit(folder,args);
|
|
|
|
return !output.startsWith("error") && !output.startsWith("fatal");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GitManager::merge(const QString &folder, const QString &commit, bool squash,
|
|
|
|
bool fastForwardOnly, bool noFastForward, bool noCommit,
|
|
|
|
QString& output,
|
|
|
|
const QString& commitMessage)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("merge");
|
|
|
|
if (squash)
|
|
|
|
args.append("--squash");
|
|
|
|
if (fastForwardOnly)
|
|
|
|
args.append("--ff-only");
|
|
|
|
else if (noFastForward)
|
|
|
|
args.append("--no-ff");
|
|
|
|
if (noCommit)
|
|
|
|
args.append("--no-commit");
|
|
|
|
if (!commitMessage.isEmpty()
|
|
|
|
&& commitMessage != QObject::tr("<Auto Generated by Git>")){
|
|
|
|
args.append("-m");
|
|
|
|
args.append(commitMessage);
|
|
|
|
}
|
|
|
|
args.append(commit);
|
|
|
|
output = runGit(folder,args);
|
|
|
|
return !output.startsWith("error") && !output.startsWith("fatal");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GitManager::continueMerge(const QString &folder)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("merge");
|
|
|
|
args.append("--continue");
|
2022-02-19 20:38:08 +08:00
|
|
|
QString output = runGit(folder,args);
|
|
|
|
return !output.startsWith("error") && !output.startsWith("fatal");
|
2022-02-20 13:26:15 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void GitManager::abortMerge(const QString &folder)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("merge");
|
|
|
|
args.append("--abort");
|
|
|
|
runGit(folder,args);
|
2022-02-19 20:38:08 +08:00
|
|
|
}
|
|
|
|
|
2022-02-09 14:58:39 +08:00
|
|
|
void GitManager::clone(const QString &folder, const QString &url)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("clone");
|
|
|
|
args.append(url);
|
|
|
|
runGit(folder,args);
|
|
|
|
}
|
|
|
|
|
2022-02-15 21:39:17 +08:00
|
|
|
void GitManager::commit(const QString &folder, const QString &message, bool autoStage)
|
2022-02-09 14:58:39 +08:00
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("commit");
|
2022-02-15 21:39:17 +08:00
|
|
|
if (autoStage)
|
|
|
|
args.append("-a");
|
2022-02-09 14:58:39 +08:00
|
|
|
args.append("-m");
|
|
|
|
args.append(message);
|
|
|
|
runGit(folder,args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GitManager::revert(const QString &folder)
|
|
|
|
{
|
|
|
|
QStringList args;
|
|
|
|
args.append("revert");
|
|
|
|
runGit(folder,args);
|
|
|
|
}
|
|
|
|
|
2022-02-22 17:12:54 +08:00
|
|
|
bool GitManager::reset(const QString &folder, const QString &commit,
|
|
|
|
GitResetStrategy strategy,
|
|
|
|
QString& ouput)
|
2022-02-09 14:58:39 +08:00
|
|
|
{
|
|
|
|
//todo reset type
|
|
|
|
QStringList args;
|
|
|
|
args.append("reset");
|
|
|
|
switch(strategy) {
|
2022-02-10 12:03:56 +08:00
|
|
|
case GitResetStrategy::Soft:
|
2022-02-09 14:58:39 +08:00
|
|
|
args.append("--soft");
|
|
|
|
break;
|
2022-02-10 12:03:56 +08:00
|
|
|
case GitResetStrategy::Hard:
|
2022-02-09 14:58:39 +08:00
|
|
|
args.append("--hard");
|
|
|
|
break;
|
2022-02-10 12:03:56 +08:00
|
|
|
case GitResetStrategy::Mixed:
|
2022-02-09 14:58:39 +08:00
|
|
|
args.append("--mixed");
|
|
|
|
break;
|
2022-02-10 12:03:56 +08:00
|
|
|
case GitResetStrategy::Merge:
|
2022-02-09 14:58:39 +08:00
|
|
|
args.append("--merge");
|
|
|
|
break;
|
2022-02-10 12:03:56 +08:00
|
|
|
case GitResetStrategy::Keep:
|
2022-02-09 14:58:39 +08:00
|
|
|
args.append("--keep");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
args.append(commit);
|
2022-02-22 17:12:54 +08:00
|
|
|
QString output = runGit(folder,args);
|
|
|
|
return !output.startsWith("error") && !output.startsWith("fatal");
|
2022-02-09 14:58:39 +08:00
|
|
|
}
|
|
|
|
|
2022-02-15 00:01:50 +08:00
|
|
|
bool GitManager::isValid()
|
|
|
|
{
|
|
|
|
return pSettings->vcs().gitOk();
|
|
|
|
}
|
|
|
|
|
2022-02-09 14:58:39 +08:00
|
|
|
QString GitManager::runGit(const QString& workingFolder, const QStringList &args)
|
|
|
|
{
|
2022-02-15 00:01:50 +08:00
|
|
|
if (!isValid())
|
|
|
|
return "";
|
2022-02-14 00:13:00 +08:00
|
|
|
QFileInfo fileInfo(pSettings->vcs().gitPath());
|
2022-02-09 14:58:39 +08:00
|
|
|
if (!fileInfo.exists())
|
2022-02-14 00:13:00 +08:00
|
|
|
return "fatal: git doesn't exist";
|
2022-02-09 14:58:39 +08:00
|
|
|
emit gitCmdRunning(QString("Running in \"%1\": \n \"%2\" \"%3\"")
|
|
|
|
.arg(workingFolder,
|
2022-02-14 00:13:00 +08:00
|
|
|
pSettings->vcs().gitPath(),
|
2022-02-09 14:58:39 +08:00
|
|
|
args.join("\" \"")));
|
2022-02-16 16:27:09 +08:00
|
|
|
// qDebug()<<"---------";
|
|
|
|
// qDebug()<<args;
|
2022-02-09 14:58:39 +08:00
|
|
|
QString output = runAndGetOutput(
|
|
|
|
fileInfo.absoluteFilePath(),
|
|
|
|
workingFolder,
|
|
|
|
args);
|
2022-02-15 00:01:50 +08:00
|
|
|
output = escapeUTF8String(output.toUtf8());
|
2022-02-16 16:27:09 +08:00
|
|
|
// qDebug()<<output;
|
2022-02-09 14:58:39 +08:00
|
|
|
emit gitCmdFinished(output);
|
2022-02-14 00:13:00 +08:00
|
|
|
// if (output.startsWith("fatal:"))
|
|
|
|
// throw GitError(output);
|
2022-02-09 14:58:39 +08:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2022-02-15 00:01:50 +08:00
|
|
|
QString GitManager::escapeUTF8String(const QByteArray &rawString)
|
|
|
|
{
|
|
|
|
QByteArray stringValue;
|
|
|
|
int p = 0;
|
|
|
|
while (p<rawString.length()) {
|
|
|
|
char ch = rawString[p];
|
|
|
|
if (ch =='\\' && p+1 < rawString.length()) {
|
|
|
|
p++;
|
|
|
|
ch = rawString[p];
|
|
|
|
switch (ch) {
|
|
|
|
case '\'':
|
|
|
|
stringValue+=0x27;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
stringValue+=0x22;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
stringValue+=0x3f;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
stringValue+=0x5c;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
stringValue+=0x07;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
stringValue+=0x08;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
stringValue+=0x0c;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
stringValue+=0x0a;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
stringValue+=0x0d;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
stringValue+=0x09;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
stringValue+=0x0b;
|
|
|
|
p++;
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
{
|
|
|
|
int i=0;
|
|
|
|
for (i=0;i<3;i++) {
|
|
|
|
if (p+i>=rawString.length() ||
|
|
|
|
rawString[p+i]<'0' || rawString[p+i]>'7')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bool ok;
|
|
|
|
unsigned char ch = rawString.mid(p,i).toInt(&ok,8);
|
|
|
|
stringValue+=ch;
|
|
|
|
p+=i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ch!='\"')
|
|
|
|
stringValue+=ch;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QString::fromUtf8(stringValue);
|
|
|
|
}
|
|
|
|
|
2022-02-09 14:58:39 +08:00
|
|
|
GitError::GitError(const QString &reason):BaseError(reason)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|