Browse Source

Make GitBase movable and refactored GitBranches

- GitBase is movable now
- GitBaseOid->GitReference->GitBranch uses move semantic to keep valid pointers to internal objects
- Changed coding stype to Qt-like
- Added branches upstream functions(Not completely tested yet)
Alexey Edelev 5 years ago
parent
commit
10e3ae91bd
10 changed files with 194 additions and 32 deletions
  1. 2 0
      CuteGit.pro
  2. 35 9
      gitbase.h
  3. 19 4
      gitbaseoid.h
  4. 56 0
      gitbranch.cpp
  5. 11 3
      gitbranch.h
  6. 1 0
      githandler.cpp
  7. 29 9
      gitreference.cpp
  8. 3 1
      gitreference.h
  9. 34 6
      gitrepository.cpp
  10. 4 0
      gitrepository.h

+ 2 - 0
CuteGit.pro

@@ -1,5 +1,7 @@
 QT += qml quick widgets concurrent
 
+QMAKE_CXXFLAGS = -std=c++14
+
 TARGET = CuteGit
 
 TEMPLATE = app

+ 35 - 9
gitbase.h

@@ -5,6 +5,7 @@
 
 #include <QObject>
 #include <git2/types.h>
+#include <git2/errors.h>
 
 class GitRepository;
 
@@ -12,12 +13,7 @@ template <typename T>
 class GitBase : public QObject
 {
 public:
-    GitBase(T* raw, GitRepository* parent) : QObject()
-      ,m_raw(raw)
-      ,m_repository(parent)
-    {}
-
-    T* raw() const {
+    T *raw() const {
         return m_raw;
     }
 
@@ -25,13 +21,43 @@ public:
         return m_raw != nullptr;
     }
 
-    GitRepository* repository() const {
+    GitRepository *repository() const {
         return m_repository;
     }
 
+    static QString lastError() {
+        const git_error *e = giterr_last();
+        if(e) {
+            return QString("(%1): %2").arg(e->klass).arg(e->message);
+            giterr_clear();
+        }
+        giterr_clear();
+        return QString();
+    }
+
 protected:
-    T* m_raw;
-    GitRepository* m_repository;
+    GitBase(T *raw, GitRepository *parent) : QObject()
+      ,m_raw(raw)
+      ,m_repository(parent)
+    {}
+
+    GitBase(GitBase &&other) {
+        //WARNING: m_raw should be moved in inherited class
+        m_repository = other.m_repository;
+        other.m_repository = nullptr;
+    }
+
+    GitBase &operator=(GitBase &&other) {
+        if (&other != this) {
+            //WARNING: m_raw should be moved in inherited class
+            m_repository = other.m_repository;
+            other.m_repository = nullptr;
+        }
+        return *this;
+    }
+
+    T *m_raw;
+    GitRepository *m_repository;
 };
 
 #endif // GITBASE_H

+ 19 - 4
gitbaseoid.h

@@ -10,14 +10,29 @@ template<typename T>
 class GitBaseOid : public GitBase<T>
 {
 public:
-    GitBaseOid(T* raw, GitRepository* parent) : GitBase<T>(raw, parent)
+    const GitOid &oid() const {
+        return m_oid;
+    }
+
+protected:
+    GitBaseOid(T *raw, GitRepository *parent) : GitBase<T>(raw, parent)
       ,m_oid(nullptr, parent)
     {}
 
-    const GitOid& oid() const {
-        return m_oid;
+    GitBaseOid(GitBaseOid &&other) : GitBase<T>(std::move(other))
+    {
+        m_oid = other.m_oid;
+        other.m_oid = GitOid();
     }
-protected:
+
+    GitBaseOid &operator=(GitBaseOid &&other) {
+        if (&other != this) {
+            m_oid = other.m_oid;
+            other.m_oid = GitOid();
+        }
+        return static_cast<GitBaseOid&>(GitBase<T>::operator=(std::move(other)));
+    }
+
     GitOid m_oid;
 };
 

+ 56 - 0
gitbranch.cpp

@@ -9,6 +9,12 @@ const char remoteAddtion = '/';
 
 }
 
+GitBranch::GitBranch() : GitReference(nullptr, nullptr)
+  ,m_commit(nullptr)
+  ,m_type(Invalid)
+{
+}
+
 GitBranch::GitBranch(git_reference *ref, git_branch_t type, GitRepository *parent) : GitReference(ref, parent)
   ,m_commit(nullptr)
   ,m_type(static_cast<BranchType>(type))
@@ -28,10 +34,36 @@ GitBranch::GitBranch(git_reference *ref, git_branch_t type, GitRepository *paren
     }
 }
 
