diff --git a/pygit2/decl/repository.h b/pygit2/decl/repository.h index 9acc5920..45e47c05 100644 --- a/pygit2/decl/repository.h +++ b/pygit2/decl/repository.h @@ -87,6 +87,7 @@ int git_repository_ident(const char **name, const char **email, const git_reposi int git_repository_set_ident(git_repository *repo, const char *name, const char *email); int git_repository_index(git_index **out, git_repository *repo); git_repository_state_t git_repository_state(git_repository *repo); - +int git_repository_message(git_buf *out, git_repository *repo); +int git_repository_message_remove(git_repository *repo); int git_repository_submodule_cache_all(git_repository *repo); int git_repository_submodule_cache_clear(git_repository *repo); diff --git a/pygit2/repository.py b/pygit2/repository.py index 5aa73217..f585929c 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -957,6 +957,51 @@ def merge(self, id, favor="normal", flags=None, file_flags=None): C.git_annotated_commit_free(commit_ptr[0]) check_error(err) + # + # Prepared message (MERGE_MSG) + # + @property + def raw_message(self) -> bytes: + """ + Retrieve git's prepared message (bytes). + See `Repository.message` for more information. + """ + buf = ffi.new('git_buf *', (ffi.NULL, 0)) + try: + err = C.git_repository_message(buf, self._repo) + if err == C.GIT_ENOTFOUND: + return b'' + check_error(err) + return ffi.string(buf.ptr) + finally: + C.git_buf_dispose(buf) + + @property + def message(self) -> str: + """ + Retrieve git's prepared message. + + Operations such as git revert/cherry-pick/merge with the -n option stop + just short of creating a commit with the changes and save their + prepared message in .git/MERGE_MSG so the next git-commit execution can + present it to the user for them to amend if they wish. + + Use this function to get the contents of this file. Don't forget to + call `Repository.remove_message()` after you create the commit. + + Note that the message is also removed by `Repository.state_cleanup()`. + + If there is no such message, an empty string is returned. + """ + return self.raw_message.decode('utf-8') + + def remove_message(self): + """ + Remove git's prepared message. + """ + err = C.git_repository_message_remove(self._repo) + check_error(err) + # # Describe # diff --git a/test/test_cherrypick.py b/test/test_cherrypick.py index 409431a6..027729ec 100644 --- a/test/test_cherrypick.py +++ b/test/test_cherrypick.py @@ -50,9 +50,14 @@ def test_cherrypick_already_something_in_index(mergerepo): def test_cherrypick_remove_conflicts(mergerepo): assert mergerepo.state() == RepositoryState.NONE + assert not mergerepo.message + other_branch_tip = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' mergerepo.cherrypick(other_branch_tip) + assert mergerepo.state() == RepositoryState.CHERRYPICK + assert mergerepo.message.startswith("commit to provoke a conflict") + idx = mergerepo.index conflicts = idx.conflicts assert conflicts is not None @@ -60,5 +65,7 @@ def test_cherrypick_remove_conflicts(mergerepo): del idx.conflicts['.gitignore'] with pytest.raises(KeyError): conflicts.__getitem__('.gitignore') assert idx.conflicts is None + mergerepo.state_cleanup() assert mergerepo.state() == RepositoryState.NONE + assert not mergerepo.message diff --git a/test/test_merge.py b/test/test_merge.py index 2d52458d..39064420 100644 --- a/test/test_merge.py +++ b/test/test_merge.py @@ -286,3 +286,26 @@ def test_merge_mergeheads(mergerepo): mergerepo.state_cleanup() assert mergerepo.listall_mergeheads() == [], "state_cleanup() should wipe the mergeheads" + + +def test_merge_message(mergerepo): + assert not mergerepo.message + assert not mergerepo.raw_message + + branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' + mergerepo.merge(branch_head_hex) + + assert mergerepo.message.startswith(f"Merge commit '{branch_head_hex}'") + assert mergerepo.message.encode('utf-8') == mergerepo.raw_message + + mergerepo.state_cleanup() + assert not mergerepo.message + + +def test_merge_remove_message(mergerepo): + branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' + mergerepo.merge(branch_head_hex) + + assert mergerepo.message.startswith(f"Merge commit '{branch_head_hex}'") + mergerepo.remove_message() + assert not mergerepo.message