Browse Source

Start working on commit tree annotation

Alexey Edelev 7 years ago
parent
commit
4594f071b9
18 changed files with 373 additions and 36 deletions
  1. 8 2
      NiceGit.pro
  2. 66 3
      commitgraph.cpp
  3. 5 0
      commitgraph.h
  4. 42 0
      gitcommit.cpp
  5. 6 0
      gitcommit.h
  6. 6 0
      gitdiff.cpp
  7. 11 0
      gitdiff.h
  8. 2 6
      githandler.cpp
  9. 27 0
      gitrepository.cpp
  10. 12 0
      gitrepository.h
  11. 18 0
      gittag.cpp
  12. 49 0
      gittag.h
  13. 11 1
      graphpoint.cpp
  14. 25 1
      graphpoint.h
  15. 2 0
      main.cpp
  16. 65 23
      qml/MainView.qml
  17. 5 0
      tagmodel.cpp
  18. 13 0
      tagmodel.h

+ 8 - 2
NiceGit.pro

@@ -17,7 +17,10 @@ SOURCES += \
     commitmodel.cpp \
     universallistmodel.cpp \
     commitgraph.cpp \
-    graphpoint.cpp
+    graphpoint.cpp \
+    gittag.cpp \
+    tagmodel.cpp \
+    gitdiff.cpp
 
 HEADERS += \
     githandler.h \
@@ -31,7 +34,10 @@ HEADERS += \
     commitmodel.h \
     universallistmodel.h \
     commitgraph.h \
-    graphpoint.h
+    graphpoint.h \
+    gittag.h \
+    tagmodel.h \
+    gitdiff.h
 
 RESOURCES += \
     resources.qrc

+ 66 - 3
commitgraph.cpp

@@ -1,9 +1,13 @@
 #include "commitgraph.h"
 
 #include <gitcommit.h>
+#include <gitbranch.h>
+#include <gittag.h>
+
 #include <graphpoint.h>
 
 #include <QDateTime>
+#include <QScopedPointer>
 
 #include <git2/revwalk.h>
 #include <git2/commit.h>
@@ -13,6 +17,13 @@ CommitGraph::CommitGraph() : QObject()
     qsrand(QDateTime::currentMSecsSinceEpoch());
 }
 
+void CommitGraph::addHead(GitBranch* branch)
+{
+    const GitOid& oid = branch->oid();
+    addHead(oid);
+    m_points[oid]->setBranch(branch->name());
+}
+
 void CommitGraph::addHead(const GitOid &oid)
 {
     //Random color generation to be replaced with resets
@@ -32,24 +43,60 @@ void CommitGraph::addHead(const GitOid &oid)
         GitOid commitOid(&newOid, oid.repository());
         GitCommit *commit = GitCommit::fromOid(commitOid);
         findParents(commit);
+        delete commit;
     }
 
     git_revwalk_free(walk);
 
