Commit 9a02725d11acb302b0d9ef7013ae81ffd59525c7

Richard Ipsum 2017-03-15T18:17:42

notes: Add git_note_commit_remove This also adds tests for this function.

diff --git a/include/git2/notes.h b/include/git2/notes.h
index c6b2d95..f3cde4e 100644
--- a/include/git2/notes.h
+++ b/include/git2/notes.h
@@ -223,6 +223,32 @@ GIT_EXTERN(int) git_note_remove(
 	const git_oid *oid);
 
 /**
+ * Remove the note for an object
+ *
+ * @param notes_commit_out pointer to store the new notes commit (optional);
+ *					NULL in case of error.
+ *					When removing a note a new tree containing all notes
+ *					sans the note to be removed is created and a new commit
+ *					pointing to that tree is also created.
+ *					In the case where the resulting tree is an empty tree
+ *					a new commit pointing to this empty tree will be returned.
+ * @param repo repository where the note lives
+ * @param notes_commit a pointer to the notes commit object
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param oid OID of the git object to remove the note from
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_remove(
+		git_oid *notes_commit_out,
+		git_repository *repo,
+		git_commit *notes_commit,
+		const git_signature *author,
+		const git_signature *committer,
+		const git_oid *oid);
+
+/**
  * Free a git_note object
  *
  * @param note git_note object
diff --git a/src/notes.c b/src/notes.c
index 35ba93d..9aef8fc 100644
--- a/src/notes.c
+++ b/src/notes.c
@@ -369,7 +369,9 @@ cleanup:
 	return error;
 }
 
-static int note_remove(git_repository *repo,
+static int note_remove(
+		git_oid *notes_commit_out,
+		git_repository *repo,
 		const git_signature *author, const git_signature *committer,
 		const char *notes_ref, git_tree *tree,
 		const char *target, git_commit **parents)
@@ -389,6 +391,12 @@ static int note_remove(git_repository *repo,
 	  *parents == NULL ? 0 : 1,
 	  (const git_commit **) parents);
 
+	if (error < 0)
+		goto cleanup;
+
+	if (notes_commit_out)
+		git_oid_cpy(notes_commit_out, &oid);
+
 cleanup:
 	git_tree_free(tree_after_removal);
 	return error;
@@ -564,7 +572,7 @@ int git_note_remove(git_repository *repo, const char *notes_ref_in,
 
 	if (!(error = retrieve_note_tree_and_commit(
 		      &tree, &commit, &notes_ref, repo, notes_ref_in)))
-		error = note_remove(
+		error = note_remove(NULL,
 			repo, author, committer, notes_ref, tree, target, &commit);
 
 	git__free(notes_ref);
@@ -574,6 +582,31 @@ int git_note_remove(git_repository *repo, const char *notes_ref_in,
 	return error;
 }
 
+int git_note_commit_remove(
+		git_oid *notes_commit_out,
+		git_repository *repo,
+		git_commit *notes_commit,
+		const git_signature *author,
+		const git_signature *committer,
+		const git_oid *oid)
+{
+	int error;
+	git_tree *tree = NULL;
+	char target[GIT_OID_HEXSZ + 1];
+
+	git_oid_tostr(target, sizeof(target), oid);
+
+	if ((error = git_commit_tree(&tree, notes_commit)) < 0)
+		goto cleanup;
+
+	error = note_remove(notes_commit_out,
+		repo, author, committer, NULL, tree, target, &notes_commit);
+
+cleanup:
+	git_tree_free(tree);
+	return error;
+}
+
 int git_note_default_ref(git_buf *out, git_repository *repo)
 {
 	char *default_ref;
diff --git a/tests/notes/notes.c b/tests/notes/notes.c
index ebc62fd..dcf607d 100644
--- a/tests/notes/notes.c
+++ b/tests/notes/notes.c
@@ -460,6 +460,50 @@ void test_notes_notes__can_read_a_note_in_an_existing_fanout(void)
 	git_note_free(note);
 }
 
+/* Can remove a note */
+void test_notes_notes__can_remove_a_note(void)
+{
+	git_oid note_oid, target_oid;
+	git_note *note;
+
+	create_note(&note_oid, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 4a20\n");
+
+	cl_git_pass(git_oid_fromstr(&target_oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+	cl_git_pass(git_note_remove(_repo, "refs/notes/i-can-see-dead-notes", _sig, _sig, &target_oid));
+
+	cl_git_fail(git_note_read(&note, _repo, "refs/notes/i-can-see-dead-notes", &target_oid));
+}
+
+/* Can remove a note from a commit */
+void test_notes_notes__can_remove_a_note_from_commit(void)
+{
+	git_oid oid, notes_commit_oid;
+	git_note *note = NULL;
+	git_commit *existing_notes_commit;
+	git_reference *ref;
+
+	cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+
+	cl_git_pass(git_note_commit_create(&notes_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0));
+
+	git_commit_lookup(&existing_notes_commit, _repo, &notes_commit_oid);
+
+	cl_assert(existing_notes_commit);
+
+	cl_git_pass(git_note_commit_remove(&notes_commit_oid, _repo, existing_notes_commit, _sig, _sig, &oid));
+
+	/* remove_from_commit will not update any ref,
+	 * so we must manually create the ref, that points to the commit */
+	cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", &notes_commit_oid, 0, NULL));
+
+	cl_git_fail(git_note_read(&note, _repo, "refs/notes/i-can-see-dead-notes", &oid));
+
+	git_commit_free(existing_notes_commit);
+	git_reference_free(ref);
+	git_note_free(note);
+}
+
+
 void test_notes_notes__can_remove_a_note_in_an_existing_fanout(void)
 {
 	git_oid target_oid;