From c15e0db5a9f259e782cf49e50dd1109548bab1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 28 Mar 2011 13:58:44 +0200 Subject: [PATCH 01/21] Fix memory leak in parse_tag_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Free the allocated memory if the signature parsing reports an error. Signed-off-by: Carlos Martín Nieto --- src/tag.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tag.c b/src/tag.c index 7baababbf..d90e2de82 100644 --- a/src/tag.c +++ b/src/tag.c @@ -142,7 +142,7 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) tag->tagger = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ")) != 0) - return error; + goto cleanup; text_len = buffer_end - ++buffer; @@ -151,6 +151,14 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) tag->message[text_len] = '\0'; return GIT_SUCCESS; + + cleanup: + if(tag->tag_name) + free(tag->tag_name); + if(tag->tagger) + git_signature_free(tag->tagger); + + return error; } int git_tag_create_o( From 7b4a16e2c8869ba18264d0c44996880c8f9bb095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 28 Mar 2011 13:59:48 +0200 Subject: [PATCH 02/21] Add git_tag_create_frombuffer API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the tag parsing capabilities already present in the library. Exporting this function makes it possible to implement the mktag command without duplicating this functionality. Signed-off-by: Carlos Martín Nieto --- include/git2/tag.h | 14 ++++++++++++++ src/tag.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/include/git2/tag.h b/include/git2/tag.h index ee92cd5c2..c47e3412c 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -188,6 +188,20 @@ GIT_EXTERN(int) git_tag_create_o( const git_signature *tagger, const char *message); +/** + * Create a new tag in the repository from a buffer + * + * @param oid Pointer where to store the OID of the newly created tag + * + * @param repo Repository where to store the tag + * + * @param buffer Raw tag data + */ +GIT_EXTERN(int) git_tag_create_frombuffer( + git_oid *oid, + git_repository *repo, + const char *buffer); + /** @} */ GIT_END_DECL #endif diff --git a/src/tag.c b/src/tag.c index d90e2de82..a68857763 100644 --- a/src/tag.c +++ b/src/tag.c @@ -241,6 +241,43 @@ int git_tag_create( return error; } +int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer) +{ + git_tag tag; + int error; + char *buf; + git_object *obj; + + assert(oid && buffer); + + memset(&tag, 0, sizeof(tag)); + + buf = strdup(buffer); + if(buf == NULL) + return GIT_ENOMEM; + + if((error = parse_tag_buffer(&tag, buf, buf + strlen(buf))) < 0) + goto exit_freebuf; + + error = git_object_lookup(&obj, repo, &tag.target, tag.type); + if(error < 0) + goto exit_freetag; + + error = git_tag_create_o(oid, repo, tag.tag_name, obj, + tag.tagger, tag.message); + + git_object_close(obj); + + exit_freetag: + git_signature_free(tag.tagger); + free(tag.tag_name); + free(tag.message); + exit_freebuf: + free(buf); + + return error; +} + int git_tag__parse(git_tag *tag, git_odb_object *obj) { From 4d00dfd438a219b20145ec76b0e2c543d9b685d1 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 29 Mar 2011 21:21:47 +0200 Subject: [PATCH 03/21] Replace gitfo_unlink() calls with git_reference_delete() in refs related tests --- tests/t10-refs.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/t10-refs.c b/tests/t10-refs.c index 413811c9d..8bdb75eab 100644 --- a/tests/t10-refs.c +++ b/tests/t10-refs.c @@ -227,9 +227,8 @@ BEGIN_TEST(create0, "create a new symbolic reference") must_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); + git_reference_delete(looked_up_ref); git_repository_free(repo); - - must_pass(gitfo_unlink(ref_path)); /* TODO: replace with git_reference_delete() when available */ END_TEST BEGIN_TEST(create1, "create a deep symbolic reference") @@ -250,9 +249,8 @@ BEGIN_TEST(create1, "create a deep symbolic reference") must_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); + git_reference_delete(looked_up_ref); git_repository_free(repo); - - must_pass(gitfo_unlink(ref_path)); /* TODO: replace with git_reference_delete() when available */ END_TEST BEGIN_TEST(create2, "create a new OID reference") @@ -290,9 +288,8 @@ BEGIN_TEST(create2, "create a new OID reference") must_pass(git_reference_lookup(&looked_up_ref, repo, new_head)); must_be_true(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0); + git_reference_delete(looked_up_ref); git_repository_free(repo); - - must_pass(gitfo_unlink(ref_path)); /* TODO: replace with git_reference_delete() when available */ END_TEST static const char *ref_name = "refs/heads/other"; From 2b9b99b6ed45d42627916f0d9e2105d61fd23b11 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 29 Mar 2011 21:29:30 +0200 Subject: [PATCH 04/21] Add test ensuring one can not create an oid reference which targets at an unknown id --- tests/t10-refs.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/t10-refs.c b/tests/t10-refs.c index 8bdb75eab..b1124b36f 100644 --- a/tests/t10-refs.c +++ b/tests/t10-refs.c @@ -292,6 +292,26 @@ BEGIN_TEST(create2, "create a new OID reference") git_repository_free(repo); END_TEST +BEGIN_TEST(create3, "Can not create a new OID reference which targets at an unknown id") + git_reference *new_reference, *looked_up_ref; + git_repository *repo; + git_oid id; + + const char *new_head = "refs/heads/new-head"; + + git_oid_mkstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644"); + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + /* Create and write the new object id reference */ + must_fail(git_reference_create_oid(&new_reference, repo, new_head, &id)); + + /* Ensure the reference can't be looked-up... */ + must_fail(git_reference_lookup(&looked_up_ref, repo, new_head)); + + git_repository_free(repo); +END_TEST + static const char *ref_name = "refs/heads/other"; static const char *ref_master_name = "refs/heads/master"; static const char *ref_branch_name = "refs/heads/branch"; @@ -889,6 +909,7 @@ BEGIN_SUITE(refs) ADD_TEST(create0); ADD_TEST(create1); ADD_TEST(create2); + ADD_TEST(create3); ADD_TEST(overwrite0); ADD_TEST(overwrite1); From 673de2cf59e2f573895f7b9239785c634ca9ba38 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 30 Mar 2011 21:29:10 +0200 Subject: [PATCH 05/21] Fix misleading comments --- tests/t08-tag.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/t08-tag.c b/tests/t08-tag.c index 70eeb28a6..09d131036 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -78,14 +78,14 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") git_oid_mkstr(&target_id, tagged_commit); - /* create signatures */ + /* create signature */ tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); must_be_true(tagger != NULL); must_pass(git_tag_create( &tag_id, /* out id */ repo, - "the-tag", /* do not update the HEAD */ + "the-tag", &target_id, GIT_OBJ_COMMIT, tagger, From 8e9a3d421755100891402670e5a709031205e664 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 30 Mar 2011 21:46:19 +0200 Subject: [PATCH 06/21] Enforce the testing of the correct creation of a tag --- tests/t08-tag.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/t08-tag.c b/tests/t08-tag.c index 09d131036..d1847f353 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -94,6 +94,7 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") git_signature_free((git_signature *)tagger); must_pass(git_tag_lookup(&tag, repo, &tag_id)); + must_be_true(git_oid_cmp(git_tag_target_oid(tag), &target_id) == 0); /* Check attributes were set correctly */ tagger = git_tag_tagger(tag); From 6d3160148b7220e78e2898aaadb457edcec4c846 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 30 Mar 2011 21:57:20 +0200 Subject: [PATCH 07/21] Add test demonstrating that one can create a tag pointing at a non existent target --- tests/t08-tag.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/t08-tag.c b/tests/t08-tag.c index d1847f353..7b3c88649 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -72,7 +72,6 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") git_oid target_id, tag_id; const git_signature *tagger; git_reference *ref_tag; - /* char hex_oid[41]; */ must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); @@ -116,8 +115,50 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") END_TEST +BEGIN_TEST(write1, "write a tag to the repository which points to an unknown oid and read it again") + git_repository *repo; + git_tag *tag; + git_oid target_id, tag_id; + const git_signature *tagger; + git_reference *ref_tag; + git_object *zombie; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + git_oid_mkstr(&target_id, "deadbeef1b46c854b31185ea97743be6a8774479"); + + /* create signature */ + tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); + must_be_true(tagger != NULL); + + must_pass(git_tag_create( + &tag_id, /* out id */ + repo, + "the-zombie-tag", + &target_id, + GIT_OBJ_COMMIT, + tagger, + TAGGER_MESSAGE)); + + git_signature_free((git_signature *)tagger); + + must_pass(git_tag_lookup(&tag, repo, &tag_id)); + + /* The non existent target can not be looked up */ + must_fail(git_tag_target(&zombie, tag)); + + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/the-zombie-tag")); + + must_pass(git_reference_delete(ref_tag)); + must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tag)); + + git_repository_free(repo); + +END_TEST + BEGIN_SUITE(tag) ADD_TEST(read0); ADD_TEST(write0); + ADD_TEST(write1); END_SUITE From bf4c39f92958b42f626fbf1d7fd95a74e62f3409 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 30 Mar 2011 22:30:55 +0200 Subject: [PATCH 08/21] Prevent tag_create() from creating a conflicting reference --- include/git2/tag.h | 3 ++- src/tag.c | 18 +++++++++++------- tests/t08-tag.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/include/git2/tag.h b/include/git2/tag.h index ee92cd5c2..b83d44733 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -140,7 +140,8 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *t); * @param repo Repository where to store the tag * * @param tag_name Name for the tag; this name is validated - * for consistency + * for consistency. It should also not conflict with an + * already existing tag name * * @param target OID to which this tag points; note that no * validation is done on this OID. Use the _o version of this diff --git a/src/tag.c b/src/tag.c index 7baababbf..9a0069448 100644 --- a/src/tag.c +++ b/src/tag.c @@ -182,10 +182,18 @@ int git_tag_create( const char *type_str; char *tagger_str; + git_reference *new_ref; + + char ref_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; int type_str_len, tag_name_len, tagger_str_len, message_len; int error; + /** Ensure the tag name doesn't conflict with an already existing reference **/ + git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name); + if (!git_reference_lookup(&new_ref, repo, ref_name)) + return GIT_EEXISTS; + type_str = git_object_type2string(target_type); @@ -223,14 +231,10 @@ int git_tag_create( error = stream->finalize_write(oid, stream); stream->free(stream); - if (error == GIT_SUCCESS) { - char ref_name[512]; - git_reference *new_ref; - git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name); - error = git_reference_create_oid(&new_ref, repo, ref_name, oid); - } + if (error < GIT_SUCCESS) + return error; - return error; + return git_reference_create_oid(&new_ref, repo, ref_name, oid); } diff --git a/tests/t08-tag.c b/tests/t08-tag.c index 7b3c88649..371f8621c 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -156,9 +156,38 @@ BEGIN_TEST(write1, "write a tag to the repository which points to an unknown oid END_TEST +BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already existing tag") + git_repository *repo; + git_oid target_id, tag_id; + const git_signature *tagger; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + git_oid_mkstr(&target_id, tagged_commit); + + /* create signature */ + tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); + must_be_true(tagger != NULL); + + must_fail(git_tag_create( + &tag_id, /* out id */ + repo, + "very-simple", + &target_id, + GIT_OBJ_COMMIT, + tagger, + TAGGER_MESSAGE)); + + git_signature_free((git_signature *)tagger); + + git_repository_free(repo); + +END_TEST + BEGIN_SUITE(tag) ADD_TEST(read0); ADD_TEST(write0); ADD_TEST(write1); + ADD_TEST(write2); END_SUITE From 74e50a2d3e53e52ede430dca4ebc5efb2a38de4e Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 30 Mar 2011 22:46:52 +0200 Subject: [PATCH 09/21] Fix memory leak in tag releated tests --- tests/t08-tag.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/t08-tag.c b/tests/t08-tag.c index 371f8621c..a5bdee3e0 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -58,6 +58,9 @@ BEGIN_TEST(read0, "read and parse a tag from the repository") must_be_true(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0); + git_tag_close(tag1); + git_tag_close(tag2); + git_commit_close(commit); git_repository_free(repo); END_TEST @@ -111,6 +114,7 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tag)); + git_tag_close(tag); git_repository_free(repo); END_TEST @@ -152,6 +156,7 @@ BEGIN_TEST(write1, "write a tag to the repository which points to an unknown oid must_pass(git_reference_delete(ref_tag)); must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tag)); + git_tag_close(tag); git_repository_free(repo); END_TEST From a50c145855598b53463c3715f399cd5ebf27240d Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 30 Mar 2011 23:16:30 +0200 Subject: [PATCH 10/21] Add git_tag_create_o_f() and git_tag_create_f() which overwrite existing tag reference, if any --- include/git2/tag.h | 58 +++++++++++++++++++++++ src/tag.c | 111 ++++++++++++++++++++++++++++++++++++--------- tests/t08-tag.c | 36 +++++++++++++++ 3 files changed, 183 insertions(+), 22 deletions(-) diff --git a/include/git2/tag.h b/include/git2/tag.h index b83d44733..6468cfdd7 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -189,6 +189,64 @@ GIT_EXTERN(int) git_tag_create_o( const git_signature *tagger, const char *message); +/** +* Create a new tag in the repository from an OID +* and overwrite an already existing tag reference, if any. +* +* @param oid Pointer where to store the OID of the +* newly created tag +* +* @param repo Repository where to store the tag +* +* @param tag_name Name for the tag; this name is validated +* for consistency. +* +* @param target OID to which this tag points; note that no +* validation is done on this OID. Use the _o_f version of this +* method to assure a proper object is being tagged +* +* @param target_type Type of the tagged OID; note that no +* validation is performed here either +* +* @param tagger Signature of the tagger for this tag, and +* of the tagging time +* +* @param message Full message for this tag +* +* @return 0 on success; error code otherwise. +* A tag object is written to the ODB, and a proper reference +* is written in the /refs/tags folder, pointing to it +*/ +GIT_EXTERN(int) git_tag_create_f( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message); + +/** + * Create a new tag in the repository from an existing + * `git_object` instance and overwrite an already existing + * tag reference, if any. + * + * This method replaces the `target` and `target_type` + * paremeters of `git_tag_create_f` by a single instance + * of a `const git_object *`, which is assured to be + * a proper object in the ODB and hence will create + * a valid tag + * + * @see git_tag_create_f + */ +GIT_EXTERN(int) git_tag_create_o_f( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message); + /** @} */ GIT_END_DECL #endif diff --git a/src/tag.c b/src/tag.c index 9a0069448..e75c46916 100644 --- a/src/tag.c +++ b/src/tag.c @@ -153,29 +153,15 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) return GIT_SUCCESS; } -int git_tag_create_o( - git_oid *oid, - git_repository *repo, - const char *tag_name, - const git_object *target, - const git_signature *tagger, - const char *message) -{ - return git_tag_create( - oid, repo, tag_name, - git_object_id(target), - git_object_type(target), - tagger, message); -} - -int git_tag_create( +static int tag_create( git_oid *oid, git_repository *repo, const char *tag_name, const git_oid *target, git_otype target_type, const git_signature *tagger, - const char *message) + const char *message, + int allow_ref_overwrite) { size_t final_size = 0; git_odb_stream *stream; @@ -187,12 +173,27 @@ int git_tag_create( char ref_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; int type_str_len, tag_name_len, tagger_str_len, message_len; - int error; + int error, should_update_ref = 0; - /** Ensure the tag name doesn't conflict with an already existing reference **/ + /** Ensure the tag name doesn't conflict with an already existing + reference unless overwriting has explictly been requested **/ git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name); - if (!git_reference_lookup(&new_ref, repo, ref_name)) - return GIT_EEXISTS; + error = git_reference_lookup(&new_ref, repo, ref_name); + + switch (error) { + case GIT_SUCCESS: + if (!allow_ref_overwrite) + return GIT_EEXISTS; + should_update_ref = 1; + + /* Fall trough */ + + case GIT_ENOTFOUND: + break; + + default: + return error; + } type_str = git_object_type2string(target_type); @@ -234,9 +235,75 @@ int git_tag_create( if (error < GIT_SUCCESS) return error; - return git_reference_create_oid(&new_ref, repo, ref_name, oid); + if (!should_update_ref) + error = git_reference_create_oid(&new_ref, repo, ref_name, oid); + else + error = git_reference_set_oid(new_ref, oid); + + return error; } +int git_tag_create_o( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message) +{ + return tag_create( + oid, repo, tag_name, + git_object_id(target), + git_object_type(target), + tagger, message, 0); +} + +int git_tag_create( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message) +{ + return tag_create( + oid, repo, tag_name, + target, + target_type, + tagger, message, 0); +} + +int git_tag_create_o_f( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message) +{ + return tag_create( + oid, repo, tag_name, + git_object_id(target), + git_object_type(target), + tagger, message, 1); +} + +int git_tag_create_f( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message) +{ + return tag_create( + oid, repo, tag_name, + target, + target_type, + tagger, message, 1); +} int git_tag__parse(git_tag *tag, git_odb_object *obj) { diff --git a/tests/t08-tag.c b/tests/t08-tag.c index a5bdee3e0..2bea4bc95 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -189,10 +189,46 @@ BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already END_TEST +BEGIN_TEST(write3, "Replace an already existing tag") + git_repository *repo; + git_oid target_id, tag_id, old_tag_id; + const git_signature *tagger; + git_reference *ref_tag; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + git_oid_mkstr(&target_id, tagged_commit); + + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple")); + git_oid_cpy(&old_tag_id, git_reference_oid(ref_tag)); + + /* create signature */ + tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); + must_be_true(tagger != NULL); + + must_pass(git_tag_create_f( + &tag_id, /* out id */ + repo, + "very-simple", + &target_id, + GIT_OBJ_COMMIT, + tagger, + TAGGER_MESSAGE)); + + git_signature_free((git_signature *)tagger); + + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple")); + must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &tag_id) == 0); + must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &old_tag_id) != 0); + + close_temp_repo(repo); + +END_TEST BEGIN_SUITE(tag) ADD_TEST(read0); ADD_TEST(write0); ADD_TEST(write1); ADD_TEST(write2); + ADD_TEST(write3); END_SUITE From 9e680bcc002af8f230bfce8dbfee3fef4001fb38 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 30 Mar 2011 23:26:36 +0200 Subject: [PATCH 11/21] Add git_tag_delete() --- include/git2/tag.h | 14 ++++++++++++++ src/tag.c | 31 +++++++++++++++++++++++++++++-- tests/t08-tag.c | 14 ++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/include/git2/tag.h b/include/git2/tag.h index 6468cfdd7..1ee1e0cc4 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -247,6 +247,20 @@ GIT_EXTERN(int) git_tag_create_o_f( const git_signature *tagger, const char *message); +/** +* Delete an existing tag reference. +* +* @param repo Repository where lives the tag +* +* @param tag_name Name of the tag to be deleted; +* this name is validated for consistency. +* +* @return 0 on success; error code otherwise. +*/ +GIT_EXTERN(int) git_tag_delete( + git_repository *repo, + const char *tag_name); + /** @} */ GIT_END_DECL #endif diff --git a/src/tag.c b/src/tag.c index e75c46916..ff2d0abe7 100644 --- a/src/tag.c +++ b/src/tag.c @@ -153,6 +153,21 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) return GIT_SUCCESS; } +static int retreive_tag_reference(git_reference **tag_reference_out, char *ref_name_out, git_repository *repo, const char *tag_name) +{ + git_reference *tag_ref; + int error; + + git__joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name); + error = git_reference_lookup(&tag_ref, repo, ref_name_out); + if (error < GIT_SUCCESS) + return error; + + *tag_reference_out = tag_ref;; + + return GIT_SUCCESS; +} + static int tag_create( git_oid *oid, git_repository *repo, @@ -177,8 +192,7 @@ static int tag_create( /** Ensure the tag name doesn't conflict with an already existing reference unless overwriting has explictly been requested **/ - git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name); - error = git_reference_lookup(&new_ref, repo, ref_name); + error = retreive_tag_reference(&new_ref, ref_name, repo, tag_name); switch (error) { case GIT_SUCCESS: @@ -305,6 +319,19 @@ int git_tag_create_f( tagger, message, 1); } +int git_tag_delete(git_repository *repo, const char *tag_name) +{ + int error; + git_reference *tag_ref; + char ref_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; + + error = retreive_tag_reference(&tag_ref, ref_name, repo, tag_name); + if (error < GIT_SUCCESS) + return error; + + return git_reference_delete(tag_ref); +} + int git_tag__parse(git_tag *tag, git_odb_object *obj) { assert(tag); diff --git a/tests/t08-tag.c b/tests/t08-tag.c index 2bea4bc95..ebb7a1f9c 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -225,6 +225,20 @@ BEGIN_TEST(write3, "Replace an already existing tag") END_TEST +BEGIN_TEST(write4, "Delete an already existing tag") + git_repository *repo; + git_reference *ref_tag; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + must_pass(git_tag_delete(repo,"very-simple")); + + must_fail(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple")); + + close_temp_repo(repo); + +END_TEST + BEGIN_SUITE(tag) ADD_TEST(read0); ADD_TEST(write0); From ac26e2454e71010821f2c85046b5cbd5a522d384 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 30 Mar 2011 23:46:54 +0200 Subject: [PATCH 12/21] Rename git_tag_create_o_f() to git_tag_create_fo() --- include/git2/tag.h | 4 ++-- src/tag.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/git2/tag.h b/include/git2/tag.h index 1ee1e0cc4..0a37f6c39 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -202,7 +202,7 @@ GIT_EXTERN(int) git_tag_create_o( * for consistency. * * @param target OID to which this tag points; note that no -* validation is done on this OID. Use the _o_f version of this +* validation is done on this OID. Use the _fo version of this * method to assure a proper object is being tagged * * @param target_type Type of the tagged OID; note that no @@ -239,7 +239,7 @@ GIT_EXTERN(int) git_tag_create_f( * * @see git_tag_create_f */ -GIT_EXTERN(int) git_tag_create_o_f( +GIT_EXTERN(int) git_tag_create_fo( git_oid *oid, git_repository *repo, const char *tag_name, diff --git a/src/tag.c b/src/tag.c index ff2d0abe7..5a1101a19 100644 --- a/src/tag.c +++ b/src/tag.c @@ -288,7 +288,7 @@ int git_tag_create( tagger, message, 0); } -int git_tag_create_o_f( +int git_tag_create_fo( git_oid *oid, git_repository *repo, const char *tag_name, From ba84cad38079b93e167175230f653a7b1133c11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 31 Mar 2011 15:02:23 +0200 Subject: [PATCH 13/21] wscript: Use -O0 on debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we want debugging symbols, we most likely want them to point to the right place. With -O2, gdb or valgrind may give wrong information. Signed-off-by: Carlos Martín Nieto --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index f3082aec7..dd9040658 100644 --- a/wscript +++ b/wscript @@ -4,7 +4,7 @@ from waflib.Build import BuildContext, CleanContext, \ # Unix flags CFLAGS_UNIX = ["-O2", "-Wall", "-Wextra"] -CFLAGS_UNIX_DBG = ['-g'] +CFLAGS_UNIX_DBG = ['-g', '-O0'] # Windows MSVC flags CFLAGS_WIN32_COMMON = ['/TC', '/W4', '/WX', '/nologo', '/Zi'] From ccfce5f30fd1e906625594fad98e079b1cfe03d3 Mon Sep 17 00:00:00 2001 From: Olivier Ramonat Date: Wed, 30 Mar 2011 19:47:12 +0200 Subject: [PATCH 14/21] Update README after sqlite option change To build libgit2 with sqlite support, waf configure should be run with --with-sqlite --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1254adcd9..dae6a76bf 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,8 @@ The waf build system for libgit2 accepts the following flags: --arch=[ia64|x64|x86|x86_amd64|x86_ia64] Force a specific architecture for compilers that support it. - --without-sqlite - Disable sqlite support. + --with-sqlite + Enable sqlite support. You can run `./waf --help` to see a full list of install options and targets. From 720d5472f8aa629161749450a27d3a4e3ecefea3 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Sat, 2 Apr 2011 12:42:04 +0300 Subject: [PATCH 15/21] Change `parse` methods to const buffer Signed-off-by: Vicent Marti --- src/commit.c | 4 ++-- src/oid.c | 4 ++-- src/repository.h | 2 +- src/signature.c | 8 ++++---- src/signature.h | 2 +- src/tag.c | 42 ++++++++++++------------------------------ src/tree.c | 4 ++-- tests/t04-commit.c | 10 +++++----- 8 files changed, 29 insertions(+), 47 deletions(-) diff --git a/src/commit.c b/src/commit.c index 03b111da5..9fc3f0767 100644 --- a/src/commit.c +++ b/src/commit.c @@ -235,9 +235,9 @@ int git_commit_create( return error; } -int commit_parse_buffer(git_commit *commit, void *data, size_t len) +int commit_parse_buffer(git_commit *commit, const void *data, size_t len) { - char *buffer = (char *)data; + const char *buffer = (char *)data; const char *buffer_end = (char *)data + len; git_oid parent_oid; diff --git a/src/oid.c b/src/oid.c index eb167a685..86c1e0039 100644 --- a/src/oid.c +++ b/src/oid.c @@ -118,13 +118,13 @@ char *git_oid_to_string(char *out, size_t n, const git_oid *oid) return out; } -int git__parse_oid(git_oid *oid, char **buffer_out, +int git__parse_oid(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header) { const size_t sha_len = GIT_OID_HEXSZ; const size_t header_len = strlen(header); - char *buffer = *buffer_out; + const char *buffer = *buffer_out; if (buffer + (header_len + sha_len + 1) > buffer_end) return GIT_EOBJCORRUPTED; diff --git a/src/repository.h b/src/repository.h index fef1c7da0..813cac942 100644 --- a/src/repository.h +++ b/src/repository.h @@ -43,7 +43,7 @@ struct git_repository { * export */ void git_object__free(void *object); -int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header); +int git__parse_oid(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header); int git__write_oid(git_odb_stream *src, const char *header, const git_oid *oid); #endif diff --git a/src/signature.c b/src/signature.c index 412637600..0c99755d4 100644 --- a/src/signature.c +++ b/src/signature.c @@ -109,14 +109,14 @@ static int parse_timezone_offset(const char *buffer, int *offset_out) } -int git_signature__parse(git_signature *sig, char **buffer_out, +int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header) { const size_t header_len = strlen(header); int name_length, email_length; - char *buffer = *buffer_out; - char *line_end, *name_end, *email_end; + const char *buffer = *buffer_out; + const char *line_end, *name_end, *email_end; int offset = 0; memset(sig, 0x0, sizeof(git_signature)); @@ -159,7 +159,7 @@ int git_signature__parse(git_signature *sig, char **buffer_out, if (buffer >= line_end) return GIT_EOBJCORRUPTED; - sig->when.time = strtol(buffer, &buffer, 10); + sig->when.time = strtol(buffer, (char **)&buffer, 10); if (sig->when.time == 0) return GIT_EOBJCORRUPTED; diff --git a/src/signature.h b/src/signature.h index 3534cb21f..feba6578d 100644 --- a/src/signature.h +++ b/src/signature.h @@ -6,7 +6,7 @@ #include "repository.h" #include -int git_signature__parse(git_signature *sig, char **buffer_out, const char *buffer_end, const char *header); +int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header); int git_signature__write(char **signature, const char *header, const git_signature *sig); #endif diff --git a/src/tag.c b/src/tag.c index a68857763..5982a053b 100644 --- a/src/tag.c +++ b/src/tag.c @@ -79,7 +79,7 @@ const char *git_tag_message(git_tag *t) return t->message; } -static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) +static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer_end) { static const char *tag_types[] = { NULL, "commit\n", "tree\n", "blob\n", "tag\n" @@ -130,9 +130,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) text_len = search - buffer; - if (tag->tag_name != NULL) - free(tag->tag_name); - tag->tag_name = git__malloc(text_len + 1); memcpy(tag->tag_name, buffer, text_len); tag->tag_name[text_len] = '\0'; @@ -141,8 +138,11 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) tag->tagger = git__malloc(sizeof(git_signature)); - if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ")) != 0) - goto cleanup; + if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ")) != 0) { + free(tag->tag_name); + git_signature_free(tag->tagger); + return error; + } text_len = buffer_end - ++buffer; @@ -151,14 +151,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) tag->message[text_len] = '\0'; return GIT_SUCCESS; - - cleanup: - if(tag->tag_name) - free(tag->tag_name); - if(tag->tagger) - git_signature_free(tag->tagger); - - return error; } int git_tag_create_o( @@ -194,7 +186,6 @@ int git_tag_create( int type_str_len, tag_name_len, tagger_str_len, message_len; int error; - type_str = git_object_type2string(target_type); tagger_str_len = git_signature__write(&tagger_str, "tagger", tagger); @@ -245,40 +236,31 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu { git_tag tag; int error; - char *buf; git_object *obj; assert(oid && buffer); memset(&tag, 0, sizeof(tag)); - buf = strdup(buffer); - if(buf == NULL) - return GIT_ENOMEM; - - if((error = parse_tag_buffer(&tag, buf, buf + strlen(buf))) < 0) - goto exit_freebuf; + if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS) + return error; error = git_object_lookup(&obj, repo, &tag.target, tag.type); - if(error < 0) - goto exit_freetag; + if (error < GIT_SUCCESS) + goto cleanup; - error = git_tag_create_o(oid, repo, tag.tag_name, obj, - tag.tagger, tag.message); + error = git_tag_create_o(oid, repo, tag.tag_name, obj, tag.tagger, tag.message); git_object_close(obj); - exit_freetag: +cleanup: git_signature_free(tag.tagger); free(tag.tag_name); free(tag.message); - exit_freebuf: - free(buf); return error; } - int git_tag__parse(git_tag *tag, git_odb_object *obj) { assert(tag); diff --git a/src/tree.c b/src/tree.c index 31b286e69..dea50463b 100644 --- a/src/tree.c +++ b/src/tree.c @@ -127,7 +127,7 @@ size_t git_tree_entrycount(git_tree *tree) return tree->entries.length; } -static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end) +static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end) { int error = GIT_SUCCESS; @@ -146,7 +146,7 @@ static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end) if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS) return GIT_ENOMEM; - entry->attr = strtol(buffer, &buffer, 8); + entry->attr = strtol(buffer, (char **)&buffer, 8); if (*buffer++ != ' ') { error = GIT_EOBJCORRUPTED; diff --git a/tests/t04-commit.c b/tests/t04-commit.c index e92842435..bcc0417c8 100644 --- a/tests/t04-commit.c +++ b/tests/t04-commit.c @@ -114,15 +114,15 @@ BEGIN_TEST(parse0, "parse the OID line in a commit") git_oid oid; #define TEST_OID_PASS(string, header) { \ - char *ptr = string;\ - char *ptr_original = ptr;\ + const char *ptr = string;\ + const char *ptr_original = ptr;\ size_t len = strlen(ptr);\ must_pass(git__parse_oid(&oid, &ptr, ptr + len, header));\ must_be_true(ptr == ptr_original + len);\ } #define TEST_OID_FAIL(string, header) { \ - char *ptr = string;\ + const char *ptr = string;\ size_t len = strlen(ptr);\ must_fail(git__parse_oid(&oid, &ptr, ptr + len, header));\ } @@ -154,7 +154,7 @@ END_TEST BEGIN_TEST(parse1, "parse the signature line in a commit") #define TEST_SIGNATURE_PASS(_string, _header, _name, _email, _time, _offset) { \ - char *ptr = _string; \ + const char *ptr = _string; \ size_t len = strlen(_string);\ git_signature person = {NULL, NULL, {0, 0}}; \ must_pass(git_signature__parse(&person, &ptr, ptr + len, _header));\ @@ -166,7 +166,7 @@ BEGIN_TEST(parse1, "parse the signature line in a commit") } #define TEST_SIGNATURE_FAIL(_string, _header) { \ - char *ptr = _string; \ + const char *ptr = _string; \ size_t len = strlen(_string);\ git_signature person = {NULL, NULL, {0, 0}}; \ must_fail(git_signature__parse(&person, &ptr, ptr + len, _header));\ From 47d8ec56e917adda2e336c29420f527fca733866 Mon Sep 17 00:00:00 2001 From: Sarath Lakshman Date: Sun, 3 Apr 2011 17:18:56 +0530 Subject: [PATCH 16/21] New external API method: `git_tree_create` Creates a tree by scanning the index file. The method handles recursive creation of trees for subdirectories and adds them to the parent tree. --- include/git2/tree.h | 12 +++++++ src/tree.c | 87 +++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 1 + 3 files changed, 100 insertions(+) diff --git a/include/git2/tree.h b/include/git2/tree.h index 164aec9e2..bd1f97073 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -138,6 +138,18 @@ GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry); */ GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry); +/** + * Create tree by scanning the index file. + * + * @param object identity for the tree + * @param repository where to lookup for object db + * @param base directory path + * @param path length for base + * @param index entry offset to start scan + * @return number of items added to the tree + */ +GIT_EXTERN(int) git_tree_create(git_oid *oid, git_repository *repo, const char *base, int baselen, int entry_no); + /** @} */ GIT_END_DECL #endif diff --git a/src/tree.c b/src/tree.c index dea50463b..299fc696c 100644 --- a/src/tree.c +++ b/src/tree.c @@ -178,3 +178,90 @@ int git_tree__parse(git_tree *tree, git_odb_object *obj) return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); } +int git_tree_create(git_oid *oid, git_repository *repo, const char *base, int baselen, int entry_no) +{ + unsigned long size, offset; + char *buffer; + int nr, maxentries; + git_index *index; + git_odb_stream *stream; + int error; + + git_index_open_inrepo(&index,repo); + maxentries = git_index_entrycount (index); + + /* FIXME: A matching error code could not be found. Hence used a close error code GIT_EBAREINDEX */ + if (maxentries <= 0) { + return GIT_EBAREINDEX; + } + + /* Guess at some random initial size */ + size = 8192; + buffer = git__malloc(size); + if (buffer == NULL) + return GIT_ENOMEM; + + offset = 0; + nr = entry_no; + + do { + git_index_entry *entry = git_index_get(index, nr); + const char *pathname = entry->path, *filename, *dirname; + int pathlen = strlen(pathname), entrylen; + git_oid *entry_oid; + unsigned int mode; + unsigned char *sha1; + + /* Did we hit the end of the directory? Return how many we wrote */ + if (baselen >= pathlen || memcmp(base, pathname, baselen)) + break; + + entry_oid = &entry->oid; + mode = entry->mode; + sha1 = entry_oid->id; + + /* Do we have _further_ subdirectories? */ + filename = pathname + baselen; + dirname = strchr(filename, '/'); + if (dirname) { + int subdir_written; + subdir_written = git_tree_create(oid, repo, pathname, dirname-pathname+1, nr); + + if (subdir_written == GIT_ENOMEM) + return GIT_ENOMEM; + + nr += subdir_written; + + /* Now we need to write out the directory entry into this tree.. */ + mode = S_IFDIR; + pathlen = dirname - pathname; + + /* ..but the directory entry doesn't count towards the total count */ + nr--; + sha1 = oid->id; + } + + entrylen = pathlen - baselen; + if (offset + entrylen + 100 > size) { + size = alloc_nr(offset + entrylen + 100); + buffer = git__realloc(buffer, size); + + if (buffer == NULL) + return GIT_ENOMEM; + } + offset += sprintf(buffer + offset, "%o %.*s", mode, entrylen, filename); + buffer[offset++] = 0; + memcpy(buffer + offset, sha1, GIT_OID_RAWSZ); + offset += GIT_OID_RAWSZ; + nr++; + } while (nr < maxentries); + + if ((error = git_odb_open_wstream(&stream, repo->db, offset, GIT_OBJ_TREE)) < GIT_SUCCESS) + return error; + + stream->write(stream, buffer, offset); + error = stream->finalize_write(oid, stream); + stream->free(stream); + + return nr; +} diff --git a/src/util.h b/src/util.h index 653b34d02..f477b64fd 100644 --- a/src/util.h +++ b/src/util.h @@ -14,6 +14,7 @@ */ #define git__malloc malloc #define git__calloc calloc +#define git__realloc realloc #define git__strdup strdup extern int git__fmt(char *, size_t, const char *, ...) From 29e1789b34d9aa5e486ca12937899a43f2e2407b Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 4 Apr 2011 12:14:03 +0300 Subject: [PATCH 17/21] Fix the git_tree_write implementation --- include/git2/tree.h | 24 +++++---- src/tree.c | 116 +++++++++++++++++++++++--------------------- 2 files changed, 77 insertions(+), 63 deletions(-) diff --git a/include/git2/tree.h b/include/git2/tree.h index bd1f97073..60150b309 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -134,21 +134,27 @@ GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry); * @param object pointer to the converted object * @param repo repository where to lookup the pointed object * @param entry a tree entry - * @return a reference to the pointed object in the repository + * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry); /** - * Create tree by scanning the index file. + * Write a tree to the ODB from the index file * - * @param object identity for the tree - * @param repository where to lookup for object db - * @param base directory path - * @param path length for base - * @param index entry offset to start scan - * @return number of items added to the tree + * This method will scan the index and write a representation + * of its current state back to disk; it recursively creates + * tree objects for each of the subtrees stored in the index, + * but only returns the OID of the root tree. This is the OID + * that can be used e.g. to create a commit. + * + * The index instance cannot be bare, and needs to be associated + * to an existing repository. + * + * @param oid Pointer where to store the written tree + * @param index Index to write + * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_tree_create(git_oid *oid, git_repository *repo, const char *base, int baselen, int entry_no); +GIT_EXTERN(int) git_tree_create_fromindex(git_oid *oid, git_index *index); /** @} */ GIT_END_DECL diff --git a/src/tree.c b/src/tree.c index 299fc696c..588b5e9a0 100644 --- a/src/tree.c +++ b/src/tree.c @@ -137,7 +137,7 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf while (buffer < buffer_end) { git_tree_entry *entry; - entry = git__malloc(sizeof(git_tree_entry)); + entry = git__calloc(1, sizeof(git_tree_entry)); if (entry == NULL) { error = GIT_ENOMEM; break; @@ -178,90 +178,98 @@ int git_tree__parse(git_tree *tree, git_odb_object *obj) return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); } -int git_tree_create(git_oid *oid, git_repository *repo, const char *base, int baselen, int entry_no) +static int write_entry(char *buffer, int mode, const char *path, size_t path_len, const git_oid *oid) { - unsigned long size, offset; + int written; + written = sprintf(buffer, "%o %.*s%c", mode, (int)path_len, path, 0); + memcpy(buffer + written, &oid->id, GIT_OID_RAWSZ); + return written + GIT_OID_RAWSZ; +} + +static int write_index(git_oid *oid, git_index *index, const char *base, int baselen, int entry_no, int maxentries) +{ + size_t size, offset; char *buffer; - int nr, maxentries; - git_index *index; - git_odb_stream *stream; - int error; - - git_index_open_inrepo(&index,repo); - maxentries = git_index_entrycount (index); - - /* FIXME: A matching error code could not be found. Hence used a close error code GIT_EBAREINDEX */ - if (maxentries <= 0) { - return GIT_EBAREINDEX; - } - + int nr, error; + /* Guess at some random initial size */ - size = 8192; + size = maxentries * 40; buffer = git__malloc(size); if (buffer == NULL) return GIT_ENOMEM; offset = 0; - nr = entry_no; - do { + for (nr = entry_no; nr < maxentries; ++nr) { git_index_entry *entry = git_index_get(index, nr); + const char *pathname = entry->path, *filename, *dirname; int pathlen = strlen(pathname), entrylen; - git_oid *entry_oid; - unsigned int mode; - unsigned char *sha1; + + unsigned int write_mode; + git_oid subtree_oid; + git_oid *write_oid; /* Did we hit the end of the directory? Return how many we wrote */ - if (baselen >= pathlen || memcmp(base, pathname, baselen)) + if (baselen >= pathlen || memcmp(base, pathname, baselen) != 0) break; - entry_oid = &entry->oid; - mode = entry->mode; - sha1 = entry_oid->id; - /* Do we have _further_ subdirectories? */ filename = pathname + baselen; dirname = strchr(filename, '/'); + + write_oid = &entry->oid; + write_mode = entry->mode; + if (dirname) { int subdir_written; - subdir_written = git_tree_create(oid, repo, pathname, dirname-pathname+1, nr); + +#if 0 + if (entry->mode != S_IFDIR) { + free(buffer); + return GIT_EOBJCORRUPTED; + } +#endif + subdir_written = write_index(&subtree_oid, index, pathname, dirname - pathname + 1, nr, maxentries); + + if (subdir_written < GIT_SUCCESS) { + free(buffer); + return subdir_written; + } - if (subdir_written == GIT_ENOMEM) - return GIT_ENOMEM; - - nr += subdir_written; + nr = subdir_written - 1; /* Now we need to write out the directory entry into this tree.. */ - mode = S_IFDIR; pathlen = dirname - pathname; - - /* ..but the directory entry doesn't count towards the total count */ - nr--; - sha1 = oid->id; + write_oid = &subtree_oid; + write_mode = S_IFDIR; } - + entrylen = pathlen - baselen; - if (offset + entrylen + 100 > size) { - size = alloc_nr(offset + entrylen + 100); + if (offset + entrylen + 32 > size) { + size = alloc_nr(offset + entrylen + 32); buffer = git__realloc(buffer, size); if (buffer == NULL) return GIT_ENOMEM; } - offset += sprintf(buffer + offset, "%o %.*s", mode, entrylen, filename); - buffer[offset++] = 0; - memcpy(buffer + offset, sha1, GIT_OID_RAWSZ); - offset += GIT_OID_RAWSZ; - nr++; - } while (nr < maxentries); - - if ((error = git_odb_open_wstream(&stream, repo->db, offset, GIT_OBJ_TREE)) < GIT_SUCCESS) - return error; - - stream->write(stream, buffer, offset); - error = stream->finalize_write(oid, stream); - stream->free(stream); - return nr; + offset += write_entry(buffer + offset, write_mode, filename, entrylen, write_oid); + } + + error = git_odb_write(oid, index->repository->db, buffer, offset, GIT_OBJ_TREE); + free(buffer); + + return (error == GIT_SUCCESS) ? nr : error; +} + +int git_tree_create_fromindex(git_oid *oid, git_index *index) +{ + int error; + + if (index->repository == NULL) + return GIT_EBAREINDEX; + + error = write_index(oid, index, "", 0, 0, git_index_entrycount(index)); + return (error < GIT_SUCCESS) ? error : GIT_SUCCESS; } From d69d0185d147a435e2851b1d9f38dddc838959f9 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 4 Apr 2011 13:05:20 +0300 Subject: [PATCH 18/21] Add a fake wstream to the ODB Streaming writes will no longer fail when writing to a backend that doesn't support streaming writes but supports direct ones. Now we create a fake stream on memory and then write it as a single block using the backend `write` callback. --- src/odb.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/odb.c b/src/odb.c index 33d5468d9..43d0ef9a4 100644 --- a/src/odb.c +++ b/src/odb.c @@ -149,6 +149,69 @@ int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type) return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, &raw); } +/** + * FAKE WSTREAM + */ + +typedef struct { + git_odb_stream stream; + char *buffer; + size_t size, written; + git_otype type; +} fake_wstream; + +static int fake_wstream__fwrite(git_oid *oid, git_odb_stream *_stream) +{ + fake_wstream *stream = (fake_wstream *)_stream; + return _stream->backend->write(oid, _stream->backend, stream->buffer, stream->size, stream->type); +} + +static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len) +{ + fake_wstream *stream = (fake_wstream *)_stream; + + if (stream->written + len >= stream->size) + return GIT_ENOMEM; + + memcpy(stream->buffer + stream->written, data, len); + stream->written += len; + return GIT_SUCCESS; +} + +static void fake_wstream__free(git_odb_stream *_stream) +{ + fake_wstream *stream = (fake_wstream *)_stream; + + free(stream->buffer); + free(stream); +} + +static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, size_t size, git_otype type) +{ + fake_wstream *stream; + + stream = git__calloc(1, sizeof(fake_wstream)); + if (stream == NULL) + return GIT_ENOMEM; + + stream->size = size; + stream->type = type; + stream->buffer = git__malloc(size); + if (stream->buffer == NULL) { + free(stream); + return GIT_ENOMEM; + } + + stream->stream.backend = backend; + stream->stream.read = NULL; /* read only */ + stream->stream.write = &fake_wstream__write; + stream->stream.finalize_write = &fake_wstream__fwrite; + stream->stream.free = &fake_wstream__free; + stream->stream.mode = GIT_STREAM_WRONLY; + + *stream_p = (git_odb_stream *)stream; + return GIT_SUCCESS; +} /*********************************************************** * @@ -467,6 +530,8 @@ int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_ if (b->writestream != NULL) error = b->writestream(stream, b, size, type); + else if (b->write != NULL) + error = init_fake_wstream(stream, b, size, type); } return error; From f3564e1e29c065f980e09d78349590ef2ab357b1 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sun, 3 Apr 2011 13:50:09 +0200 Subject: [PATCH 19/21] Fix tag reference name in testrepo.git The git test repository was holding a wrongly named tag reference ("very-simple") pointing at a tag named "e90810b". This mistake (mine :-/ ) originates back to https://github.com/libgit2/libgit2/commit/9282e92 Whole credit goes to @tclem for having spotted this. --- .../testrepo.git/refs/tags/{very-simple => e90810b} | Bin tests/t08-tag.c | 12 ++++++------ tests/t10-refs.c | 7 ++++++- 3 files changed, 12 insertions(+), 7 deletions(-) rename tests/resources/testrepo.git/refs/tags/{very-simple => e90810b} (100%) diff --git a/tests/resources/testrepo.git/refs/tags/very-simple b/tests/resources/testrepo.git/refs/tags/e90810b similarity index 100% rename from tests/resources/testrepo.git/refs/tags/very-simple rename to tests/resources/testrepo.git/refs/tags/e90810b diff --git a/tests/t08-tag.c b/tests/t08-tag.c index 5bd3e14b0..e7016488f 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -177,7 +177,7 @@ BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already must_fail(git_tag_create( &tag_id, /* out id */ repo, - "very-simple", + "e90810b", &target_id, GIT_OBJ_COMMIT, tagger, @@ -199,7 +199,7 @@ BEGIN_TEST(write3, "Replace an already existing tag") git_oid_mkstr(&target_id, tagged_commit); - must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple")); + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b")); git_oid_cpy(&old_tag_id, git_reference_oid(ref_tag)); /* create signature */ @@ -209,7 +209,7 @@ BEGIN_TEST(write3, "Replace an already existing tag") must_pass(git_tag_create_f( &tag_id, /* out id */ repo, - "very-simple", + "e90810b", &target_id, GIT_OBJ_COMMIT, tagger, @@ -217,7 +217,7 @@ BEGIN_TEST(write3, "Replace an already existing tag") git_signature_free((git_signature *)tagger); - must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple")); + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b")); must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &tag_id) == 0); must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &old_tag_id) != 0); @@ -231,9 +231,9 @@ BEGIN_TEST(write4, "Delete an already existing tag") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); - must_pass(git_tag_delete(repo,"very-simple")); + must_pass(git_tag_delete(repo,"e90810b")); - must_fail(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple")); + must_fail(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b")); close_temp_repo(repo); diff --git a/tests/t10-refs.c b/tests/t10-refs.c index b1124b36f..2797cd8ac 100644 --- a/tests/t10-refs.c +++ b/tests/t10-refs.c @@ -27,13 +27,14 @@ #include "repository.h" -static const char *loose_tag_ref_name = "refs/tags/test"; +static const char *loose_tag_ref_name = "refs/tags/e90810b"; static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist"; BEGIN_TEST(readtag0, "lookup a loose tag reference") git_repository *repo; git_reference *reference; git_object *object; + char ref_name_from_tag_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); @@ -46,6 +47,10 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference") must_be_true(object != NULL); must_be_true(git_object_type(object) == GIT_OBJ_TAG); + /* Ensure the name of the tag matches the name of the reference */ + git__joinpath(ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)); + must_be_true(strcmp(ref_name_from_tag_name, loose_tag_ref_name) == 0); + git_repository_free(repo); END_TEST From b153589be2b3b9aea1f90a68891564ddeb12083c Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sun, 3 Apr 2011 18:31:45 +0200 Subject: [PATCH 20/21] Make reinitializing a repository return GIT_ENOTIMPLEMENTED instead of GIT_SUCCESS --- src/repository.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index 91b95a881..abbbd126a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -346,7 +346,7 @@ static int repo_init_reinit(repo_init *results) { /* TODO: reinit the repository */ results->has_been_reinit = 1; - return GIT_SUCCESS; + return GIT_ENOTIMPLEMENTED; } static int repo_init_createhead(git_repository *repo) From 0ad6efa110853763894b60e4c454985a726968da Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 4 Apr 2011 19:24:19 +0300 Subject: [PATCH 21/21] Build & write custom trees in memory --- include/git2/tree.h | 117 ++++++++++++++++++++-- include/git2/types.h | 3 + src/tree.c | 227 +++++++++++++++++++++++++++++++++++++++++-- src/tree.h | 8 ++ tests/t09-tree.c | 53 +++++++++- 5 files changed, 391 insertions(+), 17 deletions(-) diff --git a/include/git2/tree.h b/include/git2/tree.h index 60150b309..0caf60a48 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -93,7 +93,7 @@ GIT_EXTERN(size_t) git_tree_entrycount(git_tree *tree); * @param filename the filename of the desired entry * @return the tree entry; NULL if not found */ -GIT_EXTERN(git_tree_entry *) git_tree_entry_byname(git_tree *tree, const char *filename); +GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(git_tree *tree, const char *filename); /** * Lookup a tree entry by its position in the tree @@ -102,7 +102,7 @@ GIT_EXTERN(git_tree_entry *) git_tree_entry_byname(git_tree *tree, const char *f * @param idx the position in the entry list * @return the tree entry; NULL if not found */ -GIT_EXTERN(git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx); +GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx); /** * Get the UNIX file attributes of a tree entry @@ -110,7 +110,7 @@ GIT_EXTERN(git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx); * @param entry a tree entry * @return attributes as an integer */ -GIT_EXTERN(unsigned int) git_tree_entry_attributes(git_tree_entry *entry); +GIT_EXTERN(unsigned int) git_tree_entry_attributes(const git_tree_entry *entry); /** * Get the filename of a tree entry @@ -118,7 +118,7 @@ GIT_EXTERN(unsigned int) git_tree_entry_attributes(git_tree_entry *entry); * @param entry a tree entry * @return the name of the file */ -GIT_EXTERN(const char *) git_tree_entry_name(git_tree_entry *entry); +GIT_EXTERN(const char *) git_tree_entry_name(const git_tree_entry *entry); /** * Get the id of the object pointed by the entry @@ -126,7 +126,7 @@ GIT_EXTERN(const char *) git_tree_entry_name(git_tree_entry *entry); * @param entry a tree entry * @return the oid of the object */ -GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry); +GIT_EXTERN(const git_oid *) git_tree_entry_id(const git_tree_entry *entry); /** * Convert a tree entry to the git_object it points too. @@ -136,7 +136,7 @@ GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry); * @param entry a tree entry * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry); +GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry); /** * Write a tree to the ODB from the index file @@ -156,6 +156,111 @@ GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository * */ GIT_EXTERN(int) git_tree_create_fromindex(git_oid *oid, git_index *index); +/** + * Create a new tree builder. + * + * The tree builder can be used to create or modify + * trees in memory and write them as tree objects to the + * database. + * + * If the `source` parameter is not NULL, the tree builder + * will be initialized with the entries of the given tree. + * + * If the `source` parameter is NULL, the tree builder will + * have no entries and will have to be filled manually. + * + * @param builder_p Pointer where to store the tree builder + * @param source Source tree to initialize the builder (optional) + * @return 0 on sucess; error code otherwise + */ +GIT_EXTERN(int) git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source); + +/** + * Clear all the entires in the builder + * + * @param bld Builder to clear + */ +GIT_EXTERN(void) git_treebuilder_clear(git_treebuilder *bld); + +/** + * Free a tree builder + * + * This will clear all the entries and free to builder. + * Failing to free the builder after you're done using it + * will result in a memory leak + * + * @param bld Builder to free + */ +GIT_EXTERN(void) git_treebuilder_free(git_treebuilder *bld); + +/** + * Get an entry from the builder from its filename + * + * The returned entry is owned by the builder and should + * not be freed manually. + * + * @param bld Tree builder + * @param filename Name of the entry + * @return pointer to the entry; NULL if not found + */ +GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(git_treebuilder *bld, const char *filename); + +/** + * Add or update an entry to the builder + * + * Insert a new entry for `filename` in the builder with the + * given attributes. + * + * if an entry named `filename` already exists, its attributes + * will be updated with the given ones. + * + * The optional pointer `entry_out` can be used to retrieve a + * pointer to the newly created/updated entry. + * + * @param entry_out Pointer to store the entry (optional) + * @param bld Tree builder + * @param filename Filename of the entry + * @param id SHA1 oid of the entry + * @param attributes Folder attributes of the entry + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes); + +/** + * Remove an entry from the builder by its filename + * + * @param bld Tree builder + * @param filename Filename of the entry to remove + */ +GIT_EXTERN(int) git_treebuilder_remove(git_treebuilder *bld, const char *filename); + +/** + * Filter the entries in the tree + * + * The `filter` callback will be called for each entry + * in the tree with a pointer to the entry and the + * provided `payload`: if the callback returns 1, the + * entry will be filtered (removed from the builder). + * + * @param bld Tree builder + * @param filter Callback to filter entries + */ +GIT_EXTERN(void) git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload); + +/** + * Write the contents of the tree builder as a tree object + * + * The tree builder will be written to the given `repo`, and + * it's identifying SHA1 hash will be stored in the `oid` + * pointer. + * + * @param oid Pointer where to store the written OID + * @param repo Repository where to store the object + * @param bld Tree builder to write + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/types.h b/include/git2/types.h index 88f6b7d55..6123abc82 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -124,6 +124,9 @@ typedef struct git_tree_entry git_tree_entry; /** Representation of a tree object. */ typedef struct git_tree git_tree; +/** Constructor for in-memory trees */ +typedef struct git_treebuilder git_treebuilder; + /** Memory representation of an index file. */ typedef struct git_index git_index; diff --git a/src/tree.c b/src/tree.c index 588b5e9a0..693ed1caa 100644 --- a/src/tree.c +++ b/src/tree.c @@ -33,6 +33,10 @@ #define MAX_FILEMODE 0777777 #define MAX_FILEMODE_BYTES 6 +static int valid_attributes(const int attributes) { + return attributes >= 0 && attributes <= MAX_FILEMODE; +} + int entry_search_cmp(const void *key, const void *array_member) { const char *filename = (const char *)key; @@ -79,30 +83,30 @@ const git_oid *git_tree_id(git_tree *c) return git_object_id((git_object *)c); } -unsigned int git_tree_entry_attributes(git_tree_entry *entry) +unsigned int git_tree_entry_attributes(const git_tree_entry *entry) { return entry->attr; } -const char *git_tree_entry_name(git_tree_entry *entry) +const char *git_tree_entry_name(const git_tree_entry *entry) { assert(entry); return entry->filename; } -const git_oid *git_tree_entry_id(git_tree_entry *entry) +const git_oid *git_tree_entry_id(const git_tree_entry *entry) { assert(entry); return &entry->oid; } -int git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry) +int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry) { assert(entry && object_out); return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY); } -git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) +const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) { int idx; @@ -115,7 +119,7 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) return git_vector_get(&tree->entries, idx); } -git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx) +const git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx) { assert(tree); return git_vector_get(&tree->entries, (unsigned int)idx); @@ -159,6 +163,7 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf } entry->filename = git__strdup(buffer); + entry->filename_len = strlen(buffer); while (buffer < buffer_end && *buffer != 0) buffer++; @@ -178,7 +183,7 @@ int git_tree__parse(git_tree *tree, git_odb_object *obj) return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); } -static int write_entry(char *buffer, int mode, const char *path, size_t path_len, const git_oid *oid) +static int write_index_entry(char *buffer, int mode, const char *path, size_t path_len, const git_oid *oid) { int written; written = sprintf(buffer, "%o %.*s%c", mode, (int)path_len, path, 0); @@ -254,7 +259,7 @@ static int write_index(git_oid *oid, git_index *index, const char *base, int bas return GIT_ENOMEM; } - offset += write_entry(buffer + offset, write_mode, filename, entrylen, write_oid); + offset += write_index_entry(buffer + offset, write_mode, filename, entrylen, write_oid); } error = git_odb_write(oid, index->repository->db, buffer, offset, GIT_OBJ_TREE); @@ -273,3 +278,209 @@ int git_tree_create_fromindex(git_oid *oid, git_index *index) error = write_index(oid, index, "", 0, 0, git_index_entrycount(index)); return (error < GIT_SUCCESS) ? error : GIT_SUCCESS; } + +static void sort_entries(git_treebuilder *bld) +{ + git_vector_sort(&bld->entries); +} + +int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) +{ + git_treebuilder *bld; + size_t i, source_entries = DEFAULT_TREE_SIZE; + + assert(builder_p); + + bld = git__calloc(1, sizeof(git_treebuilder)); + if (bld == NULL) + return GIT_ENOMEM; + + if (source != NULL) + source_entries = source->entries.length; + + if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < GIT_SUCCESS) { + free(bld); + return GIT_ENOMEM; + } + + if (source != NULL) { + bld->entry_count = source_entries; + for (i = 0; i < source->entries.length; ++i) { + git_tree_entry *entry_src = source->entries.contents[i]; + git_tree_entry *entry = git__calloc(1, sizeof(git_tree_entry)); + + if (entry == NULL) { + git_treebuilder_free(bld); + return GIT_ENOMEM; + } + + entry->filename = git__strdup(entry_src->filename); + + if (entry->filename == NULL) { + free(entry); + git_treebuilder_free(bld); + return GIT_ENOMEM; + } + + entry->filename_len = entry_src->filename_len; + git_oid_cpy(&entry->oid, &entry_src->oid); + entry->attr = entry_src->attr; + + git_vector_insert(&bld->entries, entry); + } + } + + *builder_p = bld; + return GIT_SUCCESS; +} + +int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes) +{ + git_tree_entry *entry; + int pos; + + assert(bld && id && filename); + + if (!valid_attributes(attributes)) + return GIT_ERROR; + + if ((pos = git_vector_bsearch2(&bld->entries, entry_search_cmp, filename)) != GIT_ENOTFOUND) { + entry = git_vector_get(&bld->entries, pos); + if (entry->removed) { + entry->removed = 0; + bld->entry_count++; + } + } else { + if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL) + return GIT_ENOMEM; + + memset(entry, 0x0, sizeof(git_tree_entry)); + entry->filename = git__strdup(filename); + entry->filename_len = strlen(entry->filename); + + bld->entry_count++; + } + + git_oid_cpy(&entry->oid, id); + entry->attr = attributes; + + if (pos != GIT_ENOTFOUND) { + if (git_vector_insert(&bld->entries, entry) < 0) + return GIT_ENOMEM; + } + + if (entry_out != NULL) + *entry_out = entry; + + return GIT_SUCCESS; +} + +const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename) +{ + int idx; + git_tree_entry *entry; + + assert(bld && filename); + + sort_entries(bld); + idx = git_vector_bsearch2(&bld->entries, entry_search_cmp, filename); + if (idx == GIT_ENOTFOUND) + return NULL; + + entry = git_vector_get(&bld->entries, idx); + if (entry->removed) + return NULL; + + return entry; +} + +int git_treebuilder_remove(git_treebuilder *bld, const char *filename) +{ + git_tree_entry *remove_ptr = (git_tree_entry *)git_treebuilder_get(bld, filename); + + if (remove_ptr == NULL || remove_ptr->removed) + return GIT_ENOTFOUND; + + remove_ptr->removed = 1; + bld->entry_count--; + return GIT_SUCCESS; +} + +int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld) +{ + size_t i, size = 0; + char filemode[MAX_FILEMODE_BYTES + 1 + 1]; + git_odb_stream *stream; + int error; + + assert(bld); + + sort_entries(bld); + + for (i = 0; i < bld->entries.length; ++i) { + git_tree_entry *entry = bld->entries.contents[i]; + + if (entry->removed) + continue; + + size += (entry->attr > 0x7FF) ? 7 : 6; + size += entry->filename_len + 1; + size += GIT_OID_RAWSZ; + } + + if ((error = git_odb_open_wstream(&stream, git_repository_database(repo), size, GIT_OBJ_TREE)) < GIT_SUCCESS) + return error; + + for (i = 0; i < bld->entries.length; ++i) { + git_tree_entry *entry = bld->entries.contents[i]; + + if (entry->removed) + continue; + + snprintf(filemode, sizeof(filemode), "%o ", entry->attr); + stream->write(stream, filemode, strlen(filemode)); + stream->write(stream, entry->filename, entry->filename_len + 1); + stream->write(stream, (char *)entry->oid.id, GIT_OID_RAWSZ); + } + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + return error; +} + +void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload) +{ + size_t i; + + assert(bld && filter); + + for (i = 0; i < bld->entries.length; ++i) { + git_tree_entry *entry = bld->entries.contents[i]; + if (!entry->removed && filter(entry, payload)) + entry->removed = 1; + } +} + +void git_treebuilder_clear(git_treebuilder *bld) +{ + size_t i; + assert(bld); + + for (i = 0; i < bld->entries.length; ++i) { + git_tree_entry *e = bld->entries.contents[i]; + free(e->filename); + free(e); + } + + git_vector_clear(&bld->entries); +} + +void git_treebuilder_free(git_treebuilder *bld) +{ + git_treebuilder_clear(bld); + git_vector_free(&bld->entries); + free(bld); +} + + diff --git a/src/tree.h b/src/tree.h index b4e910a9f..bff3f8edb 100644 --- a/src/tree.h +++ b/src/tree.h @@ -10,6 +10,8 @@ struct git_tree_entry { unsigned int attr; char *filename; git_oid oid; + size_t filename_len; + int removed; }; struct git_tree { @@ -17,6 +19,12 @@ struct git_tree { git_vector entries; }; +struct git_treebuilder { + git_vector entries; + size_t entry_count; +}; + + void git_tree__free(git_tree *tree); int git_tree__parse(git_tree *tree, git_odb_object *obj); diff --git a/tests/t09-tree.c b/tests/t09-tree.c index 6c1b2e643..e4252dbca 100644 --- a/tests/t09-tree.c +++ b/tests/t09-tree.c @@ -29,6 +29,36 @@ static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; +#if 0 +static int print_tree(git_repository *repo, const git_oid *tree_oid, int depth) +{ + static const char *indent = " "; + git_tree *tree; + unsigned int i; + + if (git_tree_lookup(&tree, repo, tree_oid) < GIT_SUCCESS) + return GIT_ERROR; + + for (i = 0; i < git_tree_entrycount(tree); ++i) { + const git_tree_entry *entry = git_tree_entry_byindex(tree, i); + char entry_oid[40]; + + git_oid_fmt(entry_oid, &entry->oid); + printf("%.*s%o [%.*s] %s\n", depth*2, indent, entry->attr, 40, entry_oid, entry->filename); + + if (entry->attr == S_IFDIR) { + if (print_tree(repo, &entry->oid, depth + 1) < GIT_SUCCESS) { + git_tree_close(tree); + return GIT_ERROR; + } + } + } + + git_tree_close(tree); + return GIT_SUCCESS; +} +#endif + BEGIN_TEST(read0, "acces randomly the entries on a loaded tree") git_oid id; git_repository *repo; @@ -55,7 +85,7 @@ BEGIN_TEST(read1, "read a tree from the repository") git_oid id; git_repository *repo; git_tree *tree; - git_tree_entry *entry; + const git_tree_entry *entry; git_object *obj; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); @@ -80,10 +110,27 @@ BEGIN_TEST(read1, "read a tree from the repository") git_repository_free(repo); END_TEST +#if 0 +BEGIN_TEST(write0, "write a tree from an index") + git_repository *repo; + git_index *index; + git_oid tree_oid; + + must_pass(git_repository_open(&repo, "/tmp/redtmp/.git")); + must_pass(git_repository_index(&index, repo)); + + must_pass(git_tree_create_fromindex(&tree_oid, index)); + must_pass(print_tree(repo, &tree_oid, 0)); + + git_repository_free(repo); +END_TEST +#endif + BEGIN_SUITE(tree) + //ADD_TEST(print0); ADD_TEST(read0); ADD_TEST(read1); -// ADD_TEST(write0); /* TODO THREADSAFE */ -// ADD_TEST(write1); + //ADD_TEST(write0); + //ADD_TEST(write1); END_SUITE