diff options
author | Tobias Hunger <tobias.hunger@nokia.com> | 2011-10-19 15:49:13 +0000 |
---|---|---|
committer | Tobias Hunger <tobias.hunger@nokia.com> | 2011-10-31 11:49:39 +0100 |
commit | 494fbdb0d2d486ba43dd655e2fa126bfa881f44b (patch) | |
tree | d280bed55ad9293119d96e668f322b313c352d6b /src | |
parent | 366a9d0d0eb450d1af58bc2c2d54c5e05bff82ee (diff) | |
download | qt-creator-494fbdb0d2d486ba43dd655e2fa126bfa881f44b.tar.gz |
Git: Do the right thing when commiting
Do the right thing when commiting in git. This allows
staged files to be commited without additional changes, etc.
Change-Id: Ib04c91cf9c105c4a2bbe013926112d6d5d3bade6
Reviewed-by: Tobias Hunger <tobias.hunger@nokia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/git/commitdata.cpp | 211 | ||||
-rw-r--r-- | src/plugins/git/commitdata.h | 38 | ||||
-rw-r--r-- | src/plugins/git/gitclient.cpp | 169 | ||||
-rw-r--r-- | src/plugins/git/gitclient.h | 8 | ||||
-rw-r--r-- | src/plugins/git/gitplugin.cpp | 18 | ||||
-rw-r--r-- | src/plugins/git/gitplugin.h | 2 | ||||
-rw-r--r-- | src/plugins/git/gitsubmiteditor.cpp | 46 | ||||
-rw-r--r-- | src/plugins/vcsbase/submitfilemodel.cpp | 50 | ||||
-rw-r--r-- | src/plugins/vcsbase/submitfilemodel.h | 5 |
9 files changed, 272 insertions, 275 deletions
diff --git a/src/plugins/git/commitdata.cpp b/src/plugins/git/commitdata.cpp index 41e7c2aa89..aa9845be85 100644 --- a/src/plugins/git/commitdata.cpp +++ b/src/plugins/git/commitdata.cpp @@ -33,11 +33,10 @@ #include "commitdata.h" #include <utils/qtcassert.h> +#include <QtCore/QCoreApplication> #include <QtCore/QDebug> #include <QtCore/QRegExp> -const char *const kBranchIndicatorC = "# On branch"; - namespace Git { namespace Internal { @@ -87,151 +86,121 @@ void CommitData::clear() panelData.clear(); amendSHA1.clear(); - stagedFiles.clear(); - unstagedFiles.clear(); - untrackedFiles.clear(); + files.clear(); } -// Split a state/file spec from git status output -// '#<tab>modified:<blanks>git .pro' -// into state and file ('modified', 'git .pro'). -CommitData::StateFilePair splitStateFileSpecification(const QString &line) +static CommitData::FileState stateFor(const QChar &c) { - QPair<QString, QString> rc; - const int statePos = 2; - const int colonIndex = line.indexOf(QLatin1Char(':'), statePos); - if (colonIndex == -1) - return rc; - rc.first = line.mid(statePos, colonIndex - statePos); - int filePos = colonIndex + 1; - const QChar blank = QLatin1Char(' '); - while (line.at(filePos) == blank) - filePos++; - if (filePos < line.size()) - rc.second = line.mid(filePos, line.size() - filePos); - return rc; + switch (c.unicode()) { + case ' ': + return CommitData::UntrackedFile; + case 'M': + return CommitData::ModifiedFile; + case 'A': + return CommitData::AddedFile; + case 'D': + return CommitData::DeletedFile; + case 'R': + return CommitData::RenamedFile; + case 'C': + return CommitData::CopiedFile; + case 'U': + return CommitData::UpdatedFile; + default: + return CommitData::UnknownFileState; + } } -// Convenience to add a state/file spec to a list -static inline bool addStateFileSpecification(const QString &line, QList<CommitData::StateFilePair> *list) +static bool checkLine(const QString &stateInfo, const QString &file, QList<CommitData::StateFilePair> *files) { - const CommitData::StateFilePair sf = splitStateFileSpecification(line); - if (sf.first.isEmpty() || sf.second.isEmpty()) + Q_ASSERT(stateInfo.count() == 2); + Q_ASSERT(files); + + if (stateInfo == "??") { + files->append(qMakePair(CommitData::UntrackedFile, file)); + return true; + } + + CommitData::FileState stagedState = stateFor(stateInfo.at(0)); + if (stagedState == CommitData::UnknownFileState) + return false; + + stagedState = static_cast<CommitData::FileState>(stagedState | CommitData::StagedFile); + if (stagedState != CommitData::StagedFile) + files->append(qMakePair(stagedState, file)); + + CommitData::FileState state = stateFor(stateInfo.at(1)); + if (state == CommitData::UnknownFileState) return false; - list->push_back(sf); + + if (state != CommitData::UntrackedFile) { + QString newFile = file; + if (stagedState == CommitData::RenamedStagedFile || stagedState == CommitData::CopiedStagedFile) + newFile = file.mid(file.indexOf(QLatin1String(" -> ")) + 4); + + files->append(qMakePair(state, newFile)); + } + return true; } /* Parse a git status file list: * \code - # Changes to be committed: - #<tab>modified:<blanks>git.pro - # Changed but not updated: - #<tab>modified:<blanks>git.pro - # Untracked files: - #<tab>git.pro - \endcode -*/ - -bool CommitData::filesEmpty() const -{ - return stagedFiles.empty() && unstagedFiles.empty() && untrackedFiles.empty(); -} - + ## branch_name + XY file + \endcode */ bool CommitData::parseFilesFromStatus(const QString &output) { - enum State { None, CommitFiles, NotUpdatedFiles, UntrackedFiles }; - const QStringList lines = output.split(QLatin1Char('\n')); - const QString branchIndicator = QLatin1String(kBranchIndicatorC); - const QString commitIndicator = QLatin1String("# Changes to be committed:"); - const QString notUpdatedIndicator = QLatin1String("# Changed but not updated:"); - const QString notUpdatedIndicatorGit174 = QLatin1String("# Changes not staged for commit:"); - const QString untrackedIndicator = QLatin1String("# Untracked files:"); - - State s = None; - // Match added/changed-not-updated files: "#<tab>modified: foo.cpp" - QRegExp filesPattern(QLatin1String("#\\t[^:]+:\\s+.+")); - QTC_ASSERT(filesPattern.isValid(), return false); - - const QStringList::const_iterator cend = lines.constEnd(); - for (QStringList::const_iterator it = lines.constBegin(); it != cend; ++it) { - QString line = *it; - if (line.startsWith(branchIndicator)) { - panelInfo.branch = line.mid(branchIndicator.size() + 1); - continue; - } - if (line.startsWith(commitIndicator)) { - s = CommitFiles; - continue; - } - if (line.startsWith(notUpdatedIndicator) || line.startsWith(notUpdatedIndicatorGit174)) { - s = NotUpdatedFiles; + + foreach (const QString &line, lines) { + if (line.isEmpty()) continue; - } - if (line.startsWith(untrackedIndicator)) { - // Now match untracked: "#<tab>foo.cpp" - s = UntrackedFiles; - filesPattern = QRegExp(QLatin1String("#\\t.+")); - QTC_ASSERT(filesPattern.isValid(), return false); + + if (line.startsWith("## ")) { + // Branch indication: + panelInfo.branch = line.mid(3); continue; } - if (filesPattern.exactMatch(line)) { - switch (s) { - case CommitFiles: - addStateFileSpecification(line, &stagedFiles); - break; - case NotUpdatedFiles: - // skip submodules: - if (line.endsWith(QLatin1String(" (modified content)")) - || line.endsWith(" (new commits)")) - line = line.left(line.lastIndexOf(QLatin1Char('(')) - 1); - addStateFileSpecification(line, &unstagedFiles); - break; - case UntrackedFiles: - untrackedFiles.push_back(line.mid(2).trimmed()); - break; - case None: - break; - } - } + QTC_ASSERT(line.at(2) == ' ', continue); + if (!checkLine(line.mid(0, 2), line.mid(3), &files)) + return false; } - return true; -} - -// Convert a spec pair list to a list of file names, optionally -// filter for a state -static QStringList specToFileNames(const QList<CommitData::StateFilePair> &files, - const QString &stateFilter) -{ - typedef QList<CommitData::StateFilePair>::const_iterator ConstIterator; - if (files.empty()) - return QStringList(); - const bool emptyFilter = stateFilter.isEmpty(); - QStringList rc; - const ConstIterator cend = files.constEnd(); - for (ConstIterator it = files.constBegin(); it != cend; ++it) - if (emptyFilter || stateFilter == it->first) - rc.push_back(it->second); - return rc; -} -QStringList CommitData::stagedFileNames(const QString &stateFilter) const -{ - return specToFileNames(stagedFiles, stateFilter); + return true; } -QStringList CommitData::unstagedFileNames(const QString &stateFilter) const +QStringList CommitData::filterFiles(const CommitData::FileState &state) const { - return specToFileNames(unstagedFiles, stateFilter); + QStringList result; + foreach (const StateFilePair &p, files) { + if (state == AllStates || state == p.first) + result.append(p.second); + } + return result; } -QDebug operator<<(QDebug d, const CommitData &data) +QString CommitData::stateDisplayName(const FileState &state) { - d << data.panelInfo << data.panelData; - d.nospace() << "Commit: " << data.stagedFiles << " Not updated: " - << data.unstagedFiles << " Untracked: " << data.untrackedFiles; - return d; + QString resultState; + if (state == UntrackedFile) + return QCoreApplication::translate("Git::Internal::CommitData", "untracked"); + + if (state & StagedFile) + resultState = QCoreApplication::translate("Git::Internal::CommitData", "staged + "); + if (state & ModifiedFile) + resultState.append(QCoreApplication::translate("Git::Internal::CommitData", "modified")); + else if (state & AddedFile) + resultState.append(QCoreApplication::translate("Git::Internal::CommitData", "added")); + else if (state & DeletedFile) + resultState.append(QCoreApplication::translate("Git::Internal::CommitData", "deleted")); + else if (state & RenamedFile) + resultState.append(QCoreApplication::translate("Git::Internal::CommitData", "renamed")); + else if (state & CopiedFile) + resultState.append(QCoreApplication::translate("Git::Internal::CommitData", "copied")); + else if (state & UpdatedFile) + resultState.append(QCoreApplication::translate("Git::Internal::CommitData", "updated")); + return resultState; } } // namespace Internal diff --git a/src/plugins/git/commitdata.h b/src/plugins/git/commitdata.h index b31bf73c2f..e76f1e1b52 100644 --- a/src/plugins/git/commitdata.h +++ b/src/plugins/git/commitdata.h @@ -69,34 +69,50 @@ QDebug operator<<(QDebug d, const GitSubmitEditorPanelData &); class CommitData { public: + enum FileState { + UntrackedFile = 0, + + StagedFile = 1, + ModifiedFile = 2, + AddedFile = 3, + DeletedFile = 4, + RenamedFile = 8, + CopiedFile = 16, + UpdatedFile = 32, + + ModifiedStagedFile = StagedFile | ModifiedFile, + AddedStagedFile = StagedFile | AddedFile, + DeletedStagedFile = StagedFile | DeletedFile, + RenamedStagedFile = StagedFile | RenamedFile, + CopiedStagedFile = StagedFile | CopiedFile, + UpdatedStagedFile = StagedFile | UpdatedFile, + + AllStates = UpdatedFile | CopiedFile | RenamedFile | DeletedFile | AddedFile | ModifiedFile | StagedFile, + UnknownFileState + }; + // A pair of state string/file name ('modified', 'file.cpp'). - typedef QPair<QString, QString> StateFilePair; + typedef QPair<FileState, QString> StateFilePair; void clear(); // Parse the files and the branch of panelInfo // from a git status output bool parseFilesFromStatus(const QString &output); - bool filesEmpty() const; - // Convenience to retrieve the file names from // the specification list. Optionally filter for a certain state - QStringList stagedFileNames(const QString &stateFilter = QString()) const; - QStringList unstagedFileNames(const QString &stateFilter = QString()) const; + QStringList filterFiles(const FileState &state = AllStates) const; + + static QString stateDisplayName(const FileState &state); QString amendSHA1; QString commitEncoding; GitSubmitEditorPanelInfo panelInfo; GitSubmitEditorPanelData panelData; - QList<StateFilePair> stagedFiles; - QList<StateFilePair> unstagedFiles; - QStringList untrackedFiles; + QList<StateFilePair> files; }; -QDebug operator<<(QDebug d, const CommitData &); - - } // namespace Internal } // namespace Git diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index c90fc6dce5..687fce8abe 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -39,6 +39,8 @@ #include "gitsubmiteditor.h" #include "gitversioncontrol.h" +#include <vcsbase/submitfilemodel.h> + #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> @@ -75,8 +77,7 @@ #include <QtGui/QToolButton> #include <QtCore/QTextCodec> -static const char kGitDirectoryC[] = ".git"; -static const char kBranchIndicatorC[] = "# On branch"; +static const char GIT_DIRECTORY[] = ".git"; namespace Git { namespace Internal { @@ -323,7 +324,7 @@ const char *GitClient::decorateOption = "--decorate"; QString GitClient::findRepositoryForDirectory(const QString &dir) { // Check for ".git/config" - const QString checkFile = QLatin1String(kGitDirectoryC) + QLatin1String("/config"); + const QString checkFile = QLatin1String(GIT_DIRECTORY) + QLatin1String("/config"); return VCSBase::VCSBasePlugin::findRepositoryForDirectory(dir, checkFile); } @@ -1415,28 +1416,29 @@ static inline QString trimFileSpecification(QString fileSpec) return fileSpec; } -GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory, - bool untracked, - QString *output, - QString *errorMessage, - bool *onBranch) +GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory, bool untracked, + QString *output, QString *errorMessage, bool *onBranch) { // Run 'status'. Note that git returns exitcode 1 if there are no added files. QByteArray outputText; QByteArray errorText; - // @TODO: Use "--no-color" once it is supported + QStringList statusArgs(QLatin1String("status")); if (untracked) statusArgs << QLatin1String("-u"); + statusArgs << QLatin1String("-s") << QLatin1String("-b"); + const bool statusRc = fullySynchronousGit(workingDirectory, statusArgs, &outputText, &errorText); - VCSBase::Command::removeColorCodes(&outputText); if (output) *output = commandOutputFromLocal8Bit(outputText); - const bool branchKnown = outputText.contains(kBranchIndicatorC); + + static const char * NO_BRANCH = "## HEAD (no branch)\n"; + + const bool branchKnown = !outputText.startsWith(NO_BRANCH); if (onBranch) *onBranch = branchKnown; // Is it something really fatal? - if (!statusRc && !branchKnown && !outputText.contains("# Not currently on any branch.")) { + if (!statusRc && !branchKnown) { if (errorMessage) { const QString error = commandOutputFromLocal8Bit(errorText); *errorMessage = tr("Cannot obtain status: %1").arg(error); @@ -1444,10 +1446,8 @@ GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory, return StatusFailed; } // Unchanged (output text depending on whether -u was passed) - if (outputText.contains("nothing to commit")) + if (outputText.count('\n') == 1) return StatusUnchanged; - if (outputText.contains("nothing added to commit but untracked files present")) - return untracked ? StatusChanged : StatusUnchanged; return StatusChanged; } @@ -1570,7 +1570,7 @@ bool GitClient::getCommitData(const QString &workingDirectory, commitData->panelInfo.repository = repoDirectory; QDir gitDir(repoDirectory); - if (!gitDir.cd(QLatin1String(kGitDirectoryC))) { + if (!gitDir.cd(QLatin1String(GIT_DIRECTORY))) { *errorMessage = tr("The repository \"%1\" is not initialized.").arg(repoDirectory); return false; } @@ -1605,33 +1605,31 @@ bool GitClient::getCommitData(const QString &workingDirectory, } // Output looks like: - // # On branch [branchname] - // # Changes to be committed: - // # (use "git reset HEAD <file>..." to unstage) - // # - // # modified: somefile.cpp - // # new File: somenew.h - // # - // # Changed but not updated: - // # (use "git add <file>..." to update what will be committed) - // # - // # modified: someother.cpp - // # modified: submodule (modified content) - // # modified: submodule2 (new commit) - // # - // # Untracked files: - // # (use "git add <file>..." to include in what will be committed) - // # - // # list of files... - + // ## branch_name + // MM filename + // A new_unstaged_file + // R old -> new + // ?? missing_file if (status != StatusUnchanged) { if (!commitData->parseFilesFromStatus(output)) { *errorMessage = msgParseFilesFailed(); return false; } + // Filter out untracked files that are not part of the project - VCSBase::VCSBaseSubmitEditor::filterUntrackedFilesOfProject(repoDirectory, &commitData->untrackedFiles); - if (commitData->filesEmpty()) { + QStringList untrackedFiles = commitData->filterFiles(CommitData::UntrackedFile); + + VCSBase::VCSBaseSubmitEditor::filterUntrackedFilesOfProject(repoDirectory, &untrackedFiles); + QList<CommitData::StateFilePair> filteredFiles; + QList<CommitData::StateFilePair>::const_iterator it = commitData->files.constBegin(); + for ( ; it != commitData->files.constEnd(); ++it) { + if (it->first == CommitData::UntrackedFile && !untrackedFiles.contains(it->second)) + continue; + filteredFiles.append(*it); + } + commitData->files = filteredFiles; + + if (commitData->files.isEmpty()) { *errorMessage = msgNoChangedFiles(); return false; } @@ -1683,46 +1681,74 @@ static inline QString msgCommitted(const QString &amendSHA1, int fileCount) return GitClient::tr("Amended \"%1\".").arg(amendSHA1); } -// addAndCommit: bool GitClient::addAndCommit(const QString &repositoryDirectory, const GitSubmitEditorPanelData &data, const QString &amendSHA1, const QString &messageFile, - const QStringList &checkedFiles, - const QStringList &origCommitFiles, - const QStringList &origDeletedFiles) + VCSBase::SubmitFileModel *model) { - const QString renamedSeparator = QLatin1String(" -> "); + const QString renameSeparator = QLatin1String(" -> "); const bool amend = !amendSHA1.isEmpty(); - // Do we need to reset any files that had been added before - // (did the user uncheck any previously added files) - // Split up renamed files ('foo.cpp -> foo2.cpp'). - QStringList resetFiles = origCommitFiles.toSet().subtract(checkedFiles.toSet()).toList(); - for (QStringList::iterator it = resetFiles.begin(); it != resetFiles.end(); ++it) { - const int renamedPos = it->indexOf(renamedSeparator); - if (renamedPos != -1) { - const QString newFile = it->mid(renamedPos + renamedSeparator.size()); - it->truncate(renamedPos); - it = resetFiles.insert(++it, newFile); + QStringList filesToAdd; + QStringList filesToRemove; + QStringList filesToReset; + + int commitCount = 0; + + for (int i = 0; i < model->rowCount(); ++i) { + const CommitData::FileState state = static_cast<CommitData::FileState>(model->data(i).toInt()); + QString file = model->file(i); + const bool checked = model->checked(i); + + if (checked) + ++commitCount; + + if (state == CommitData::UntrackedFile && checked) + filesToAdd.append(file); + + if (state == CommitData::ModifiedStagedFile && !checked) { + filesToReset.append(file); + } else if (state == CommitData::AddedStagedFile && !checked) { + filesToReset.append(file); + } else if (state == CommitData::DeletedStagedFile && !checked) { + filesToReset.append(file); + } else if (state == CommitData::RenamedStagedFile && !checked) { + const int pos = file.indexOf(QLatin1String(" -> ")); + const QString newFile = file.mid(pos + 4); + filesToReset.append(newFile); + } else if (state == CommitData::CopiedStagedFile && !checked) { + const QString newFile = file.mid(file.indexOf(renameSeparator) + renameSeparator.count()); + filesToReset.append(newFile); + } else if (state == CommitData::UpdatedStagedFile && !checked) { + QTC_ASSERT(false, continue); // There should not be updated files when commiting! + } + + if (state == CommitData::ModifiedFile && checked) { + filesToReset.removeAll(file); + filesToAdd.append(file); + } else if (state == CommitData::AddedFile && checked) { + QTC_ASSERT(false, continue); // these should be untracked! + } else if (state == CommitData::DeletedFile && checked) { + filesToReset.removeAll(file); + filesToRemove.append(file); + } else if (state == CommitData::RenamedFile && checked) { + QTC_ASSERT(false, continue); // git mv directly stages. + } else if (state == CommitData::CopiedFile && checked) { + QTC_ASSERT(false, continue); // only is noticed after adding a new file to the index + } else if (state == CommitData::UpdatedFile && checked) { + QTC_ASSERT(false, continue); // There should not be updated files when commiting! } } - if (!resetFiles.isEmpty()) - if (!synchronousReset(repositoryDirectory, resetFiles)) - return false; + if (!filesToReset.isEmpty() && !synchronousReset(repositoryDirectory, filesToReset)) + return false; - // Re-add all to make sure we have the latest changes, but only add those that aren't marked - // for deletion. Purge out renamed files ('foo.cpp -> foo2.cpp'). - QStringList addFiles = checkedFiles.toSet().subtract(origDeletedFiles.toSet()).toList(); - for (QStringList::iterator it = addFiles.begin(); it != addFiles.end(); ) { - if (it->contains(renamedSeparator)) - it = addFiles.erase(it); - else - ++it; - } - if (!addFiles.isEmpty() && !synchronousAdd(repositoryDirectory, false, addFiles)) - return false; + if (!filesToRemove.isEmpty() && !synchronousDelete(repositoryDirectory, true, filesToRemove)) + return false; + + if (!filesToAdd.isEmpty() && !synchronousAdd(repositoryDirectory, false, filesToAdd)) + return false; // Do the final commit QStringList args; @@ -1736,11 +1762,13 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory, QByteArray outputText; QByteArray errorText; + const bool rc = fullySynchronousGit(repositoryDirectory, args, &outputText, &errorText); if (rc) - outputWindow()->append(msgCommitted(amendSHA1, checkedFiles.size())); + outputWindow()->append(msgCommitted(amendSHA1, commitCount)); else - outputWindow()->appendError(tr("Cannot commit %n file(s): %1\n", 0, checkedFiles.size()).arg(commandOutputFromLocal8Bit(errorText))); + outputWindow()->appendError(tr("Cannot commit %n file(s): %1\n", 0, commitCount).arg(commandOutputFromLocal8Bit(errorText))); + return rc; } @@ -1796,9 +1824,8 @@ GitClient::RevertResult GitClient::revertI(QStringList files, } // From the status output, determine all modified [un]staged files. - const QString modifiedState = QLatin1String("modified"); - const QStringList allStagedFiles = data.stagedFileNames(modifiedState); - const QStringList allUnstagedFiles = data.unstagedFileNames(modifiedState); + const QStringList allStagedFiles = data.filterFiles(CommitData::ModifiedStagedFile); + const QStringList allUnstagedFiles = data.filterFiles(CommitData::ModifiedFile); // Unless a directory was passed, filter all modified files for the // argument file list. QStringList stagedFiles = allStagedFiles; diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index f4520e215a..fff6aa6b0b 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -57,6 +57,7 @@ namespace Core { namespace VCSBase { class VCSBaseEditorWidget; + class SubmitFileModel; } namespace Utils { @@ -204,16 +205,13 @@ public: const GitSubmitEditorPanelData &data, const QString &amendSHA1, const QString &messageFile, - const QStringList &checkedFiles, - const QStringList &origCommitFiles, - const QStringList &origDeletedFiles); + VCSBase::SubmitFileModel *model); enum StatusResult { StatusChanged, StatusUnchanged, StatusFailed }; StatusResult gitStatus(const QString &workingDirectory, bool untracked = false, QString *output = 0, - QString *errorMessage = 0, - bool *onBranch = 0); + QString *errorMessage = 0, bool *onBranch = 0); void launchGitK(const QString &workingDirectory); QStringList synchronousRepositoryBranches(const QString &repositoryURL); diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index b98dfe6ea8..7ff41b4b98 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -63,6 +63,7 @@ #include <utils/fileutils.h> #include <vcsbase/basevcseditorfactory.h> +#include <vcsbase/submitfilemodel.h> #include <vcsbase/vcsbaseeditor.h> #include <vcsbase/basevcssubmiteditorfactory.h> #include <vcsbase/vcsbaseoutputwindow.h> @@ -695,8 +696,6 @@ void GitPlugin::startCommit(bool amend) // files to be able to unstage files the user unchecks m_submitRepository = data.panelInfo.repository; m_commitAmendSHA1 = data.amendSHA1; - m_submitOrigCommitFiles = data.stagedFileNames(); - m_submitOrigDeleteFiles = data.stagedFileNames("deleted"); // Start new temp file with message template Utils::TempFileSaver saver; @@ -772,21 +771,18 @@ bool GitPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEdi default: break; } + + // Go ahead! - const QStringList fileList = editor->checkedFiles(); + VCSBase::SubmitFileModel *model = qobject_cast<VCSBase::SubmitFileModel *>(editor->fileModel()); bool closeEditor = true; - if (!fileList.empty() || !m_commitAmendSHA1.isEmpty()) { + if (model->hasCheckedFiles() || !m_commitAmendSHA1.isEmpty()) { // get message & commit if (!m_core->fileManager()->saveFile(fileIFace)) return false; - closeEditor = m_gitClient->addAndCommit(m_submitRepository, - editor->panelData(), - m_commitAmendSHA1, - m_commitMessageFileName, - fileList, - m_submitOrigCommitFiles, - m_submitOrigDeleteFiles); + closeEditor = m_gitClient->addAndCommit(m_submitRepository, editor->panelData(), + m_commitAmendSHA1, m_commitMessageFileName, model); } if (closeEditor) cleanCommitMessageFile(); diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 5e45dd14c5..658c299c5b 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -206,8 +206,6 @@ private: QPointer<BranchDialog> m_branchDialog; QPointer<RemoteDialog> m_remoteDialog; QString m_submitRepository; - QStringList m_submitOrigCommitFiles; - QStringList m_submitOrigDeleteFiles; QString m_commitMessageFileName; QString m_commitAmendSHA1; bool m_submitActionTriggered; diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp index 34a96db285..9bb879b7ae 100644 --- a/src/plugins/git/gitsubmiteditor.cpp +++ b/src/plugins/git/gitsubmiteditor.cpp @@ -44,9 +44,6 @@ namespace Git { namespace Internal { -enum { FileTypeRole = Qt::UserRole + 1 }; -enum FileType { StagedFile , UnstagedFile, UntrackedFile }; - /* The problem with git is that no diff can be obtained to for a random * multiselection of staged/unstaged files; it requires the --cached * option for staged files. So, we sort apart the diff file lists @@ -64,21 +61,6 @@ GitSubmitEditorWidget *GitSubmitEditor::submitEditorWidget() return static_cast<GitSubmitEditorWidget *>(widget()); } -// Utility to add a list of state/file pairs to the model -// setting a file type. -static void addStateFileListToModel(const QList<CommitData::StateFilePair> &l, - bool checked, FileType ft, - VCSBase::SubmitFileModel *model) -{ - typedef QList<CommitData::StateFilePair>::const_iterator ConstIterator; - if (!l.empty()) { - const ConstIterator cend = l.constEnd(); - const QVariant fileTypeData(ft); - for (ConstIterator it = l.constBegin(); it != cend; ++it) - model->addFile(it->second, it->first, checked).front()->setData(fileTypeData, FileTypeRole); - } -} - void GitSubmitEditor::setCommitData(const CommitData &d) { submitEditorWidget()->setPanelData(d.panelData); @@ -87,14 +69,14 @@ void GitSubmitEditor::setCommitData(const CommitData &d) m_commitEncoding = d.commitEncoding; m_model = new VCSBase::SubmitFileModel(this); - addStateFileListToModel(d.stagedFiles, true, StagedFile, m_model); - addStateFileListToModel(d.unstagedFiles, false, UnstagedFile, m_model); - if (!d.untrackedFiles.empty()) { - const QString untrackedSpec = QLatin1String("untracked"); - const QVariant fileTypeData(UntrackedFile); - const QStringList::const_iterator cend = d.untrackedFiles.constEnd(); - for (QStringList::const_iterator it = d.untrackedFiles.constBegin(); it != cend; ++it) - m_model->addFile(*it, untrackedSpec, false).front()->setData(fileTypeData, FileTypeRole); + if (!d.files.isEmpty()) { + for (QList<CommitData::StateFilePair>::const_iterator it = d.files.constBegin(); + it != d.files.constEnd(); ++it) { + const CommitData::FileState state = it->first; + const QString file = it->second; + m_model->addFile(file, CommitData::stateDisplayName(state), state & CommitData::StagedFile, + QVariant(static_cast<int>(state))); + } } setFileModel(m_model); } @@ -109,17 +91,11 @@ void GitSubmitEditor::slotDiffSelected(const QStringList &files) for (int r = 0; r < rowCount; r++) { const QString fileName = m_model->item(r, fileColumn)->text(); if (files.contains(fileName)) { - const FileType ft = static_cast<FileType>(m_model->item(r, 0)->data(FileTypeRole).toInt()); - switch (ft) { - case StagedFile: + const CommitData::FileState state = static_cast<CommitData::FileState>(m_model->data(r).toInt()); + if (state & CommitData::StagedFile) stagedFiles.push_back(fileName); - break; - case UnstagedFile: + else if (state != CommitData::UntrackedFile) unstagedFiles.push_back(fileName); - break; - case UntrackedFile: - break; - } } } if (!unstagedFiles.empty() || !stagedFiles.empty()) diff --git a/src/plugins/vcsbase/submitfilemodel.cpp b/src/plugins/vcsbase/submitfilemodel.cpp index a35b23e2d0..45195dba89 100644 --- a/src/plugins/vcsbase/submitfilemodel.cpp +++ b/src/plugins/vcsbase/submitfilemodel.cpp @@ -38,6 +38,29 @@ namespace VCSBase { +// -------------------------------------------------------------------------- +// Helpers: +// -------------------------------------------------------------------------- + +static QList<QStandardItem *> createFileRow(const QString &fileName, const QString &status, + bool checked, const QVariant &v) +{ + QStandardItem *statusItem = new QStandardItem(status); + statusItem->setCheckable(true); + statusItem->setCheckState(checked ? Qt::Checked : Qt::Unchecked); + statusItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); + statusItem->setData(v); + QStandardItem *fileItem = new QStandardItem(fileName); + fileItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + QList<QStandardItem *> row; + row << statusItem << fileItem; + return row; +} + +// -------------------------------------------------------------------------- +// SubmitFileModel: +// -------------------------------------------------------------------------- + /*! \class VCSBase::SubmitFileModel @@ -54,24 +77,10 @@ SubmitFileModel::SubmitFileModel(QObject *parent) : setHorizontalHeaderLabels(headerLabels); } -QList<QStandardItem *> SubmitFileModel::createFileRow(const QString &fileName, const QString &status, bool checked) -{ - if (VCSBase::Constants::Internal::debug) - qDebug() << Q_FUNC_INFO << fileName << status << checked; - QStandardItem *statusItem = new QStandardItem(status); - statusItem->setCheckable(true); - statusItem->setCheckState(checked ? Qt::Checked : Qt::Unchecked); - statusItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); - QStandardItem *fileItem = new QStandardItem(fileName); - fileItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); - QList<QStandardItem *> row; - row << statusItem << fileItem; - return row; -} - -QList<QStandardItem *> SubmitFileModel::addFile(const QString &fileName, const QString &status, bool checked) +QList<QStandardItem *> SubmitFileModel::addFile(const QString &fileName, const QString &status, bool checked, + const QVariant &v) { - const QList<QStandardItem *> row = createFileRow(fileName, status, checked); + const QList<QStandardItem *> row = createFileRow(fileName, status, checked, v); appendRow(row); return row; } @@ -106,6 +115,13 @@ bool SubmitFileModel::checked(int row) const return (item(row)->checkState() == Qt::Checked); } +QVariant SubmitFileModel::data(int row) const +{ + if (row < 0 || row >= rowCount()) + return false; + return item(row)->data(); +} + bool SubmitFileModel::hasCheckedFiles() const { for (int i = 0; i < rowCount(); ++i) { diff --git a/src/plugins/vcsbase/submitfilemodel.h b/src/plugins/vcsbase/submitfilemodel.h index e5049b2009..7b5f5a01e1 100644 --- a/src/plugins/vcsbase/submitfilemodel.h +++ b/src/plugins/vcsbase/submitfilemodel.h @@ -46,8 +46,8 @@ public: explicit SubmitFileModel(QObject *parent = 0); // Convenience to create and add rows containing a file plus status text. - static QList<QStandardItem *> createFileRow(const QString &fileName, const QString &status = QString(), bool checked = true); - QList<QStandardItem *> addFile(const QString &fileName, const QString &status = QString(), bool checked = true); + QList<QStandardItem *> addFile(const QString &fileName, const QString &status = QString(), + bool checked = true, const QVariant &data = QVariant()); // Find convenience that returns the whole row (as opposed to QStandardItemModel::find). QList<QStandardItem *> findRow(const QString &text, int column = 0) const; @@ -58,6 +58,7 @@ public: QString state(int row) const; QString file(int row) const; bool checked(int row) const; + QVariant data(int row) const; bool hasCheckedFiles() const; |