File indexing completed on 2025-01-19 04:22:45

0001 // Copyright (C) 2020 Hamed Masafi <hamed.masafi@gmail.com>
0002 // SPDX-License-Identifier: GPL-3.0-or-later
0003 
0004 #include "gitmanager.h"
0005 
0006 #include "models/authorsmodel.h"
0007 #include "models/branchesmodel.h"
0008 #include "models/logsmodel.h"
0009 #include "models/remotesmodel.h"
0010 #include "models/stashesmodel.h"
0011 #include "models/submodulesmodel.h"
0012 #include "models/tagsmodel.h"
0013 
0014 #include "libkommit_debug.h"
0015 #include <QFile>
0016 #include <QProcess>
0017 #include <QSortFilterProxyModel>
0018 #include <QtConcurrent>
0019 #include <QFuture>
0020 #include <QFutureWatcher>
0021 
0022 namespace Git
0023 {
0024 
0025 const QString &Manager::path() const
0026 {
0027     return mPath;
0028 }
0029 
0030 void Manager::setPath(const QString &newPath)
0031 {
0032     if (mPath == newPath)
0033         return;
0034 
0035     QProcess p;
0036     p.setProgram(QStringLiteral("git"));
0037     p.setArguments({QStringLiteral("rev-parse"), QStringLiteral("--show-toplevel")});
0038     p.setWorkingDirectory(newPath);
0039     p.start();
0040     p.waitForFinished();
0041     auto ret = p.readAllStandardOutput() + p.readAllStandardError();
0042 
0043     if (ret.contains("fatal")) {
0044         mPath = QString();
0045         mIsValid = false;
0046     } else {
0047         mPath = ret.replace("\n", "");
0048         mIsValid = true;
0049         loadAsync();
0050 
0051         setIsMerging(QFile::exists(mPath + QStringLiteral("/.git/MERGE_HEAD")));
0052         setIsRebasing(QFile::exists(mPath + QStringLiteral("/.git/REBASE_HEAD")));
0053     }
0054 
0055     Q_EMIT pathChanged();
0056 }
0057 
0058 QMap<QString, ChangeStatus> Manager::changedFiles(const QString &hash) const
0059 {
0060     QMap<QString, ChangeStatus> statuses;
0061     auto buffer = QString(runGit({QStringLiteral("show"), QStringLiteral("--name-status"), hash})).split(QLatin1Char('\n'));
0062 
0063     for (auto &line : buffer) {
0064         if (!line.trimmed().size())
0065             continue;
0066 
0067         const auto parts = line.split(QLatin1Char('\t'));
0068         if (parts.size() != 2)
0069             continue;
0070 
0071         const auto partFirst{parts.first()};
0072         if (partFirst == QLatin1Char('A'))
0073             statuses.insert(parts.at(1), ChangeStatus::Added);
0074         else if (partFirst == QLatin1Char('M'))
0075             statuses.insert(parts.at(1), ChangeStatus::Modified);
0076         else if (partFirst == QLatin1Char('D'))
0077             statuses.insert(parts.at(1), ChangeStatus::Removed);
0078         else
0079             qCDebug(KOMMITLIB_LOG) << "Unknown file status" << partFirst;
0080     }
0081     return statuses;
0082 }
0083 
0084 QStringList Manager::ignoredFiles() const
0085 {
0086     return readAllNonEmptyOutput({QStringLiteral("check-ignore"), QStringLiteral("*")});
0087 }
0088 
0089 QList<FileStatus> Manager::repoFilesStatus() const
0090 {
0091     const auto buffer = QString(runGit({QStringLiteral("status"),
0092                                         QStringLiteral("--untracked-files=all"),
0093                                         QStringLiteral("--ignored"),
0094                                         QStringLiteral("--short"),
0095                                         QStringLiteral("--ignore-submodules"),
0096                                         QStringLiteral("--porcelain")}))
0097             .split(QLatin1Char('\n'));
0098     QList<FileStatus> files;
0099     for (const auto &item : buffer) {
0100         if (!item.trimmed().size())
0101             continue;
0102         FileStatus fs;
0103         fs.parseStatusLine(item);
0104         qCDebug(KOMMITLIB_LOG) << "[STATUS]" << fs.name() << fs.status();
0105         fs.setFullPath(mPath + QLatin1Char('/') + fs.name());
0106         files.append(fs);
0107     }
0108     return files;
0109 }
0110 
0111 bool Manager::isValid() const
0112 {
0113     return mIsValid;
0114 }
0115 
0116 bool Manager::addRemote(const QString &name, const QString &url) const
0117 {
0118     runGit({QStringLiteral("remote"), QStringLiteral("add"), name, url});
0119     return true;
0120 }
0121 
0122 bool Manager::removeRemote(const QString &name) const
0123 {
0124     runGit({QStringLiteral("remote"), QStringLiteral("remove"), name});
0125     return true;
0126 }
0127 
0128 bool Manager::renameRemote(const QString &name, const QString &newName) const
0129 {
0130     runGit({QStringLiteral("remote"), QStringLiteral("rename"), name, newName});
0131     return true;
0132 }
0133 
0134 bool Manager::isIgnored(const QString &path)
0135 {
0136     auto tmp = readAllNonEmptyOutput({QStringLiteral("check-ignore"), path});
0137     qCDebug(KOMMITLIB_LOG) << Q_FUNC_INFO << tmp;
0138     return !tmp.empty();
0139 }
0140 
0141 QPair<int, int> Manager::uniqueCommitsOnBranches(const QString &branch1, const QString &branch2) const
0142 {
0143     if (branch1 == branch2)
0144         return qMakePair(0, 0);
0145 
0146     auto ret = readAllNonEmptyOutput(
0147                 {QStringLiteral("rev-list"), QStringLiteral("--left-right"), QStringLiteral("--count"), branch1 + QStringLiteral("...") + branch2});
0148 
0149     if (ret.size() != 1)
0150         return qMakePair(-1, -1);
0151 
0152     const auto parts = ret.first().split(QLatin1Char('\t'));
0153     if (parts.size() != 2)
0154         return qMakePair(-1, -1);
0155 
0156     return qMakePair(parts.at(0).toInt(), parts.at(1).toInt());
0157 }
0158 
0159 QStringList Manager::fileLog(const QString &fileName) const
0160 {
0161     return readAllNonEmptyOutput({QStringLiteral("log"), QStringLiteral("--format=format:%H"), QStringLiteral("--"), fileName});
0162 }
0163 
0164 QString Manager::diff(const QString &from, const QString &to) const
0165 {
0166     return runGit({QStringLiteral("diff"), from, to});
0167 }
0168 
0169 QList<FileStatus> Manager::diffBranch(const QString &from) const
0170 {
0171     auto buffer = QString(runGit({QStringLiteral("diff"), from, QStringLiteral("--name-status")})).split(QLatin1Char('\n'));
0172     QList<FileStatus> files;
0173     for (auto &item : buffer) {
0174         if (!item.trimmed().size())
0175             continue;
0176         auto parts = item.split(QStringLiteral("\t"));
0177         if (parts.size() != 2)
0178             continue;
0179 
0180         FileStatus fs;
0181         fs.setStatus(parts.at(0));
0182         fs.setName(parts.at(1));
0183         files.append(fs);
0184     }
0185     return files;
0186 }
0187 
0188 QList<FileStatus> Manager::diffBranches(const QString &from, const QString &to) const
0189 {
0190     const auto buffer = QString(runGit({QStringLiteral("diff"), from + QStringLiteral("..") + to, QStringLiteral("--name-status")})).split(QLatin1Char('\n'));
0191     QList<FileStatus> files;
0192     for (const auto &item : buffer) {
0193         if (!item.trimmed().size())
0194             continue;
0195         const auto parts = item.split(QLatin1Char('\t'));
0196         if (parts.size() != 2)
0197             continue;
0198 
0199         FileStatus fs;
0200         fs.setStatus(parts.at(0));
0201         fs.setName(parts.at(1));
0202         files.append(fs);
0203     }
0204     return files;
0205 }
0206 
0207 QString Manager::config(const QString &name, ConfigType type) const
0208 {
0209     QStringList cmd;
0210     switch (type) {
0211     case ConfigLocal:
0212         cmd = QStringList{QStringLiteral("config"), name};
0213         break;
0214     case ConfigGlobal:
0215         cmd = QStringList{QStringLiteral("config"), QStringLiteral("--global"), name};
0216         break;
0217     }
0218     const auto list = readAllNonEmptyOutput(cmd);
0219     if (!list.empty())
0220         return list.first();
0221 
0222     return {};
0223 }
0224 
0225 bool Manager::configBool(const QString &name, ConfigType type) const
0226 {
0227     const auto buffer = config(name, type);
0228     return buffer == QStringLiteral("true") || buffer == QStringLiteral("yes") || buffer == QStringLiteral("on");
0229 }
0230 
0231 void Manager::setConfig(const QString &name, const QString &value, ConfigType type) const
0232 {
0233     QStringList cmd;
0234     switch (type) {
0235     case ConfigLocal:
0236         cmd = QStringList{QStringLiteral("config"), name, value};
0237         break;
0238     case ConfigGlobal:
0239         cmd = QStringList{QStringLiteral("config"), QStringLiteral("--global"), name, value};
0240         break;
0241     }
0242 
0243     runGit(cmd);
0244 }
0245 
0246 void Manager::unsetConfig(const QString &name, ConfigType type) const
0247 {
0248     QStringList cmd{QStringLiteral("config"), QStringLiteral("--unset")};
0249 
0250     if (type == ConfigGlobal)
0251         cmd.append(QStringLiteral("--global"));
0252 
0253     cmd.append(name);
0254 
0255     runGit(cmd);
0256 }
0257 
0258 QStringList Manager::readAllNonEmptyOutput(const QStringList &cmd) const
0259 {
0260     QStringList list;
0261     const auto out = QString(runGit(cmd)).split(QLatin1Char('\n'));
0262 
0263     for (const auto &line : out) {
0264         const auto b = line.trimmed();
0265         if (b.isEmpty())
0266             continue;
0267 
0268         list.append(b.trimmed());
0269     }
0270     return list;
0271 }
0272 
0273 QString Manager::escapeFileName(const QString &filePath) const
0274 {
0275     if (filePath.contains(QLatin1Char(' ')))
0276         return QLatin1Char('\'') + filePath + QLatin1Char('\'');
0277     return filePath;
0278 }
0279 
0280 AbstractGitItemsModel * load(AbstractGitItemsModel *cache)
0281 {
0282     cache->load();
0283     return cache;
0284 }
0285 
0286 void Manager::loadAsync()
0287 {
0288     QList<AbstractGitItemsModel *> models;
0289     if (mAuthorsModel) {
0290         mAuthorsModel->clear();
0291     }
0292     if (mLoadFlags & LoadStashes)
0293         models << mStashesCache;
0294     if (mLoadFlags & LoadRemotes)
0295         models << mRemotesModel;
0296     if (mLoadFlags & LoadSubmodules)
0297         models << mSubmodulesModel;
0298     if (mLoadFlags & LoadBranches)
0299         models << mBranchesModel;
0300     if (mLoadFlags & LoadLogs)
0301         models << mLogsCache;
0302     if (mLoadFlags & LoadTags)
0303         models << mTagsModel;
0304 
0305     if (!models.empty()) {
0306 #ifdef QT_CONCURRENT_LIB
0307         qDebug() << "LOADING MODELS ASYNC";
0308         auto future = QtConcurrent::mapped(models, load);
0309 
0310         auto watcher = new QFutureWatcher<AbstractGitItemsModel *>;
0311 
0312         watcher->setFuture(future);
0313 
0314         connect(watcher, &QFutureWatcher<bool>::finished, [this]()
0315         {
0316             Q_EMIT modelsReady();
0317         });
0318 
0319         connect(watcher, &QFutureWatcher<AbstractGitItemsModel *>::resultReadyAt, [this, watcher](int index)
0320         {
0321             auto model = watcher->resultAt(index);
0322             model->reset();
0323         });
0324 #else
0325         for (auto &m : models)
0326             m->load();
0327 #endif
0328     }
0329 }
0330 
0331 TagsModel *Manager::tagsModel() const
0332 {
0333     return mTagsModel;
0334 }
0335 
0336 StashesModel *Manager::stashesModel() const
0337 {
0338     return mStashesCache;
0339 }
0340 
0341 LogsModel *Manager::logsModel() const
0342 {
0343     return mLogsCache;
0344 }
0345 
0346 BranchesModel *Manager::branchesModel() const
0347 {
0348     return mBranchesModel;
0349 }
0350 
0351 AuthorsModel *Manager::authorsModel() const
0352 {
0353     return mAuthorsModel;
0354 }
0355 
0356 SubmodulesModel *Manager::submodulesModel() const
0357 {
0358     return mSubmodulesModel;
0359 }
0360 
0361 RemotesModel *Manager::remotesModel() const
0362 {
0363     return mRemotesModel;
0364 }
0365 
0366 const LoadFlags &Manager::loadFlags() const
0367 {
0368     return mLoadFlags;
0369 }
0370 
0371 void Manager::setLoadFlags(Git::LoadFlags newLoadFlags)
0372 {
0373     mLoadFlags = newLoadFlags;
0374 }
0375 
0376 QString Manager::readNote(const QString &branchName) const
0377 {
0378     return runGit({QStringLiteral("notes"), QStringLiteral("show"), branchName});
0379 }
0380 
0381 void Manager::saveNote(const QString &branchName, const QString &note) const
0382 {
0383     runGit({QStringLiteral("notes"), QStringLiteral("add"), branchName, QStringLiteral("-f"), QStringLiteral("--message=") + note});
0384 }
0385 
0386 Manager::Manager()
0387     : QObject()
0388     , mRemotesModel{new RemotesModel(this)}
0389     , mAuthorsModel{new AuthorsModel(this)}
0390     , mSubmodulesModel{new SubmodulesModel(this)}
0391     , mBranchesModel{new BranchesModel(this)}
0392     , mLogsCache{new LogsModel(this, mAuthorsModel)}
0393     , mStashesCache{new StashesModel(this)}
0394     , mTagsModel{new TagsModel(this)}
0395 {
0396 }
0397 
0398 Manager::Manager(const QString &path)
0399     : Manager()
0400 {
0401     setPath(path);
0402 }
0403 
0404 Manager *Manager::instance()
0405 {
0406     static Manager instance;
0407     return &instance;
0408 }
0409 
0410 QString Manager::currentBranch() const
0411 {
0412     const auto ret = QString(runGit({QStringLiteral("rev-parse"), QStringLiteral("--abbrev-ref"), QStringLiteral("HEAD")}))
0413             .remove(QLatin1Char('\n'))
0414             .remove(QLatin1Char('\r'));
0415     return ret;
0416 }
0417 
0418 //git rev-parse --abbrev-ref --symbolic-full-name @{u}
0419 QString Manager::currentRemote() const
0420 {
0421     const auto ret = QString(runGit({QStringLiteral("rev-parse"), QStringLiteral("--abbrev-ref"), QStringLiteral("--symbolic-full-name"), QStringLiteral("@{u}")})+"/")
0422                          .remove(QLatin1Char('\n'))
0423                          .remove(QLatin1Char('\r'))
0424                          .split("/").first();
0425     return ret;
0426 }
0427 
0428 QString Manager::run(const AbstractCommand &cmd) const
0429 {
0430     return QString(runGit(cmd.generateArgs()));
0431 }
0432 
0433 void Manager::init(const QString &path)
0434 {
0435     mPath = path;
0436     runGit({QStringLiteral("init")});
0437 }
0438 
0439 QByteArray Manager::runGit(const QStringList &args) const
0440 {
0441     //    qCDebug(KOMMITLIB_LOG).noquote() << "Running: git " << args.join(" ");
0442 
0443     QProcess p;
0444     p.setProgram(QStringLiteral("git"));
0445     p.setArguments(args);
0446     p.setWorkingDirectory(mPath);
0447     p.start();
0448     p.waitForFinished();
0449     auto out = p.readAllStandardOutput();
0450     auto err = p.readAllStandardError();
0451 
0452     if (p.exitStatus() == QProcess::CrashExit) {
0453         qWarning() << "=== Crash on git process ===";
0454         qWarning() << "====\nERROR:\n====\n" << err;
0455         qWarning() << "====\nOUTPUR:\n====\n" << out;
0456     }
0457     return out; // + err;
0458 }
0459 
0460 QStringList Manager::ls(const QString &place) const
0461 {
0462     auto buffer = readAllNonEmptyOutput({QStringLiteral("ls-tree"), QStringLiteral("--name-only"), QStringLiteral("-r"), place});
0463     QMutableListIterator<QString> it(buffer);
0464     while (it.hasNext()) {
0465         auto s = it.next();
0466         if (s.startsWith(QLatin1String("\"")) && s.endsWith(QLatin1String("\"")))
0467             it.setValue(s.mid(1, s.length() - 2));
0468     }
0469     return buffer;
0470 }
0471 
0472 QString Manager::fileContent(const QString &place, const QString &fileName) const
0473 {
0474     return runGit({QStringLiteral("show"), place + QLatin1Char(':') + fileName});
0475 }
0476 
0477 void Manager::saveFile(const QString &place, const QString &fileName, const QString &localFile) const
0478 {
0479     auto buffer = runGit({QStringLiteral("show"), place + QLatin1Char(':') + fileName});
0480     QFile f{localFile};
0481     if (!f.open(QIODevice::WriteOnly))
0482         return;
0483     f.write(buffer);
0484     f.close();
0485 }
0486 
0487 QStringList Manager::branches() const
0488 {
0489     QStringList branchesList;
0490     auto out = QString(runGit({QStringLiteral("branch"), QStringLiteral("--list")})).split(QLatin1Char('\n'));
0491 
0492     for (auto &line : out) {
0493         auto b = line.trimmed();
0494         if (b.isEmpty())
0495             continue;
0496         if (b.startsWith(QLatin1String("* ")))
0497             b = b.mid(2);
0498 
0499         if (b.startsWith(QLatin1String("(HEAD detached at")))
0500             continue;
0501 
0502         branchesList.append(b.trimmed());
0503     }
0504     return branchesList;
0505 }
0506 
0507 QStringList Manager::remoteBranches() const
0508 {
0509     QStringList branchesList;
0510     const auto out = QString(runGit({QStringLiteral("branch"), QStringLiteral("--remote"), QStringLiteral("--list")})).split(QLatin1Char('\n'));
0511 
0512     for (const auto &line : out) {
0513         auto b = line.trimmed();
0514         if (b.isEmpty())
0515             continue;
0516         if (b.startsWith(QStringLiteral("* ")))
0517             b = b.mid(2);
0518 
0519         if (!b.contains(QStringLiteral("->")))
0520             branchesList.append(b.trimmed());
0521     }
0522     return branchesList;
0523 }
0524 
0525 QStringList Manager::remotes() const
0526 {
0527     return readAllNonEmptyOutput({QStringLiteral("remote")});
0528 }
0529 
0530 QStringList Manager::tags() const
0531 {
0532     return readAllNonEmptyOutput({QStringLiteral("tag"), QStringLiteral("--list")});
0533 }
0534 
0535 void Manager::createTag(const QString &name, const QString &message) const
0536 {
0537     runGit({QStringLiteral("tag"), QStringLiteral("-a"), name, QStringLiteral("--message"), message});
0538 }
0539 
0540 QList<Stash> Manager::stashes()
0541 {
0542     QList<Stash> ret;
0543     const auto list = readAllNonEmptyOutput({QStringLiteral("stash"), QStringLiteral("list"), QStringLiteral("--format=format:%s%m%an%m%ae%m%aD")});
0544     int id{0};
0545     for (const auto &item : std::as_const(list)) {
0546         const auto parts = item.split(QStringLiteral(">"));
0547         if (parts.size() != 4)
0548             continue;
0549 
0550         const auto subject = parts.first();
0551         Stash stash(this, QStringLiteral("stash@{%1}").arg(id));
0552 
0553         stash.mSubject = subject;
0554         stash.mAuthorName = parts.at(1);
0555         stash.mAuthorEmail = parts.at(2);
0556         stash.mPushTime = QDateTime::fromString(parts.at(3), Qt::RFC2822Date);
0557         qCDebug(KOMMITLIB_LOG) << item << subject << stash.mPushTime;
0558 
0559         ret.append(stash);
0560         id++;
0561     }
0562     return ret;
0563 }
0564 
0565 void Manager::createStash(const QString &name) const
0566 {
0567     QStringList args{QStringLiteral("stash"), QStringLiteral("push")};
0568 
0569     if (!name.isEmpty())
0570         args.append({QStringLiteral("--message"), name});
0571 
0572     const auto ret = runGit(args);
0573     qCDebug(KOMMITLIB_LOG) << ret;
0574 }
0575 
0576 bool Manager::removeStash(const QString &name) const
0577 {
0578     runGit({QStringLiteral("stash"), QStringLiteral("drop"), name});
0579     return true;
0580 }
0581 
0582 bool Manager::applyStash(const QString &name) const
0583 {
0584     runGit({QStringLiteral("stash"), QStringLiteral("apply"), name});
0585     return true;
0586 }
0587 
0588 Remote Manager::remoteDetails(const QString &remoteName)
0589 {
0590     if (mRemotes.contains(remoteName))
0591         return mRemotes.value(remoteName);
0592     Remote r;
0593     auto ret = QString(runGit({QStringLiteral("remote"), QStringLiteral("show"), remoteName}));
0594     r.parse(ret);
0595     mRemotes.insert(remoteName, r);
0596     return r;
0597 }
0598 
0599 bool Manager::removeBranch(const QString &branchName) const
0600 {
0601     auto ret = readAllNonEmptyOutput({QStringLiteral("branch"), QStringLiteral("-D"), branchName});
0602     return true;
0603 }
0604 
0605 BlameData Manager::blame(const File &file)
0606 {
0607     BlameData b;
0608     auto lines = readAllNonEmptyOutput({QStringLiteral("--no-pager"), QStringLiteral("blame"), QStringLiteral("-l"), file.fileName()});
0609     b.reserve(lines.size());
0610 
0611     for (auto &line : lines) {
0612         BlameDataRow row;
0613         row.commitHash = line.mid(0, 40);
0614 
0615         auto metaIndex = line.indexOf(QLatin1Char(')'));
0616         row.code = line.mid(metaIndex + 1);
0617 
0618         auto hash = row.commitHash;
0619         if (hash.startsWith(QLatin1Char('^')))
0620             hash = hash.remove(0, 1);
0621         row.log = mLogsCache->findLogByHash(hash, LogsModel::LogMatchType::BeginMatch);
0622 
0623         b.append(row);
0624     }
0625 
0626     return b;
0627 }
0628 
0629 void Manager::revertFile(const QString &filePath) const
0630 {
0631     runGit({QStringLiteral("checkout"), QStringLiteral("--"), filePath});
0632 }
0633 
0634 QMap<QString, ChangeStatus> Manager::changedFiles() const
0635 {
0636     // status --untracked-files=all --ignored --short --ignore-submodules --porcelain
0637     QMap<QString, ChangeStatus> statuses;
0638     const auto buffer = QString(runGit({QStringLiteral("status"), QStringLiteral("--short")})).split(QLatin1Char('\n'));
0639 
0640     for (const auto &line : buffer) {
0641         if (!line.trimmed().size())
0642             continue;
0643 
0644         auto status0 = line.mid(0, 1).trimmed();
0645         auto status1 = line.mid(1, 1).trimmed();
0646         const auto fileName = line.mid(3);
0647 
0648         if (status1 == QLatin1Char('M') || status0 == QLatin1Char('M'))
0649             statuses.insert(fileName, ChangeStatus::Modified);
0650         else if (status1 == QLatin1Char('A'))
0651             statuses.insert(fileName, ChangeStatus::Added);
0652         else if (status1 == QLatin1Char('D') || status0 == QLatin1Char('D'))
0653             statuses.insert(fileName, ChangeStatus::Removed);
0654         else if (status1 == QLatin1Char('R'))
0655             statuses.insert(fileName, ChangeStatus::Renamed);
0656         else if (status1 == QLatin1Char('C'))
0657             statuses.insert(fileName, ChangeStatus::Copied);
0658         else if (status1 == QLatin1Char('U'))
0659             statuses.insert(fileName, ChangeStatus::UpdatedButInmerged);
0660         else if (status1 == QLatin1Char('?'))
0661             statuses.insert(fileName, ChangeStatus::Untracked);
0662         else if (status1 == QLatin1Char('!'))
0663             statuses.insert(fileName, ChangeStatus::Ignored);
0664         else {
0665             qDebug() << __FUNCTION__ << "The status" << status1 << "is unknown";
0666             statuses.insert(fileName, ChangeStatus::Unknown);
0667         }
0668     }
0669     return statuses;
0670 }
0671 
0672 void Manager::commit(const QString &message) const
0673 {
0674     runGit({QStringLiteral("commit"), QStringLiteral("-m"), message});
0675 }
0676 
0677 void Manager::push() const
0678 {
0679     runGit({QStringLiteral("push"), QStringLiteral("origin"), QStringLiteral("master")});
0680 }
0681 
0682 void Manager::addFile(const QString &file) const
0683 {
0684     runGit({QStringLiteral("add"), file});
0685 }
0686 
0687 void Manager::removeFile(const QString &file, bool cached) const
0688 {
0689     QStringList args;
0690     args.append(QStringLiteral("rm"));
0691     if (cached)
0692         args.append(QStringLiteral("--cached"));
0693     args.append(file);
0694     runGit(args);
0695 }
0696 
0697 bool Manager::isMerging() const
0698 {
0699     return m_isMerging;
0700 }
0701 
0702 void Manager::setIsMerging(bool newIsMerging)
0703 {
0704     if (m_isMerging == newIsMerging)
0705         return;
0706     m_isMerging = newIsMerging;
0707     Q_EMIT isMergingChanged();
0708 }
0709 
0710 bool Manager::isRebasing() const
0711 {
0712     return m_isRebasing;
0713 }
0714 
0715 void Manager::setIsRebasing(bool newIsRebasing)
0716 {
0717     if (m_isRebasing == newIsRebasing)
0718         return;
0719     m_isRebasing = newIsRebasing;
0720     emit isRebasingChanged();
0721 }
0722 
0723 } // namespace Git