-    qDebug() << "Update Y coordinate after head added";
+//    qDebug() << "Update Y coordinate after head added";
+    QList<int> branchStarted;
     for(int i = 0; i < m_sortedPoints.count(); i++) {
         GraphPoint* point = static_cast<GraphPoint*>(m_sortedPoints.at(i));
         point->setY(m_sortedPoints.count() - i - 1);
         GitCommit *commit = GitCommit::fromOid(point->oid());
         git_commit* commitRaw = nullptr;
+        QPointer<GitTag> tag = commit->repository()->tags().value(commit->oid());
+        if(!tag.isNull()) {
+            point->setTag(tag.data()->name());
+        }
         int parentCount = git_commit_parentcount(commit->raw());
+        delete commit;
+//        qDebug() << "New commit: " << point->oid().toString() << point->x() << point->y();
         for(int j = 0; j < parentCount; j++) {//Add connection to parent in case if count of parents > 1
             git_commit_parent(&commitRaw, commit->raw(), j);
             GitOid oidParent(git_commit_id(commitRaw), oid.repository());
             GraphPoint* parentPoint = m_points.value(oidParent);
-            parentPoint->addChildPoint(point);
+            bool rearrangmentRequired = parentPoint->addChildPoint(point);
+            Q_UNUSED(rearrangmentRequired)
+//            if(rearrangmentRequired) { //TODO: need to investigate how to avoid croses on branches
+                                         //in case of while addChildPoint operations we have conflict
+                                         //with logic bellow
+//                i = m_sortedPoints.indexOf(parentPoint) - 1;
+//                break;
+//            }
+
+            //This logic denie to produce branches that are on the same 'x' line with other branches
+            //that are active at this time. The loop bellow finds free 'x' line to use it for branch
+            //allocation.
+            if(parentPoint->x() < point->x()) {
+                bool contains = false;
+                while(branchStarted.contains(point->x())) {
+                    contains = true;
+                    point->setX(point->x() + 1);
+                }
+                if(!contains) {
+                    branchStarted.append(point->x());
+                }
+            //Once branch is merged to another branch or ends "in the air" this logic releases branch
+            //line and provides possibility to use freed 'x' for other branches.
+            } else if(parentPoint->x() > point->x()) {
+                branchStarted.removeAll(parentPoint->x());
+            }
+
+            if( point->childPoints().count() <= 0) {
+                branchStarted.removeAll(point->x());
+            }
         }
-        qDebug() << "New commit: " << point->oid().toString() << point->x() << point->y();
     }
 }
 
@@ -113,3 +160,19 @@ void CommitGraph::addCommits(QList<GitOid>& reversList)
         }
     }
 }
+
+QString CommitGraph::commitData(GraphPoint *point) const
+{
+    if(point == nullptr) {
+        return QString();
+    }
+
+    QScopedPointer<GitCommit> commit(GitCommit::fromOid(point->oid()));
+    if(!commit.data()->isValid()) {
+        return QString();
+    }
+
+    return commit.data()->body();
+
+
+}

+ 5 - 0
commitgraph.h

@@ -11,6 +11,7 @@
 
 class GitCommit;
 class GraphPoint;
