diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp
index 089c15107e9149d600948e33543d2fe9bc8d466a..8f6b94408146c9403e3611e4e517459ab8b8169c 100644
--- a/src/jamidht/conversationrepository.cpp
+++ b/src/jamidht/conversationrepository.cpp
@@ -26,7 +26,6 @@
 #include <opendht/rng.h>
 using random_device = dht::crypto::random_device;
 
-
 #include <ctime>
 #include <fstream>
 
@@ -54,6 +53,10 @@ public:
     }
     ~Impl() = default;
 
+    GitSignature signature();
+    bool mergeFastforward(const git_oid* target_oid, int is_unborn);
+    bool createMergeCommit(git_index* index, const std::string& wanted_ref);
+
     std::weak_ptr<JamiAccount> account_;
     const std::string id_;
     GitRepository repository_ {nullptr, git_repository_free};
@@ -70,7 +73,10 @@ GitRepository
 create_empty_repository(const std::string& path)
 {
     git_repository* repo = nullptr;
-    if (git_repository_init(&repo, path.c_str(), false /* we want a non-bare repo to work on it */)
+    git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+    opts.flags |= GIT_REPOSITORY_INIT_MKPATH;
+    opts.initial_head = "main";
+    if (git_repository_init_ext(&repo, path.c_str(), &opts)
         < 0) {
         JAMI_ERR("Couldn't create a git repository in %s", path.c_str());
     }
@@ -246,11 +252,11 @@ initial_commit(GitRepository& repo, const std::shared_ptr<JamiAccount>& account)
         return {};
     }
 
-    // Move commit to master branch
+    // Move commit to main branch
     git_commit* commit = nullptr;
     if (git_commit_lookup(&commit, repo.get(), &commit_id) == 0) {
         git_reference* ref = nullptr;
-        git_branch_create(&ref, repo.get(), "master", commit, true);
+        git_branch_create(&ref, repo.get(), "main", commit, true);
         git_commit_free(commit);
         git_reference_free(ref);
     }
@@ -261,6 +267,219 @@ initial_commit(GitRepository& repo, const std::shared_ptr<JamiAccount>& account)
     return {};
 }
 