+GitBranch::GitBranch(GitBranch&& other) : GitReference(std::move(other))
+{
+    //TODO: looks like free() functionality might be more common for GitBase childred.
+    //Need to think about it
+    if(m_commit != nullptr) {
+        git_annotated_commit_free(m_commit);
+        m_commit = nullptr;
+    }
+    m_commit = other.m_commit;
+    other.m_commit = nullptr;
+}
+
+GitBranch &GitBranch::operator=(GitBranch&& other)
+{
+    if(&other != this) {
+        if(m_commit != nullptr) {
+            git_annotated_commit_free(m_commit);
+            m_commit = nullptr;
+        }
+        m_commit = other.m_commit;
+        other.m_commit = nullptr;
+    }
+    return static_cast<GitBranch&>(GitReference::operator=(std::move(other)));
+}
+
 GitBranch::~GitBranch()
 {
     if(m_commit != nullptr) {
         git_annotated_commit_free(m_commit);
+        m_commit = nullptr;
     }
 }
 
@@ -39,3 +71,27 @@ GitBranch::BranchType GitBranch::type() const
 {
     return m_type;
 }
+
+void GitBranch::setUpstream(const GitBranch& branch)
+{
+    if(type() == GIT_BRANCH_REMOTE
+            || branch.type() == GIT_BRANCH_LOCAL) {
+        qWarning() << "Try to setup invalid pair of upstream/local branches";
+        return;
+    }
+    git_branch_set_upstream(branch.raw(), branch.fullName().toUtf8().data());
+}
+
+GitBranch GitBranch::upstream() const
+{
+    git_reference* ref = nullptr;
+    if(type() == GIT_BRANCH_REMOTE) {
+        qDebug() << "Skipping upstream read for remote branch";
+        return GitBranch();
+    }
+    if(git_branch_upstream(&ref, raw()) != 0) {
+        qWarning() << "Invalid reference or branch type" << GitBase::lastError();
+        return GitBranch();
+    }
+    return GitBranch(ref, GIT_BRANCH_REMOTE, repository());
+}

+ 11 - 3
gitbranch.h

@@ -18,11 +18,15 @@ class GitBranch : public GitReference
 
 public:
     enum BranchType {
+        Invalid = 0,
         Local = GIT_BRANCH_LOCAL,
         Remote = GIT_BRANCH_REMOTE
     };
 
-    GitBranch(git_reference* ref, git_branch_t type, GitRepository* parent);
+    GitBranch(git_reference *ref, git_branch_t type, GitRepository *parent);
+    GitBranch(GitBranch &&other);
+    GitBranch &operator=(GitBranch &&other);
+
     virtual ~GitBranch();
 
     BranchType type() const;
@@ -35,7 +39,7 @@ public:
         return m_fullName;
     }
 
-    git_annotated_commit* annontatedCommit()
+    git_annotated_commit *annotatedCommit()
     {
         return m_commit;
     }
@@ -45,6 +49,10 @@ public:
         return m_remote;
     }
 