+class GitBranch;
 
 class CommitGraph : public QObject
 {
@@ -18,12 +19,16 @@ class CommitGraph : public QObject
     Q_PROPERTY(QList<QObject*> points READ points CONSTANT)
 public:
     CommitGraph();
+    void addHead(GitBranch* branch);
     void addHead(const GitOid& oid);
 
     QList<QObject*> points() const {
         return m_sortedPoints;
     }
 
+    //TODO: move this functionality to more proper place;
+    Q_INVOKABLE QString commitData(GraphPoint* point) const;
+    //TODO: move this functionality to more proper place;
 private:
     void findParents(GitCommit *commit);
     void addCommits(QList<GitOid> &reversList);

+ 42 - 0
gitcommit.cpp

@@ -5,6 +5,10 @@
 #include <QDebug>
 
 #include <git2/commit.h>
+#include <git2/tag.h>
+#include <git2/diff.h>
+#include <git2/patch.h>
+#include <git2/buffer.h>
 
 GitCommit::GitCommit(git_commit* raw, GitRepository* parent) : GitBase(raw, parent)
 {
@@ -69,6 +73,44 @@ bool GitCommit::isMerge() const
     return git_commit_parentcount(m_raw) > 1;
 }
 
+QString GitCommit::body()
+{
+    if(isMerge()) {
+        return QString("Commit - merge");
+    }
+
+
+    if(m_body.isEmpty() || m_body.isNull()) {
+        git_commit *parent = nullptr;
+        git_commit_parent(&parent, raw(), 0);
+
+        git_tree *commit_tree = nullptr, *parent_tree = nullptr;
+        git_commit_tree(&commit_tree, raw());
+        git_commit_tree(&parent_tree, parent);
+
+        git_diff *diff = nullptr;
+        git_diff_tree_to_tree(
+                    &diff, repository()->raw()  , parent_tree, commit_tree, nullptr);
+
+
+       git_diff_print(diff,
+                      GIT_DIFF_FORMAT_PATCH,
+                        [](const git_diff_delta *delta, /**< delta that contains this data */
+                      const git_diff_hunk *hunk,   /**< hunk containing this data */
+                      const git_diff_line *line,   /**< line data */
+                      void *payload)->int
+        {
+           qDebug() << "GIT_DELTA_ADDED" << delta->status;
+//           qDebug() << hunk->new_lines;
+//           qDebug() << hunk->old_lines;
+//            qDebug() << line->new_lineno;
+            return 0;
+        }, this);
+//        m_body = QString::fromUtf8(git_diff_(raw()));
+    }
+    return m_body;
+}
+
 void GitCommit::setAuthor(QString author)
 {
     Q_UNUSED(author)

+ 6 - 0
gitcommit.h

@@ -2,6 +2,7 @@
 #define GITCOMMIT_H
 
 #include <gitbase.h>
+#include <gittag.h>
 
 #include <QDateTime>
 #include <QString>
@@ -19,6 +20,7 @@ class GitCommit : public GitBase<git_commit>
     Q_PROPERTY(QString sha1 READ sha1 NOTIFY commitChanged)
     Q_PROPERTY(QString shortSha1 READ shortSha1 NOTIFY commitChanged)
     Q_PROPERTY(bool isMerge READ isMerge NOTIFY commitChanged)
+    Q_PROPERTY(QString body READ body NOTIFY bodyChanged)
 
 public:
     GitCommit(git_commit* raw, GitRepository* parent);
@@ -33,6 +35,7 @@ public:
     QString sha1() const;
     QString shortSha1() const;
     bool isMerge() const;
+    QString body();
 
 public slots:
     void setAuthor(QString author);
@@ -43,6 +46,8 @@ public slots:
 signals:
     void commitChanged();
 
+    void bodyChanged(QString body);
+
 private:
     GitCommit();
 
@@ -50,6 +55,7 @@ private:
     QDateTime m_time;
     QString m_message;
     QString m_email;
+    QString m_body;
 };
 
 #endif // GITCOMMIT_H

+ 6 - 0
gitdiff.cpp

@@ -0,0 +1,6 @@
+#include "gitdiff.h"
+
+GitDiff::GitDiff()
+{
+
+}

+ 11 - 0
gitdiff.h

@@ -0,0 +1,11 @@
+#ifndef GITDIFF_H
+#define GITDIFF_H
+
+
+class GitDiff
+{
+public:
+    GitDiff();
+};
+
+#endif // GITDIFF_H

+ 2 - 6
githandler.cpp

@@ -7,6 +7,7 @@
 #include <gitrepository.h>
 #include <gitbranch.h>
 #include <commitmodel.h>
+#include <tagmodel.h>
 #include <git2.h>
 
 #include <commitgraph.h>
@@ -50,15 +51,10 @@ void GitHandler::open(const QString &path)
     graph->addHead(branches.value("master").data()->oid());
     foreach(GitBranch* branch, branches) {
         qDebug() << "Next head " << branch->name();
-        graph->addHead(branch->oid());
+        graph->addHead(branch);
     }
 
     setGraph(graph);
-//    CommitModel* main = new CommitModel("main");
-//    foreach (GitCommit* commitPtr, graph->m_points) {
-//        main->add(commitPtr);
-//    }
-//    m_commits.insert("main", main);
     m_repositories->add(repo);
 }
 

+ 27 - 0
gitrepository.cpp

@@ -6,6 +6,7 @@
 
 #include <gitbranch.h>
 #include <gitcommit.h>
+#include <gittag.h>
 
 #include <git2.h>
 
@@ -22,6 +23,7 @@ GitRepository::GitRepository(const QString& root) : QObject(nullptr)
     m_name = m_path;//TODO: replace with Human readable name
     qDebug() << "New repo:" << m_name << m_root << m_path;
     readBranches();