+//////////////////////////////////
+
+GitSignature
+ConversationRepository::Impl::signature()
+{
+    auto account = account_.lock();
+    if (!account)
+        return {nullptr, git_signature_free};
+    auto deviceId = std::string(account->currentDeviceId());
+    auto name = account->getDisplayName();
+    if (name.empty())
+        name = deviceId;
+
+    git_signature* sig_ptr = nullptr;
+    // Sign commit's buffer
+    if (git_signature_new(&sig_ptr, name.c_str(), deviceId.c_str(), std::time(nullptr), 0) < 0) {
+        JAMI_ERR("Unable to create a commit signature.");
+        return {nullptr, git_signature_free};
+    }
+    GitSignature sig {sig_ptr, git_signature_free};
+    return std::move(sig);
+}
+
+bool
+ConversationRepository::Impl::createMergeCommit(git_index* index, const std::string& wanted_ref)
+{
+    // The merge will occur between current HEAD and wanted_ref
+    git_reference* head_ref_ptr = nullptr;
+    if (git_repository_head(&head_ref_ptr, repository_.get()) < 0) {
+        JAMI_ERR("Could not get HEAD reference");
+        return false;
+    }
+    GitReference head_ref {head_ref_ptr, git_reference_free};
+
+    // Maybe that's a ref, so DWIM it
+    git_reference* merge_ref_ptr = nullptr;
+    git_reference_dwim(&merge_ref_ptr, repository_.get(), wanted_ref.c_str());
+    GitReference merge_ref {merge_ref_ptr, git_reference_free};
+
+    GitSignature sig {signature()};
+
+    // Prepare a standard merge commit message
+    const char* msg_target = nullptr;
+    if (merge_ref) {
+        git_branch_name(&msg_target, merge_ref.get());
+    } else {
+        msg_target = wanted_ref.c_str();
+    }
+
+    std::stringstream stream;
+    stream << "Merge " << (merge_ref ? "branch" : "commit") << " '" << msg_target << "'";
+
+    // Setup our parent commits
+    GitCommit parents[2] {{nullptr, git_commit_free}, {nullptr, git_commit_free}};
+    git_commit* parent = nullptr;
+    if (git_reference_peel((git_object**) &parent, head_ref.get(), GIT_OBJ_COMMIT) < 0) {
+        JAMI_ERR("Could not peel HEAD reference");
+        return false;
+    }
+    parents[0] = {parent, git_commit_free};
+    git_oid commit_id;
+    if (git_oid_fromstr(&commit_id, wanted_ref.c_str()) < 0) {
+        return false;
+    }
+    git_annotated_commit* annotated_ptr = nullptr;
+    if (git_annotated_commit_lookup(&annotated_ptr, repository_.get(), &commit_id) < 0) {
+        JAMI_ERR("Couldn't lookup commit %s", wanted_ref.c_str());
+        return false;
+    }
+    GitAnnotatedCommit annotated {annotated_ptr, git_annotated_commit_free};
+    if (git_commit_lookup(&parent, repository_.get(), git_annotated_commit_id(annotated.get()))
+        < 0) {
+        JAMI_ERR("Couldn't lookup commit %s", wanted_ref.c_str());
+        return false;
+    }
+    parents[1] = {parent, git_commit_free};
+
+    // Prepare our commit tree
+    git_oid tree_oid;
+    git_tree* tree = nullptr;
+    if (git_index_write_tree(&tree_oid, index) < 0) {
+        JAMI_ERR("Couldn't write index");
+        return false;
+    }
+    if (git_tree_lookup(&tree, repository_.get(), &tree_oid) < 0) {
+        JAMI_ERR("Couldn't lookup tree");
+        return false;
+    }
+
+    // Commit
+    git_buf to_sign = {};
+    const git_commit* parents_ptr[2] {parents[0].get(), parents[1].get()};
+    if (git_commit_create_buffer(&to_sign,
+                                 repository_.get(),
+                                 sig.get(),
+                                 sig.get(),
+                                 nullptr,
+                                 stream.str().c_str(),
+                                 tree,
+                                 2,
+                                 &parents_ptr[0])
+        < 0) {
+        JAMI_ERR("Could not create commit buffer");
+        return false;
+    }
+
+    auto account = account_.lock();
+    if (!account)
+        false;
+    // git commit -S
+    auto to_sign_vec = std::vector<uint8_t>(to_sign.ptr, to_sign.ptr + to_sign.size);
+    auto signed_buf = account->identity().first->sign(to_sign_vec);
+    std::string signed_str = base64::encode(signed_buf);
+    git_oid commit_oid;
+    if (git_commit_create_with_signature(&commit_oid,
+                                         repository_.get(),
+                                         to_sign.ptr,
+                                         signed_str.c_str(),
+                                         "signature")
+        < 0) {
+        JAMI_ERR("Could not sign commit");
+        return false;
+    }
+
+    auto commit_str = git_oid_tostr_s(&commit_oid);
+    if (commit_str) {
+        JAMI_INFO("New merge commit added with id: %s", commit_str);
+        // Move commit to main branch
+        git_reference* ref_ptr = nullptr;
+        if (git_reference_create(&ref_ptr,
+                                 repository_.get(),
+                                 "refs/heads/main",
+                                 &commit_oid,
+                                 true,
+                                 nullptr)
+            < 0) {
+            JAMI_WARN("Could not move commit to main");
+        }
+        git_reference_free(ref_ptr);
+    }
+
+    // We're done merging, cleanup the repository state
+    git_repository_state_cleanup(repository_.get());
+
+    return true;
+}
+
+bool
+ConversationRepository::Impl::mergeFastforward(const git_oid* target_oid, int is_unborn)
+{
+    // Initialize target
+    git_reference* target_ref_ptr = nullptr;
+    if (is_unborn) {
+        git_reference* head_ref_ptr = nullptr;
+        // HEAD reference is unborn, lookup manually so we don't try to resolve it
+        if (git_reference_lookup(&head_ref_ptr, repository_.get(), "HEAD") < 0) {
+            JAMI_ERR("failed to lookup HEAD ref");
+            return false;
+        }
+        GitReference head_ref {head_ref_ptr, git_reference_free};
+
+        // Grab the reference HEAD should be pointing to
+        const auto* symbolic_ref = git_reference_symbolic_target(head_ref.get());
+
+        // Create our main reference on the target OID
+        if (git_reference_create(&target_ref_ptr,
+                                 repository_.get(),
+                                 symbolic_ref,
+                                 target_oid,
+                                 0,
+                                 nullptr)
+            < 0) {
+            JAMI_ERR("failed to create main reference");
+            return false;
+        }
+
+    } else if (git_repository_head(&target_ref_ptr, repository_.get()) < 0) {
+        // HEAD exists, just lookup and resolve
+        JAMI_ERR("failed to get HEAD reference");
+        return false;
+    }
+    GitReference target_ref {target_ref_ptr, git_reference_free};
+
+    // Lookup the target object
+    git_object* target_ptr = nullptr;
+    if (git_object_lookup(&target_ptr, repository_.get(), target_oid, GIT_OBJ_COMMIT) != 0) {
+        JAMI_ERR("failed to lookup OID %s", git_oid_tostr_s(target_oid));
+        return false;
+    }
+    GitObject target {target_ptr, git_object_free};
+
+    // Checkout the result so the workdir is in the expected state
+    git_checkout_options ff_checkout_options;
+    git_checkout_init_options(&ff_checkout_options, GIT_CHECKOUT_OPTIONS_VERSION);
+    ff_checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
+    if (git_checkout_tree(repository_.get(), target.get(), &ff_checkout_options) != 0) {
+        JAMI_ERR("failed to checkout HEAD reference");
+        return false;
+    }
+
+    // Move the target reference to the target OID
+    git_reference* new_target_ref;
+    if (git_reference_set_target(&new_target_ref, target_ref.get(), target_oid, nullptr) < 0) {
+        JAMI_ERR("failed to move HEAD reference");
+        return false;
+    }
+    git_reference_free(new_target_ref);
+
+    return 0;
+}
+
+//////////////////////////////////
+
 std::unique_ptr<ConversationRepository>
 ConversationRepository::createConversation(const std::weak_ptr<JamiAccount>& account)
 {
@@ -612,4 +831,96 @@ ConversationRepository::log(const std::string& last, unsigned n)
     return commits;
 }
 