+    GitBranch upstream() const;
+
+    void setUpstream(const GitBranch &branch);
+
 public slots:
     void setName(QString name) {
         if (m_name == name)
@@ -66,7 +74,7 @@ private:
     GitBranch();
     Q_DISABLE_COPY(GitBranch)
 
-    git_annotated_commit* m_commit;
+    git_annotated_commit *m_commit;
     BranchType m_type;
     QString m_name;
     QString m_fullName;

+ 1 - 0
githandler.cpp

@@ -215,6 +215,7 @@ void GitHandler::pull(PullStrategy strategy) const
     }
 
     remote->fetch();
+//    git_merge_analysis();
 }
 
 void GitHandler::updateModels()

+ 29 - 9
gitreference.cpp

@@ -7,7 +7,7 @@
 GitReference::GitReference(git_reference *ref, GitRepository *parent) : GitBaseOid(nullptr, parent)
   ,m_namespace(Invalid)
 {
-    if(ref == nullptr) {
+    if (ref == nullptr) {
         qDebug() << "Null reference ptr";
         return;
     }
@@ -15,38 +15,59 @@ GitReference::GitReference(git_reference *ref, GitRepository *parent) : GitBaseO
     m_raw = ref;
 
     detectNamespace();
-    if(m_namespace == Invalid) {
+    if (m_namespace == Invalid) {
         qDebug() << "Namespace for reference is invalid";
         free();
         return;
     }
 }
 
+GitReference::GitReference(GitReference &&other) : GitBaseOid(std::move(other))
+{
+    free();
+    m_raw = other.m_raw;
+    m_namespace = other.m_namespace;
+    other.m_raw = nullptr;
+    other.m_namespace = Invalid;
+}
+
+GitReference& GitReference::operator=(GitReference &&other)
+{
+    if (&other != this) {
+        free();
+        m_raw = other.m_raw;
+        m_namespace = other.m_namespace;
+        other.m_raw = nullptr;
+        other.m_namespace = Invalid;
+    }
+    return static_cast<GitReference&>(GitBaseOid::operator=(std::move(other)));
+}
+
 void GitReference::detectNamespace()
 {
-    if(m_raw == nullptr) {
+    if (m_raw == nullptr) {
         return;
     }
 
-    if(git_reference_is_branch(m_raw)) {
+    if (git_reference_is_branch(m_raw)) {
         m_namespace = Branch;
         qDebug() << "Reference namespace Branch";
         return;
     }
 
-    if(git_reference_is_tag(m_raw)) {
+    if (git_reference_is_tag(m_raw)) {
         m_namespace = Tag;
         qDebug() << "Reference namespace Tag";
         return;
     }
 
-    if(git_reference_is_note(m_raw)) {
+    if (git_reference_is_note(m_raw)) {
         m_namespace = Note;
         qDebug() << "Reference namespace Note";
         return;
     }
 
-    if(git_reference_is_remote(m_raw)) {
+    if (git_reference_is_remote(m_raw)) {
         m_namespace = Remote;
         qDebug() << "Reference namespace Remote";
         return;
@@ -60,10 +81,9 @@ GitReference::~GitReference()
 
 void GitReference::free()
 {
-    if(m_raw != nullptr) {
+    if (m_raw != nullptr) {
         git_reference_free(m_raw);
     }
-
     m_raw = nullptr;
 }
 

+ 3 - 1
gitreference.h

@@ -31,7 +31,9 @@ public:
     QString refName() const;
 
 protected:
-    GitReference(git_reference* ref, GitRepository* parent);
+    GitReference(git_reference *ref, GitRepository *parent);
+    GitReference(GitReference &&ref);
+    GitReference &operator=(GitReference &&ref);
     void free();
 
     ReferenceNamespace m_namespace;

+ 34 - 6
gitrepository.cpp

@@ -25,6 +25,7 @@ GitRepository::GitRepository(const QString& root) : QObject(nullptr)
     m_path = git_repository_workdir(m_raw);
     m_name = m_path.split("/", QString::SkipEmptyParts).last();
     qDebug() << "New repo:" << m_name << m_root << m_path;
+
     readBranches();
     readTags();
     readRemotes();
@@ -93,6 +94,7 @@ void GitRepository::readRemotes()
             m_remotes.insert(remote->name(), QPointer<GitRemote>(remote));
         }
     }
+//TODO: Why this code was left?
 //    git_reference_foreach_glob(raw(),"refs/remotes/*", [](const char *name, void *payload) -> int
 //    {
 //        qDebug() << "Remotes: " << name;
@@ -108,7 +110,7 @@ void GitRepository::readRemotes()
 void GitRepository::checkout(QObject* object)
 {
     git_checkout_options opts;
-    git_checkout_init_options(&opts,GIT_CHECKOUT_OPTIONS_VERSION);
+    git_checkout_init_options(&opts, GIT_CHECKOUT_OPTIONS_VERSION);
 
     opts.checkout_strategy = GIT_CHECKOUT_FORCE;//TODO: modify to merge policy
     opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
@@ -152,14 +154,14 @@ void GitRepository::checkout(QObject* object)
         commit = GitCommit::fromOid(branch->oid());
         qDebug() << "Checkout branch: " << git_checkout_tree(raw(), (git_object*)(commit->raw()), &opts);
         switch(branch->type()) {
-            case GitBranch::Local:
-                break;
+        case GitBranch::Local:
+            break;
         case GitBranch::Remote:
             git_reference* ref;
             //TODO: better to create API for local branch creation
-            if(0 == git_branch_create_from_annotated(&ref, raw(), branch->name().toUtf8().data(), branch->annontatedCommit(), 0)) {
+            if(0 == git_branch_create_from_annotated(&ref, raw(), branch->name().toUtf8().data(), branch->annotatedCommit(), 0)) {
                 branch = new GitBranch(ref, GIT_BRANCH_LOCAL, this);
-                git_branch_set_upstream(ref, branch->fullName().toUtf8().data());
+                branch->setUpstream(*branch);
                 m_branches.insert(branch->fullName(), QPointer<GitBranch>(branch));
                 emit branchesChanged();
             } else if(m_branches.contains(branch->name())) { /*
@@ -200,8 +202,34 @@ void GitRepository::updateHead()
     qDebug() << "Repo head" << m_head.toString();
 }
 
-
 QString GitRepository::id() const
 {
     return QCryptographicHash::hash(name().toUtf8() + path().toUtf8(), QCryptographicHash::Sha1).toHex();
 }
+
+#if 0 //Test functionality
+void GitRepository::testReadBranches()
+{
+    QList<GitBranch*> m_branches;
+    git_reference *branchRef;
+    git_branch_t branchType;
+    git_branch_iterator* iter;
+    git_branch_iterator_new(&iter, m_raw, GIT_BRANCH_ALL);
+    while(git_branch_next(&branchRef, &branchType, iter) == 0) {
+        GitBranch* branch = new GitBranch(branchRef, branchType, this);
+        qDebug() << branch->fullName();
+        qDebug() << branch->type();
+        GitBranch upstreamBranch = std::move(branch->upstream());
+        m_branches.append(branch);
+//        qDebug() << upstreamBranch.fullName();
+    }
+
+    for(auto branch : m_branches)
+    {
+        GitBranch branchMoved(std::move(*branch));
+        delete branch;
+        GitBranch upstream = branchMoved.upstream();
+        qDebug() << "valid upstream:" << upstream.isValid();
+    }
+}
+#endif

+ 4 - 0
gitrepository.h

@@ -130,6 +130,10 @@ private:
     TagContainer m_tags;
     RemoteContainer m_remotes;
     GitOid m_head;
+#if 0 //Test functionality
+public:
+    void testReadBranches();
+#endif
 };
 
 #endif // GITREPOSITORY_H