+    readTags();
 }
 
 GitRepository::~GitRepository()
@@ -52,3 +54,28 @@ void GitRepository::readBranches()
     }
 }
 
+void GitRepository::readTags()
+{
+    git_tag_foreach(
+                raw(),
+                [](const char *name, git_oid *oid, void *payload) -> int
+    {
+        Q_UNUSED(payload)
+        Q_UNUSED(name)
+
+        GitRepository* repo = static_cast<GitRepository*>(payload);
+        git_tag* tagraw = 0;
+        if(git_tag_lookup(&tagraw, repo->raw(), oid) != 0) {
+            qCritical() << "Invalid tag found. Broken repository";
+            return 1;
+        }
+        GitTag* tag = new GitTag(tagraw, repo);
+        if(tag->isValid()) {
+            repo->m_tags.insert(tag->targetId(), tag);
+        }
+        qDebug() << "Tag found: " << tag->name() << tag->sha1();
+        return 0;
+    },
+    this);
+}
+

+ 12 - 0
gitrepository.h

@@ -6,10 +6,16 @@
 #include <QMap>
 #include <QPointer>
 
+#include <gitoid.h>
+
 struct git_repository;
 
 class GitBranch;
+class GitTag;
+struct git_oid;
+
 typedef QMap<QString, QPointer<GitBranch> > BranchContainer;
+typedef QMap<GitOid, QPointer<GitTag> > TagContainer;
 
 class GitRepository : public QObject
 {
@@ -46,6 +52,10 @@ public:
         return m_branches;
     }
 
+    TagContainer& tags() {
+        return m_tags;
+    }
+
 
 public slots:
     void setRoot(QString root) {
@@ -71,6 +81,7 @@ signals:
 private:
     void close();
     void readBranches();
+    void readTags();
 
     QString m_root;
     QString m_name;
@@ -78,6 +89,7 @@ private:
     git_repository* m_raw;
 
     BranchContainer m_branches;
+    TagContainer m_tags;
 };
 
 #endif // GITREPOSITORY_H

+ 18 - 0
gittag.cpp

@@ -0,0 +1,18 @@
+#include "gittag.h"
+#include <git2/tag.h>
+
+GitTag::GitTag(git_tag *raw, GitRepository *parent) : GitBase(raw, parent)
+{
+    if(raw == nullptr) {
+        return;
+    }
+
+    const git_oid* oid = git_tag_id(raw);
+    m_oid = GitOid(oid, parent);
+    m_name = QString::fromUtf8(git_tag_name(raw));
+}
+
+GitOid GitTag::targetId() const
+{
+    return GitOid(git_tag_target_id(m_raw), repository());
+}

+ 49 - 0
gittag.h

@@ -0,0 +1,49 @@
+#ifndef GITTAG_H
+#define GITTAG_H
+
+#include <gitbase.h>
+
+class GitTag : public GitBase<git_tag>
+{
+    Q_OBJECT
+    Q_PROPERTY(QString name READ name NOTIFY tagChanged)
+    Q_PROPERTY(QString message READ message NOTIFY tagChanged)
+    Q_PROPERTY(QString owner READ owner NOTIFY tagChanged)
+    Q_PROPERTY(QString sha1 READ sha1 NOTIFY tagChanged)
+
+public:
+    GitTag(git_tag* raw, GitRepository* parent);
+
+    QString name() const
+    {
+        return m_name;
+    }
+
+    QString message() const
+    {
+        return m_message;
+    }
+
+    QString owner() const
+    {
+        return m_owner;
+    }
+
+    QString sha1() const
+    {
+        return m_oid.toString();
+    }
+
+    GitOid targetId() const;
+
+signals:
+    void tagChanged();
+
+private:
+    QString m_name;
+    QString m_message;
+    QString m_owner;
+    QString m_sha1;
+};
+
+#endif // GITTAG_H