+bool
+ConversationRepository::merge(const std::string& merge_id)
+{
+    // First, the repository must be in a clean state
+    int state = git_repository_state(pimpl_->repository_.get());
+    if (state != GIT_REPOSITORY_STATE_NONE) {
+        JAMI_ERR("Merge operation aborted: repository is in unexpected state %d", state);
+        return false;
+    }
+    // Checkout main (to do a `git_merge branch`)
+    if (git_repository_set_head(pimpl_->repository_.get(), "refs/heads/main") < 0) {
+        JAMI_ERR("Merge operation aborted: couldn't checkout main branch");
+        return false;
+    }
+
+    // Then check that merge_id exists
+    git_oid commit_id;
+    if (git_oid_fromstr(&commit_id, merge_id.c_str()) < 0) {
+        JAMI_ERR("Merge operation aborted: couldn't lookup commit %s", merge_id.c_str());
+        return false;
+    }
+    git_annotated_commit* annotated_ptr = nullptr;
+    if (git_annotated_commit_lookup(&annotated_ptr, pimpl_->repository_.get(), &commit_id) < 0) {
+        JAMI_ERR("Merge operation aborted: couldn't lookup commit %s", merge_id.c_str());
+        return false;
+    }
+    GitAnnotatedCommit annotated {annotated_ptr, git_annotated_commit_free};
+
+    // Now, we can analyze which type of merge do we need
+    git_merge_analysis_t analysis;
+    git_merge_preference_t preference;
+    const git_annotated_commit* const_annotated = annotated.get();
+    if (git_merge_analysis(&analysis, &preference, pimpl_->repository_.get(), &const_annotated, 1)
+        < 0) {
+        JAMI_ERR("Merge operation aborted: repository analysis failed");
+        return false;
+    }
+
+    if (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) {
+        JAMI_INFO("Already up-to-date");
+        return true;
+    } else if (analysis & GIT_MERGE_ANALYSIS_UNBORN
+               || (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD
+                   && !(preference & GIT_MERGE_PREFERENCE_NO_FASTFORWARD))) {
+        if (analysis & GIT_MERGE_ANALYSIS_UNBORN)
+            JAMI_INFO("Merge analysis result: Unborn");
+        else
+            JAMI_INFO("Merge analysis result: Fast-forward");
+        const auto* target_oid = git_annotated_commit_id(annotated.get());
+
+        if (pimpl_->mergeFastforward(target_oid, (analysis & GIT_MERGE_ANALYSIS_UNBORN)) < 0) {
+            const git_error* err = giterr_last();
+            if (err)
+                JAMI_ERR("Fast forward merge failed: %s", err->message);
+            return false;
+        }
+        return true;
+    } else if (analysis & GIT_MERGE_ANALYSIS_NORMAL) {
+        git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+        merge_opts.file_flags = GIT_MERGE_FILE_STYLE_DIFF3;
+        git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+        checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+        if (preference & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY) {
+            JAMI_ERR("Fast-forward is preferred, but only a merge is possible");
+            return false;
+        }
+
+        if (git_merge(pimpl_->repository_.get(), &const_annotated, 1, &merge_opts, &checkout_opts)
+            < 0) {
+            const git_error* err = giterr_last();
+            if (err)
+                JAMI_ERR("Git merge failed: %s", err->message);
+            return false;
+        }
+    }
+
+    git_index* index_ptr = nullptr;
+    if (git_repository_index(&index_ptr, pimpl_->repository_.get()) < 0) {
+        JAMI_ERR("Merge operation aborted: could not open repository index");
+        return false;
+    }
+    GitIndex index {index_ptr, git_index_free};
+    if (git_index_has_conflicts(index.get())) {
+        JAMI_WARN("Merge operation aborted: the merge operation resulted in some conflicts");
+        return false;
+    }
+    auto result = pimpl_->createMergeCommit(index.get(), merge_id);
+    JAMI_INFO("Merge done between %s and main", merge_id.c_str());
+    return result;
+}
+
 } // namespace jami
