diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp index 8a2120b044e7718fb9e56653e6fc9a61a9acf836..83a98283df3e170a11b6a2d9cce2c7102345a6df 100644 --- a/src/jamidht/conversation.cpp +++ b/src/jamidht/conversation.cpp @@ -244,8 +244,10 @@ Conversation::Impl::convCommitToMap(const std::vector<ConversationCommit>& commi JAMI_WARN("%s", err.c_str()); } } + auto linearizedParent = repository_->linearizedParent(commit.id); message["id"] = commit.id; message["parents"] = parents; + message["linearizedParent"] = linearizedParent ? *linearizedParent : ""; message["author"] = authorId; message["type"] = type; message["timestamp"] = std::to_string(commit.timestamp); diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp index e86e549d45e335c58ba4e31b71db89dff7c45a41..5607c9a04738cbcf3e07aa8c03dbdd9df9b21dde 100644 --- a/src/jamidht/conversationrepository.cpp +++ b/src/jamidht/conversationrepository.cpp @@ -108,6 +108,7 @@ public: std::vector<ConversationCommit> log(const std::string& from, const std::string& to, unsigned n) const; + std::optional<std::string> linearizedParent(const std::string& commitId) const; GitObject fileAtTree(const std::string& path, const GitTree& tree) const; // NOTE! GitDiff needs to be deteleted before repo @@ -715,7 +716,8 @@ ConversationRepository::Impl::checkVote(const std::string& userDevice, return false; } auto* blob = reinterpret_cast<git_blob*>(vote.get()); - auto voteContent = std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), git_blob_rawsize(blob)); + auto voteContent = std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), + git_blob_rawsize(blob)); if (!voteContent.empty()) { JAMI_ERR("Vote file not empty: %s", votedFile.c_str()); return false; @@ -823,7 +825,8 @@ ConversationRepository::Impl::checkValidAdd(const std::string& userDevice, } auto* blob = reinterpret_cast<git_blob*>(blob_invite.get()); - auto invitation = std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), git_blob_rawsize(blob)); + auto invitation = std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), + git_blob_rawsize(blob)); if (!invitation.empty()) { JAMI_ERR("Invitation not empty for commit %s", commitId.c_str()); return false; @@ -1100,9 +1103,11 @@ ConversationRepository::Impl::isValidUserAtCommit(const std::string& userDevice, // Check that certificate matches auto* blob = reinterpret_cast<git_blob*>(blob_device.get()); - auto deviceCert = std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), git_blob_rawsize(blob)); + auto deviceCert = std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), + git_blob_rawsize(blob)); blob = reinterpret_cast<git_blob*>(blob_parent.get()); - auto parentCert = std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), git_blob_rawsize(blob)); + auto parentCert = std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), + git_blob_rawsize(blob)); auto deviceCertStr = cert->toString(false); auto parentCertStr = cert->issuer->toString(true); @@ -1454,7 +1459,7 @@ ConversationRepository::Impl::log(const std::string& from, const std::string& to return commits; } GitRevWalker walker {walker_ptr, git_revwalk_free}; - git_revwalk_sorting(walker.get(), GIT_SORT_TOPOLOGICAL); + git_revwalk_sorting(walker.get(), GIT_SORT_TOPOLOGICAL | GIT_SORT_TIME); for (auto idx = 0u; !git_revwalk_next(&oid, walker.get()); ++idx) { if (n != 0 && idx == n) { @@ -1507,6 +1512,45 @@ ConversationRepository::Impl::log(const std::string& from, const std::string& to return commits; } +std::optional<std::string> +ConversationRepository::Impl::linearizedParent(const std::string& commitId) const +{ + git_oid oid; + auto repo = repository(); + if (!repo or git_reference_name_to_id(&oid, repo.get(), "HEAD") < 0) { + JAMI_ERR("Cannot get reference for HEAD"); + return std::nullopt; + } + + git_revwalk* walker_ptr = nullptr; + if (git_revwalk_new(&walker_ptr, repo.get()) < 0 || git_revwalk_push(walker_ptr, &oid) < 0) { + if (walker_ptr) + git_revwalk_free(walker_ptr); + // This fail can be ok in the case we check if a commit exists before pulling (so can fail + // there). only log if the fail is unwanted. + return std::nullopt; + } + GitRevWalker walker {walker_ptr, git_revwalk_free}; + git_revwalk_sorting(walker.get(), GIT_SORT_TOPOLOGICAL | GIT_SORT_TIME); + + auto ret = false; + for (auto idx = 0u; !git_revwalk_next(&oid, walker.get()); ++idx) { + git_commit* commit_ptr = nullptr; + std::string id = git_oid_tostr_s(&oid); + if (git_commit_lookup(&commit_ptr, repo.get(), &oid) < 0) { + JAMI_WARN("Failed to look up commit %s", id.c_str()); + break; + } + + if (ret) + return id; + if (id == commitId) + ret = true; + } + + return std::nullopt; +} + GitObject ConversationRepository::Impl::fileAtTree(const std::string& path, const GitTree& tree) const { @@ -2734,6 +2778,12 @@ ConversationRepository::validClone() const return pimpl_->validCommits(logN("", 0)); } +std::optional<std::string> +ConversationRepository::linearizedParent(const std::string& commitId) const +{ + return pimpl_->linearizedParent(commitId); +} + void ConversationRepository::removeBranchWith(const std::string& remoteDevice) { diff --git a/src/jamidht/conversationrepository.h b/src/jamidht/conversationrepository.h index 71457148a6417e153ccfe77b530ba393c6383b21..2d368db6f11f8e1690f41bd69235b8b6177cbfe3 100644 --- a/src/jamidht/conversationrepository.h +++ b/src/jamidht/conversationrepository.h @@ -191,6 +191,12 @@ public: const std::string& to = "") const; std::optional<ConversationCommit> getCommit(const std::string& commitId) const; + /** + * Get parent via topological + date sort in branch main of a commit + * @param commitId id to choice + */ + std::optional<std::string> linearizedParent(const std::string& commitId) const; + /** * Merge another branch into the main branch * @param merge_id The reference to merge