+ 11 - 1
graphpoint.cpp

@@ -50,10 +50,20 @@ void GraphPoint::setColor(const QString& color)
     emit colorChanged(color);
 }
 
-void GraphPoint::addChildPoint(GraphPoint* point)
+bool GraphPoint::addChildPoint(GraphPoint* point)
 {
+    bool orderChanged = false;
     if(m_childPoints.indexOf(point) < 0) {
+        if(point->x() < x()) {
+            for(int i = 0; i < m_childPoints.count(); i++) {
+                GraphPoint* child = static_cast<GraphPoint*>(m_childPoints.at(i));
+                child->setX(child->x() + 1);
+            }
+            orderChanged = true;
+        }
         m_childPoints.append(point);
     }
+
+    return orderChanged;
 }
 

+ 25 - 1
graphpoint.h

@@ -13,6 +13,8 @@ class GraphPoint : public QObject
     Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
     Q_PROPERTY(QString sha1 READ sha1 CONSTANT)
     Q_PROPERTY(QList<QObject*> childPoints READ childPoints CONSTANT)
+    Q_PROPERTY(QString tag READ tag CONSTANT)
+    Q_PROPERTY(QString branch READ branch CONSTANT)
 
 public:
     GraphPoint(const GitOid& commitOid, QObject* parent = 0);
@@ -49,18 +51,38 @@ public:
         return m_childPoints.count();
     }
 
-    void addChildPoint(GraphPoint* point);
+    bool addChildPoint(GraphPoint* point);
 
     QList<QObject*> childPoints() const
     {
         return m_childPoints;
     }
 
+    QString tag() const
+    {
+        return m_tag;
+    }
+
+    QString branch() const
+    {
+        return m_branch;
+    }
+
 public slots:
     void setX(int x);
     void setY(int y);
     void setColor(const QString& color);
 
+    void setTag(QString tag)
+    {
+        m_tag = tag;
+    }
+
+    void setBranch(QString branch)
+    {
+        m_branch = branch;
+    }
+
 signals:
     void xChanged(int x);
     void yChanged(int y);
@@ -73,6 +95,8 @@ private:
     int m_x;
     int m_y;
     QString m_color;
+    QString m_tag;
+    QString m_branch;
 };
 
 #endif // GRAPTHPOINT_H

+ 2 - 0
main.cpp

@@ -10,6 +10,7 @@
 #include <commitmodel.h>
 #include <graphpoint.h>
 #include <commitgraph.h>
+#include <gittag.h>
 
 #include <QDebug>
 
@@ -26,6 +27,7 @@ int main(int argc, char *argv[])
     qmlRegisterUncreatableType<GitRepository>("org.semlanik.nicegit", 1, 0, "GitRepository", "Owned only by GitHandler");
     qmlRegisterUncreatableType<GitBranch>("org.semlanik.nicegit", 1, 0, "GitRepository", "Owned only by GitHandler");
     qmlRegisterUncreatableType<GitHandler>("org.semlanik.nicegit", 1, 0, "GitHandler", "Global for qml");
+    qmlRegisterUncreatableType<GitTag>("org.semlanik.nicegit", 1, 0, "GitTag", "Global for qml");
 
 
     GitHandler handler;

+ 65 - 23
qml/MainView.qml

