diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp index ef03874bfae07c3c7711f6033e1a0cd5d87a43ac..f4cc6c8654230b851ebb9d95497efcb25506f1a0 100644 --- a/src/jamidht/conversation.cpp +++ b/src/jamidht/conversation.cpp @@ -993,17 +993,35 @@ Conversation::Impl::pull() cb(false); continue; } - auto oldId = repo->getHead(); + auto oldHead = repo->getHead(); + std::string newHead = oldHead; std::unique_lock<std::mutex> lk(writeMtx_); - auto newCommits = mergeHistory(deviceId); - announce(newCommits); + auto commits = mergeHistory(deviceId); + if (!commits.empty()) { + newHead = commits.rbegin()->at("id"); + // Note: Because clients needs to linearize the history, they need to know all commits + // that can be updated. + // In this case, all commits until the common merge base should be announced. + // The client ill need to update it's model after this. + std::string mergeBase = oldHead; // If fast-forward, the merge base is the previous head + auto newHeadCommit = repo->getCommit(newHead); + if (newHeadCommit != std::nullopt && newHeadCommit->parents.size() > 1) { + mergeBase = repo->mergeBase(newHeadCommit->parents[0], newHeadCommit->parents[1]); + auto updatedCommits = loadMessages("", mergeBase); + // We announce commits from oldest to update to newest. This generally avoid + // to get detached commits until they are all announced. + std::reverse(std::begin(updatedCommits), std::end(updatedCommits)); + announce(updatedCommits); + } else { + announce(commits); + } + } lk.unlock(); if (cb) cb(true); // Announce if profile changed - auto newId = repo->getHead(); - if (oldId != newId) { - auto diffStats = repo->diffStats(newId, oldId); + if (oldHead != newHead) { + auto diffStats = repo->diffStats(newHead, oldHead); auto changedFiles = repo->changedFiles(diffStats); if (find(changedFiles.begin(), changedFiles.end(), "profile.vcf") != changedFiles.end()) { @@ -1050,9 +1068,7 @@ Conversation::generateInvitation() const Json::StreamWriterBuilder wbuilder; wbuilder["commentStyle"] = "None"; wbuilder["indentation"] = ""; - return { - {"application/invite+json", Json::writeString(wbuilder, root)} - }; + return {{"application/invite+json", Json::writeString(wbuilder, root)}}; } std::string diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp index 61c9d48e3f3e57595aecbe57114f05a4db4f3fa7..129420ddd2d954ce9fc3b80a135125324df65567 100644 --- a/src/jamidht/conversationrepository.cpp +++ b/src/jamidht/conversationrepository.cpp @@ -42,11 +42,15 @@ constexpr size_t MAX_FETCH_SIZE {256 * 1024 * 1024}; // 256Mb namespace jami { -inline std::string_view as_view(const git_blob* blob) { +inline std::string_view +as_view(const git_blob* blob) +{ return std::string_view(static_cast<const char*>(git_blob_rawcontent(blob)), - git_blob_rawsize(blob)); + git_blob_rawsize(blob)); } -inline std::string_view as_view(const GitObject& blob) { +inline std::string_view +as_view(const GitObject& blob) +{ return as_view(reinterpret_cast<git_blob*>(blob.get())); } @@ -228,7 +232,8 @@ public: const std::string& userUri, std::string_view oldCert = ""sv) const { - auto cert = dht::crypto::Certificate((const uint8_t*)certContent.data(), certContent.size()); + auto cert = dht::crypto::Certificate((const uint8_t*) certContent.data(), + certContent.size()); auto isDeviceCertificate = cert.getId().toString() != userUri; auto issuerUid = cert.getIssuerUID(); if (isDeviceCertificate && issuerUid.empty()) { @@ -236,7 +241,8 @@ public: JAMI_ERR("Empty issuer for %s", cert.getId().to_c_str()); } if (!oldCert.empty()) { - auto deviceCert = dht::crypto::Certificate((const uint8_t*)oldCert.data(), oldCert.size()); + auto deviceCert = dht::crypto::Certificate((const uint8_t*) oldCert.data(), + oldCert.size()); if (isDeviceCertificate) { if (issuerUid != deviceCert.getIssuerUID()) { // NOTE: Here, because JAMS certificate can be incorrectly formatted, there is @@ -495,7 +501,8 @@ initial_commit(GitRepository& repo, return {}; } - std::string signed_str = base64::encode(account->identity().first->sign((const uint8_t*)to_sign.ptr, to_sign.size)); + std::string signed_str = base64::encode( + account->identity().first->sign((const uint8_t*) to_sign.ptr, to_sign.size)); // git commit -S if (git_commit_create_with_signature(&commit_id, @@ -898,7 +905,8 @@ ConversationRepository::Impl::checkVote(const std::string& userDevice, } // Check votedFile path - static const std::regex regex_votes("votes.(\\w+).(members|devices|admins|invited).(\\w+).(\\w+)"); + static const std::regex regex_votes( + "votes.(\\w+).(members|devices|admins|invited).(\\w+).(\\w+)"); std::svmatch base_match; if (!std::regex_match(votedFile, base_match, regex_votes) or base_match.size() != 5) { JAMI_WARN("Invalid votes path: %s", votedFile.c_str()); @@ -2910,6 +2918,20 @@ ConversationRepository::merge(const std::string& merge_id, bool force) return {!result.empty(), result}; } +std::string +ConversationRepository::mergeBase(const std::string& from, const std::string& to) const +{ + if (auto repo = pimpl_->repository()) { + git_oid oid, oidFrom, oidMerge; + git_oid_fromstr(&oidFrom, from.c_str()); + git_oid_fromstr(&oid, to.c_str()); + git_merge_base(&oidMerge, repo.get(), &oid, &oidFrom); + if (auto* commit_str = git_oid_tostr_s(&oidMerge)) + return commit_str; + } + return {}; +} + std::string ConversationRepository::diffStats(const std::string& newId, const std::string& oldId) const { diff --git a/src/jamidht/conversationrepository.h b/src/jamidht/conversationrepository.h index ec1420d2150c83d67241279848489df7621f05ac..7d66e9b290a8f17f4f822a81374839a6e1a27545 100644 --- a/src/jamidht/conversationrepository.h +++ b/src/jamidht/conversationrepository.h @@ -220,6 +220,14 @@ public: */ std::pair<bool, std::string> merge(const std::string& merge_id, bool force = false); + /** + * Get the common parent between two branches + * @param from The first branch + * @param to The second branch + * @return the common parent + */ + std::string mergeBase(const std::string& from, const std::string& to) const; + /** * Get current diff stats between two commits * @param oldId Old commit