.gear-rules | 3 ++ .gear-tags/list | 1 + configure.ac | 2 +- qgit.spec | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ src/annotate.cpp | 10 +++++- src/common.h | 7 ++-- src/consoleimpl.cpp | 21 ++++++++----- src/dataloader.h | 2 +- src/fileview.cpp | 11 ++++++- src/git.cpp | 17 ++++++---- src/git.h | 2 +- src/git_startup.cpp | 27 ++++++++++------- src/mainimpl.cpp | 39 ++++++++++++------------ src/mainimpl.h | 2 +- src/namespace_def.cpp | 9 +++++- src/revsview.cpp | 8 ++-- src/treeview.cpp | 5 +-- 17 files changed, 181 insertions(+), 64 deletions(-) diff --git a/.gear-rules b/.gear-rules new file mode 100644 index 0000000..de84aae --- /dev/null +++ b/.gear-rules @@ -0,0 +1,3 @@ +spec: qgit.spec +tar: @name@-@version@:. +diff: @name@-@version@:. . diff --git a/.gear-tags/list b/.gear-tags/list new file mode 100644 index 0000000..7b62648 --- /dev/null +++ b/.gear-tags/list @@ -0,0 +1 @@ +d1ef8749d0acdc7cef875c830e47aad52dd9d8ca qgit-1.5.6 diff --git a/configure.ac b/configure.ac index 29fbe20..fa0426d 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ LIBS="$LIBS $QT_LIBS" # If default CXXFLAGS is used, add some g++ warnings if test -z "$ac_env_CXXFLAGS_set"; then if test -n "$GXX"; then - CXXFLAGS="$CXXFLAGS -Wall -Wno-non-virtual-dtor -W -Wno-long-long -pedantic -frepo" + CXXFLAGS="$CXXFLAGS -Wall -Wno-non-virtual-dtor -W -Wno-long-long -pedantic" fi fi diff --git a/qgit.spec b/qgit.spec new file mode 100644 index 0000000..5e720b8 --- /dev/null +++ b/qgit.spec @@ -0,0 +1,79 @@ +# vim: set ft=spec: -*- rpm-spec -*- + +Name: qgit +Version: 1.5.6 +Release: alt2 + +Summary: git GUI viewer built on Qt/C++ +Group: Development/Other +License: GPL +Url: http://digilander.libero.it/mcostalba/ + +Packager: Sir Raorn + +# git://git.altlinux.org/people/raorn/packages/qgit.git +Source: %name-%version.tar +Patch: %name-%version-%release.patch + +# Automatically added by buildreq on Fri May 12 2006 +BuildRequires: gcc-c++ libqt3-devel libXft-devel libXt-devel + +%description +With qgit you will be able to browse revisions history, view +patch content and changed files, graphically following different +development branches. + +Features: + +* View revisions, diffs, files history, files annotation, + archive tree. + +* Commit changes visually cherry picking modified files. + +* Apply or format patch series from selected commits, drag and + drop commits between two instances of qgit. + +* qgit implements a GUI for the most common StGIT commands like + push/pop and apply/format patches. You can also create new + patches or refresh current top one using the same semantics of + git commit, i.e. cherry picking single modified files. + +%prep +%setup +%patch -p1 + +%build +%__autoreconf +%configure +%make_build + +%install +%make_install DESTDIR=%buildroot install + +%files +%doc README ChangeLog +%_bindir/%name + +%changelog +* Sun Sep 02 2007 Sir Raorn 1.5.6-alt2 +- Updated to 1.5.6-5a135439 +- Use QProcess instead of temporary files (CVE-2007-4631 workaround) + +* Tue Aug 07 2007 Sir Raorn 1.5.6-alt1 +- [1.5.6] + +* Fri Feb 02 2007 Sir Raorn 1.5.4-alt1 +- [1.5.4] + +* Mon Nov 20 2006 Sir Raorn 1.5.3-alt1 +- [1.5.3] + +* Wed Nov 08 2006 Sir Raorn 1.5.2-alt1 +- [1.5.2] + +* Sun Sep 03 2006 Sir Raorn 1.4-alt1 +- [1.4] + +* Fri May 12 2006 Sir Raorn 1.2-alt1 +- Built for Sisyphus + diff --git a/src/annotate.cpp b/src/annotate.cpp index 56bbe3b..d7d9c76 100644 --- a/src/annotate.cpp +++ b/src/annotate.cpp @@ -385,8 +385,8 @@ void Annotate::setAnnotation(SCRef diff, SCRef author, SCList prevAnn, // message is locale dependent, so just test the space after '\' if (line[1] == ' ') break; - else - ; // fall through + + // fall through default: ++cur; break; @@ -737,6 +737,12 @@ const QString Annotate::getAncestor(SCRef sha, SCRef fileName, int* shaIdx) { if (fa.fileSha == fileSha) return histRevOrder[*shaIdx]; } + // ok still not found, this could happen if sha is an unapplied + // stgit patch. In this case fall back on the first in the list + // that is the newest. + if (git->getAllRefSha(Git::UN_APPLIED).contains(sha)) + return histRevOrder.first(); + dbp("ASSERT in getAncestor: ancestor of %1 not found", sha); return ""; } diff --git a/src/common.h b/src/common.h index 2b49050..cd062ca 100644 --- a/src/common.h +++ b/src/common.h @@ -267,11 +267,11 @@ public: descRefsMaster = ancRefsMaster = descBrnMaster = -1; *next = indexData(); } - bool isBoundary() const { return (boundaryOfs == 1); } + bool isBoundary() const { return boundary; } uint parentsCount() const { return parentsCnt; } const QString parent(int idx) const; const QStringList parents() const; - const QString sha() const { return mid(start + boundaryOfs, 40); } + const QString sha() const { return mid(start + startOfs, 40); } const QString author() const { return mid(autStart, autLen); } const QString authorDate() const { return mid(autDateStart, autDateLen); } const QString shortLog() const { return mid(sLogStart, sLogLen); } @@ -293,7 +293,8 @@ private: const QByteArray& ba; // reference here! const uint start; - uint parentsCnt, boundaryOfs; + bool boundary; + uint parentsCnt, startOfs; int autStart, autLen, autDateStart, autDateLen; int sLogStart, sLogLen, lLogStart, lLogLen; }; diff --git a/src/consoleimpl.cpp b/src/consoleimpl.cpp index c105df2..3964c15 100644 --- a/src/consoleimpl.cpp +++ b/src/consoleimpl.cpp @@ -52,17 +52,22 @@ bool ConsoleImpl::start(const QString& cmd, const QString& cmdArgs) { statusBar()->message("Executing \'" + name + "\' action..."); - QString t(cmd.section('\n', 1, 0xffffffff, QString::SectionIncludeLeadingSep)); - // in case of a multi-sequence, line arguments are bounded to first command only - QString txt = cmd.section('\n', 0, 0).append(cmdArgs).append(t); - textLabelCmd->setText(txt); + QString txt = cmd.section('\n', 0, 0).stripWhiteSpace(); + QString args = cmdArgs.stripWhiteSpace(); + QString tail(cmd.section('\n', 1).stripWhiteSpace()); + + if (!args.isEmpty()) + txt.append(' ' + args); - if (t.stripWhiteSpace().isEmpty()) - // any one-line command followed by a newline would fail - proc = git->runAsync(cmd.stripWhiteSpace(), this); + if (!tail.isEmpty()) // any one-line command followed by a newline would fail + txt.append('\n' + tail); + + textLabelCmd->setText(txt); + if (tail.isEmpty()) + proc = git->runAsync(txt, this); else - proc = git->runAsScript(cmd, this); // wrap in a script + proc = git->runAsScript(txt.append('\n'), this); // wrap in a script if (proc.isNull()) deleteLater(); diff --git a/src/dataloader.h b/src/dataloader.h index fdcc3a0..0c4bbd1 100644 --- a/src/dataloader.h +++ b/src/dataloader.h @@ -20,7 +20,7 @@ class MyProcess; // data exchange facility with git-rev-list could be based on QProcess or on // a temporary file (default). Uncomment following line to use QProcess -// #define USE_QPROCESS +#define USE_QPROCESS class DataLoader : public QObject { Q_OBJECT diff --git a/src/fileview.cpp b/src/fileview.cpp index 8fdb8ea..b0cb0e2 100644 --- a/src/fileview.cpp +++ b/src/fileview.cpp @@ -335,8 +335,17 @@ void FileView::on_fileAvailable(bool b) { void FileView::on_revIdSelected(int id) { - if (id != 0 && fileTab->spinBoxRevision->isEnabled()) + if (id == 0) + return; + + if (fileTab->spinBoxRevision->isEnabled()) fileTab->spinBoxRevision->setValue(id); + else { + QListView* hv = fileTab->histListView; + QString row = QString::number(id); + QListViewItem* item = hv->findItem(row, QGit::ANN_ID_COL, Qt::Contains); + hv->setCurrentItem(item); + } } // ******************************* data events **************************** diff --git a/src/git.cpp b/src/git.cpp index 4c498d6..d2513fa 100644 --- a/src/git.cpp +++ b/src/git.cpp @@ -1113,13 +1113,16 @@ bool Git::resetCommits(int parentDepth) { return run(runCmd); } -bool Git::applyPatchFile(SCRef patchPath, bool commit, bool fold, bool isDragDrop) { - - if (commit && isStGIT) { - if (fold) - return run("stg fold " + quote(patchPath)); - - return run("stg import --mail " + quote(patchPath)); +bool Git::applyPatchFile(SCRef patchPath, bool fold, bool isDragDrop) { + + if (isStGIT) { + if (fold) { + bool ok = run("stg fold " + quote(patchPath)); // merge in working dir + if (ok) + ok = run("stg refresh"); // update top patch + return ok; + } else + return run("stg import --mail " + quote(patchPath)); } QString runCmd("git am --utf8 --3way "); if (isDragDrop) diff --git a/src/git.h b/src/git.h index f3425aa..c0376bd 100644 --- a/src/git.h +++ b/src/git.h @@ -114,7 +114,7 @@ public: bool commitFiles(SCList files, SCRef msg); bool makeTag(SCRef sha, SCRef tag, SCRef msg); bool deleteTag(SCRef sha); - bool applyPatchFile(SCRef patchPath, bool commit, bool fold, bool sign); + bool applyPatchFile(SCRef patchPath, bool fold, bool sign); bool resetCommits(int parentDepth); bool stgCommit(SCList selFiles, SCRef msg, SCRef patchName, bool fold); bool stgPush(SCRef sha); diff --git a/src/git_startup.cpp b/src/git_startup.cpp index 9dd1562..d9bba4f 100644 --- a/src/git_startup.cpp +++ b/src/git_startup.cpp @@ -725,7 +725,7 @@ int Git::addChunk(FileHistory* fh, const QByteArray& ba, int start) { // out unknown revs until no more StGIT patches are waited and // firstNonStGitPatch is reached if (!(firstNonStGitPatch.isEmpty() && patchesStillToFind == 0) && - !loadingUnAppliedPatches) { + !loadingUnAppliedPatches && isMainHistory(fh)) { Reference* rf = lookupReference(sha); if (!(rf && (rf->type & APPLIED))) { @@ -759,18 +759,20 @@ int Git::addChunk(FileHistory* fh, const QByteArray& ba, int start) { c->isUnApplied = true; c->lanes.append(UNAPPLIED); - } else if (patchesStillToFind > 0) { // try to avoid costly lookup + } else if (patchesStillToFind > 0 || !isMainHistory(fh)) { // try to avoid costly lookup Reference* rf = lookupReference(sha); if (rf && (rf->type & APPLIED)) { Rev* c = const_cast(revLookup(sha, fh)); c->isApplied = true; - patchesStillToFind--; - if (patchesStillToFind == 0) - // any rev will be discarded until - // firstNonStGitPatch arrives - firstNonStGitPatch = c->parent(0); + if (isMainHistory(fh)) { + patchesStillToFind--; + if (patchesStillToFind == 0) + // any rev will be discarded until + // firstNonStGitPatch arrives + firstNonStGitPatch = c->parent(0); + } } } } @@ -1131,7 +1133,7 @@ const QString Rev::mid(int start, int len) const { const QString Rev::parent(int idx) const { - return mid(start + boundaryOfs + 41 + 41 * idx, 40); + return mid(start + startOfs + 41 + 41 * idx, 40); } const QStringList Rev::parents() const { @@ -1139,7 +1141,7 @@ const QStringList Rev::parents() const { if (parentsCnt == 0) return QStringList(); - int ofs = start + boundaryOfs + 41; + int ofs = start + startOfs + 41; return QStringList::split(' ', mid(ofs, 41 * parentsCnt - 1)); } @@ -1159,10 +1161,13 @@ int Rev::indexData() { // fast path here, less then 4% of load time - a terminating '\0' */ int last = ba.size() - 1; - boundaryOfs = uint(ba[start] == '-'); + + // take in account --boundary and --left-right options + startOfs = uint(ba.at(start) == '-' || ba.at(start) == '<' || ba.at(start) == '>'); + boundary = startOfs && ba.at(start) == '-'; parentsCnt = 0; - int idx = start + boundaryOfs + 40; + int idx = start + startOfs + 40; while (idx < last && ba[idx] == ' ') { idx += 41; parentsCnt++; diff --git a/src/mainimpl.cpp b/src/mainimpl.cpp index f444eb6..1169464 100644 --- a/src/mainimpl.cpp +++ b/src/mainimpl.cpp @@ -603,13 +603,14 @@ void MainImpl::filterList(bool isOn, bool onlyHighlight) { } bool evenLine = false; int visibleCnt = 0; + QRegExp re(filter, false, true); QListViewItem* firstItem = NULL; QListView* lv = rv->tab()->listViewLog; QListViewItemIterator it(lv); while (it.current()) { ListViewItem* item = static_cast(it.current()); if (isOn) { - if (passFilter(item, filter, colNum, shaMap)) { + if (passFilter(item, re, colNum, shaMap)) { if (onlyHighlight) item->setHighlighted(true); else { @@ -655,7 +656,7 @@ void MainImpl::filterList(bool isOn, bool onlyHighlight) { QApplication::postEvent(rv, new MessageEvent(msg)); } -bool MainImpl::passFilter(ListViewItem* item, SCRef filter, int colNum, +bool MainImpl::passFilter(ListViewItem* item, const QRegExp& filter, int colNum, const QMap& shaMap) { if (colNum == SHA_MAP_COL) @@ -680,7 +681,7 @@ bool MainImpl::passFilter(ListViewItem* item, SCRef filter, int colNum, field = item->sha(); } // wildcard search, case insensitive - return (field.find(QRegExp(filter, false, true)) != -1); + return (field.find(filter) != -1); } void MainImpl::customEvent(QCustomEvent* e) { @@ -1319,25 +1320,22 @@ void MainImpl::ActMailFormatPatch_activated() { QApplication::restoreOverrideCursor(); } -bool MainImpl::askApplyPatchParameters(bool* commit, bool* fold) { +bool MainImpl::askApplyPatchParameters(bool* workDirOnly, bool* fold) { - int ret = QMessageBox::question(this, "Apply Patch", - "Do you want to commit or just to apply changes to " - "working directory?", "&Cancel", "&Working dir", "&Commit", 0, 0); - if (ret == 0) - return false; - - *commit = (ret == 2); - *fold = false; - if (*commit && git->isStGITStack()) { + int ret = 0; + if (!git->isStGITStack()) { + ret = QMessageBox::question(this, "Apply Patch", + "Do you want to commit or just to apply changes to " + "working directory?", "&Cancel", "&Working dir", "&Commit", 0, 0); + *workDirOnly = (ret == 1); + *fold = false; + } else { ret = QMessageBox::question(this, "Apply Patch", "Do you want to " "import or fold the patch?", "&Cancel", "&Fold", "&Import", 0, 0); - if (ret == 0) - return false; - + *workDirOnly = false; *fold = (ret == 1); } - return true; + return (ret != 0); } void MainImpl::ActMailApplyPatch_activated() { @@ -1352,13 +1350,14 @@ void MainImpl::ActMailApplyPatch_activated() { QFileInfo f(patchName); settings.writeEntry(APP_KEY + FP_DIR_KEY, f.dirPath(true)); - bool commit, fold; - if (!askApplyPatchParameters(&commit, &fold)) + bool workDirOnly, fold; + if (!askApplyPatchParameters(&workDirOnly, &fold)) return; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - if (git->applyPatchFile(f.absFilePath(), commit, fold, !Git::optDragDrop) && !commit) + bool ok = git->applyPatchFile(f.absFilePath(), fold, !Git::optDragDrop); + if (workDirOnly && ok) git->resetCommits(1); QApplication::restoreOverrideCursor(); diff --git a/src/mainimpl.h b/src/mainimpl.h index 6484800..880a227 100644 --- a/src/mainimpl.h +++ b/src/mainimpl.h @@ -116,7 +116,7 @@ private: void setupAccelerator(QAccel* accel); int currentTabType(Domain** t); void filterList(bool isOn, bool onlyHighlight); - bool passFilter(ListViewItem* i, SCRef f, int cn, const QMap& sm); + bool passFilter(ListViewItem* i, const QRegExp& f, int cn, const QMap& sm); void setRepository(SCRef wd, bool r, bool ks, QStringList* fl = NULL); void diffViewerUpdate(QListViewItem* item, bool newDiff); void getExternalDiffArgs(QStringList* args); diff --git a/src/namespace_def.cpp b/src/namespace_def.cpp index dc1b4bc..50579ba 100644 --- a/src/namespace_def.cpp +++ b/src/namespace_def.cpp @@ -114,7 +114,14 @@ void QGit::writeSetting(SCRef key, SCRef value, SCRef group) { // misc helpers bool QGit::stripPartialParaghraps(const QByteArray& ba, QString* dst, QString* prev) { - const QString src(ba); + QString src(ba); + // handle rare case of a '\0' inside content + while (src.length() < ba.size() && ba.at(src.length()) == '\0') { + int start = src.length() + 1; + QByteArray tail; + tail.duplicate(ba.data() + start, ba.size() - start); + src.append(" ").append(tail); // a space instead of '\0' so sizes match + } int idx = src.findRev('\n'); if (idx == -1) { prev->append(src); diff --git a/src/revsview.cpp b/src/revsview.cpp index 4304367..926b881 100644 --- a/src/revsview.cpp +++ b/src/revsview.cpp @@ -277,8 +277,8 @@ void RevsView::on_droppedRevisions(SCList remoteRevs) { m()->statusBar()->message(tmp); return; } - bool commit, fold; - if (!m()->askApplyPatchParameters(&commit, &fold)) + bool workDirOnly, fold; + if (!m()->askApplyPatchParameters(&workDirOnly, &fold)) return; // ok, let's go @@ -313,7 +313,7 @@ void RevsView::on_droppedRevisions(SCList remoteRevs) { break; } SCRef fn(dr.absFilePath(dr[0])); - if (!git->applyPatchFile(fn, commit, fold, Git::optDragDrop)) + if (!git->applyPatchFile(fn, fold, Git::optDragDrop)) break; dr.remove(fn); @@ -325,7 +325,7 @@ void RevsView::on_droppedRevisions(SCList remoteRevs) { else m()->statusBar()->message("Failed to import revision " + QString::number(revNum--)); - if (!commit && (revNum > 0)) + if (workDirOnly && (revNum > 0)) git->resetCommits(revNum); dr.rmdir(dr.absPath()); // 'dr' must be already empty diff --git a/src/treeview.cpp b/src/treeview.cpp index a72eb04..11855d8 100644 --- a/src/treeview.cpp +++ b/src/treeview.cpp @@ -326,14 +326,13 @@ void TreeView::update() { item = item->itemBelow(); } } + // check if st->fileName() has been deleted by a patch older than this tree if (item && treeIsValid) { listView->clearSelection(); listView->setSelected(item, true); listView->setCurrentItem(item); // calls on_currentChanged() listView->ensureItemVisible(item); - } else - ; // st->fileName() has been deleted by a patch older than this tree - + } listView->setUpdatesEnabled(true); listView->triggerUpdate(); restoreStuff();