@@ -34,29 +34,6 @@ Item {
         anchors.right: parent.right
         contentWidth: innerItem.width
         contentHeight: innerItem.height
-        Column {
-            width: parent.width
-            spacing: 20
-            Repeater {
-                model: _handler.graph.points.length
-                Rectangle {
-                    width: parent.width
-                    height: innerItem.elementHeight
-                    color: textSelector.containsMouse ? "#9999ff" : "#009999ff"
-                    Text {
-                        anchors.fill: parent
-                        verticalAlignment: Text.AlignVCenter
-                        horizontalAlignment: Text.AlignRight
-                        text: _handler.graph.points[_handler.graph.points.length - model.index - 1].sha1
-                    }
-                    MouseArea {
-                        id: textSelector
-                        anchors.fill: parent
-                        hoverEnabled: true
-                    }
-                }
-            }
-        }
         Canvas {
             id: innerItem
             width: root.width/2
@@ -95,6 +72,7 @@ Item {
                                                   point.x*(elementWidth + 20) + elementWidth/2, childPoint.y*(elementHeight + 20) + elementHeight/2,
                                                   childPoint.x*(elementWidth + 20) + elementWidth/2, childPoint.y*(elementHeight + 20) + elementHeight + 20  + elementHeight/2,
                                                   childPoint.x*(elementWidth + 20) + elementWidth/2, childPoint.y*(elementHeight + 20) + elementHeight/2)
+//                                ctx.lineTo(childPoint.x*(elementWidth + 20) + elementWidth/2, childPoint.y*(elementHeight + 20) + elementHeight/2)
                             }
                         } else {
                             ctx.moveTo(point.x*(elementWidth + 20) + elementWidth/2, point.y*(elementHeight + 20) + elementHeight/2)
@@ -106,6 +84,50 @@ Item {
                 }
             }
         }
+        Column {
+            width: parent.width
+            spacing: 20
+            Repeater {
+                model: _handler.graph.points.length
+                Rectangle {
+                    width: parent.width
+                    height: innerItem.elementHeight
+                    color: textSelector.containsMouse ? "#9999ff" : "#009999ff"
+                    property QtObject graphPointData: _handler.graph.points[_handler.graph.points.length - model.index - 1]
+                    Text {
+                        anchors.fill: parent
+                        verticalAlignment: Text.AlignVCenter
+                        horizontalAlignment: Text.AlignRight
+                        text: graphPointData.sha1
+                        color: "e95f8cf36d49d7f5c084d8c17bc791170739917e" === graphPointData.sha1 ?
+                                   "red" : "black"
+                    }
+                    Row {
+                        anchors.left: parent.left
+                        Text {
+                            verticalAlignment: Text.AlignVCenter
+                            horizontalAlignment: Text.AlignLeft
+                            text: graphPointData.branch
+                        }
+                        Text {
+                            verticalAlignment: Text.AlignVCenter
+                            horizontalAlignment: Text.AlignLeft
+                            text: graphPointData.tag
+                        }
+                    }
+
+                    MouseArea {
+                        id: textSelector
+                        anchors.fill: parent
+                        hoverEnabled: true
+                        onClicked: {
+                            commitBody.visible = true
+                            commitBodyText.text = _handler.graph.commitData(graphPointData)
+                        }
+                    }
+                }
+            }
+        }
     }
 
     FileDialog {
@@ -117,4 +139,24 @@ Item {
             _handler.open(repoOpenDialog.fileUrl)
         }
     }
+
+    Rectangle {
+        id: commitBody
+        anchors.left: parent.left
+        anchors.top: parent.top
+        anchors.bottom: parent.bottom
+        width: parent.width/2
+        color: "white"
+        visible: false
+        MouseArea {
+            anchors.fill: parent
+            onClicked: {
+                commitBody.visible = false;
+            }
+        }
+        Text {
+            id: commitBodyText
+            anchors.fill: parent
+        }
+    }
 }

+ 5 - 0
tagmodel.cpp

@@ -0,0 +1,5 @@
+#include "tagmodel.h"
+
+TagModel::TagModel(QObject* parent) : UniversalListModel(parent)
+{
+}

+ 13 - 0
tagmodel.h

@@ -0,0 +1,13 @@
+#ifndef TAGMODEL_H
+#define TAGMODEL_H
+
+#include <gittag.h>
+#include <universallistmodel.h>
+
+class TagModel : UniversalListModel<GitTag>
+{
+public:
+    TagModel(QObject* parent = 0);
+};
+
+#endif // TAGMODEL_H