diff --git a/src/jamidht/conversationrepository.h b/src/jamidht/conversationrepository.h
index c1bf213e6f3efd8e3d6c078fa5f92ccc7c6609f9..b2d85c91860bfda854e47a3d465051a95304eec6 100644
--- a/src/jamidht/conversationrepository.h
+++ b/src/jamidht/conversationrepository.h
@@ -21,6 +21,7 @@
 #include <memory>
 #include <string>
 #include <vector>
+#include <git2.h>
 
 #include "def.h"
 
@@ -41,7 +42,6 @@ using GitDiffStats = std::unique_ptr<git_diff_stats, decltype(&git_diff_stats_fr
 using GitIndexConflictIterator
     = std::unique_ptr<git_index_conflict_iterator, decltype(&git_index_conflict_iterator_free)>;
 
-
 namespace jami {
 
 class JamiAccount;
@@ -111,10 +111,10 @@ public:
     /**
      * Retrieve remote head. Can be useful after a fetch operation
      * @param remoteDeviceId        The remote name
-     * @param branch                Remote branch to check (default: master)
+     * @param branch                Remote branch to check (default: main)
      * @return the commit id pointed
      */
-    std::string remoteHead(const std::string& remoteDeviceId, const std::string& branch = "master");
+    std::string remoteHead(const std::string& remoteDeviceId, const std::string& branch = "main");
 
     /**
      * Return the conversation id
@@ -136,6 +136,13 @@ public:
      */
     std::vector<ConversationCommit> log(const std::string& last = "", unsigned n = 0);
 
+    /**
+     * Merge another branch into the main branch
+     * @param merge_id      The reference to merge
+     * @return if the merge was successful
+     */
+    bool merge(const std::string& merge_id);
+
 private:
     ConversationRepository() = delete;
     class Impl;
diff --git a/test/unitTest/conversationRepository/conversationRepository.cpp b/test/unitTest/conversationRepository/conversationRepository.cpp
index e456714cf48daa2e06c2270497adfc07370f9dbb..4a45b5d4fb6cf28bcb48f9ec65ba9b7a54e6f7bf 100644
--- a/test/unitTest/conversationRepository/conversationRepository.cpp
+++ b/test/unitTest/conversationRepository/conversationRepository.cpp
@@ -61,6 +61,16 @@ private:
     void testAddSomeMessages();
     void testLogMessages();
     void testFetch();
+    void testMerge();
+    void testFFMerge();
+
+    std::string addCommit(git_repository* repo,
+                          const std::shared_ptr<JamiAccount> account,
+                          const std::string& branch,
+                          const std::string& commit_msg);
+    bool merge_in_main(const std::shared_ptr<JamiAccount> account,
+                         git_repository* repo,
+                         const std::string& commit_ref);
 
     CPPUNIT_TEST_SUITE(ConversationRepositoryTest);
     CPPUNIT_TEST(testCreateRepository);
@@ -68,6 +78,8 @@ private:
     CPPUNIT_TEST(testAddSomeMessages);
     CPPUNIT_TEST(testLogMessages);
     CPPUNIT_TEST(testFetch);
+    CPPUNIT_TEST(testMerge);
+    CPPUNIT_TEST(testFFMerge);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -198,9 +210,9 @@ ConversationRepositoryTest::testCloneViaChannelSocket()
 {
     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
-    auto aliceDeviceId = aliceAccount->currentDeviceId();
+    auto aliceDeviceId = std::string(aliceAccount->currentDeviceId());
     auto uri = aliceAccount->getUsername();
-    auto bobDeviceId = bobAccount->currentDeviceId();
+    auto bobDeviceId = std::string(bobAccount->currentDeviceId());
 
     bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
     aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
@@ -381,8 +393,8 @@ ConversationRepositoryTest::testFetch()
 {
     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
-    auto aliceDeviceId = aliceAccount->currentDeviceId();
-    auto bobDeviceId = bobAccount->currentDeviceId();
+    auto aliceDeviceId = std::string(aliceAccount->currentDeviceId());
+    auto bobDeviceId = std::string(bobAccount->currentDeviceId());
 
     bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
     aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
@@ -506,6 +518,161 @@ ConversationRepositoryTest::testFetch()
                                                                        messages[2].signature));
 }
 
+std::string
+ConversationRepositoryTest::addCommit(git_repository* repo,
+                                      const std::shared_ptr<JamiAccount> account,
+                                      const std::string& branch,
+                                      const std::string& commit_msg)
+{
+    auto deviceId = std::string(account->currentDeviceId());
+    auto name = account->getDisplayName();
+    if (name.empty())
+        name = deviceId;
+
+    git_signature* sig_ptr = nullptr;
+    // Sign commit's buffer
+    if (git_signature_new(&sig_ptr, name.c_str(), deviceId.c_str(), std::time(nullptr), 0) < 0) {
+        JAMI_ERR("Unable to create a commit signature.");
+        return {};
+    }
+    GitSignature sig {sig_ptr, git_signature_free};
+
+    // Retrieve current HEAD
+    git_oid commit_id;
+    if (git_reference_name_to_id(&commit_id, repo, "HEAD") < 0) {
+        JAMI_ERR("Cannot get reference for HEAD");
+        return {};
+    }
+
+    git_commit* head_ptr = nullptr;
+    if (git_commit_lookup(&head_ptr, repo, &commit_id) < 0) {
+        JAMI_ERR("Could not look up HEAD commit");
+        return {};
+    }
+    GitCommit head_commit {head_ptr, git_commit_free};
+
+    git_tree* tree_ptr = nullptr;
+    if (git_commit_tree(&tree_ptr, head_commit.get()) < 0) {
+        JAMI_ERR("Could not look up initial tree");
+        return {};
+    }
+    GitTree tree {tree_ptr, git_tree_free};
+
+    git_buf to_sign = {};
+    const git_commit* head_ref[1] = {head_commit.get()};
+    if (git_commit_create_buffer(&to_sign,
+                                 repo,
+                                 sig.get(),
+                                 sig.get(),
+                                 nullptr,
+                                 commit_msg.c_str(),
+                                 tree.get(),
+                                 1,
+                                 &head_ref[0])
+        < 0) {
+        JAMI_ERR("Could not create commit buffer");
+        return {};
+    }
+
+    // git commit -S
+    auto to_sign_vec = std::vector<uint8_t>(to_sign.ptr, to_sign.ptr + to_sign.size);
+    auto signed_buf = account->identity().first->sign(to_sign_vec);
+    std::string signed_str = base64::encode(signed_buf.begin(), signed_buf.end());
+    if (git_commit_create_with_signature(&commit_id,
+                                         repo,
+                                         to_sign.ptr,
+                                         signed_str.c_str(),
+                                         "signature")
+        < 0) {
+        JAMI_ERR("Could not sign commit");
+        return {};
+    }
+
+    auto commit_str = git_oid_tostr_s(&commit_id);
+    if (commit_str) {
+        JAMI_INFO("New commit added with id: %s", commit_str);
+        // Move commit to main branch
+        git_reference* ref_ptr = nullptr;
+        std::string branch_name = "refs/heads/" + branch;
+        if (git_reference_create(&ref_ptr, repo, branch_name.c_str(), &commit_id, true, nullptr)
+            < 0) {
+            JAMI_WARN("Could not move commit to main");
+        }
+        git_reference_free(ref_ptr);
+    }
+    return commit_str ? commit_str : "";
+}
+
+void
+ConversationRepositoryTest::testMerge()
+{
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto repository = ConversationRepository::createConversation(aliceAccount->weak());
+
+    // Assert that repository exists
+    CPPUNIT_ASSERT(repository != nullptr);
+    auto repoPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + aliceAccount->getAccountID()
+                    + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + repository->id();
+    CPPUNIT_ASSERT(fileutils::isDirectory(repoPath));
+
+    // Assert that first commit is signed by alice
+    git_repository* repo;
+    CPPUNIT_ASSERT(git_repository_open(&repo, repoPath.c_str()) == 0);
+    auto id1 = addCommit(repo, aliceAccount, "main", "Commit 1");
+
+    git_reference* ref = nullptr;
+    git_commit* commit = nullptr;
+    git_oid commit_id;
+    git_oid_fromstr(&commit_id, repository->id().c_str());
+    git_commit_lookup(&commit, repo, &commit_id);
+    git_branch_create(&ref, repo, "to_merge", commit, false);
+    git_reference_free(ref);
+    git_repository_set_head(repo, "refs/heads/to_merge");
+
+    auto id2 = addCommit(repo, aliceAccount, "to_merge", "Commit 2");
+    git_repository_free(repo);
+
+    // This will create a merge commit
+    repository->merge(id2);
+
+    CPPUNIT_ASSERT(repository->log().size() == 4 /* Initial, commit 1, 2, merge */);
+}
+
+void
+ConversationRepositoryTest::testFFMerge()
+{
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto repository = ConversationRepository::createConversation(aliceAccount->weak());
+
+    // Assert that repository exists
+    CPPUNIT_ASSERT(repository != nullptr);
+    auto repoPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + aliceAccount->getAccountID()
+                    + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + repository->id();
+    CPPUNIT_ASSERT(fileutils::isDirectory(repoPath));
+
+    // Assert that first commit is signed by alice
+    git_repository* repo;
+    CPPUNIT_ASSERT(git_repository_open(&repo, repoPath.c_str()) == 0);
+    auto id1 = addCommit(repo, aliceAccount, "main", "Commit 1");
+
+    git_reference* ref = nullptr;
+    git_commit* commit = nullptr;
+    git_oid commit_id;
+    git_oid_fromstr(&commit_id, id1.c_str());
+    git_commit_lookup(&commit, repo, &commit_id);
+    git_branch_create(&ref, repo, "to_merge", commit, false);
+    git_reference_free(ref);
+    git_repository_set_head(repo, "refs/heads/to_merge");
+
+    auto id2 = addCommit(repo, aliceAccount, "to_merge", "Commit 2");
+    git_repository_free(repo);
+
+    // This will use a fast forward merge
+    repository->merge(id2);
+
+    CPPUNIT_ASSERT(repository->log().size() == 3 /* Initial, commit 1, 2 */);
+}
+
 } // namespace test
 } // namespace jami