diff options
author | Vicent Marti <tanoku@gmail.com> | 2012-07-27 18:39:40 +0200 |
---|---|---|
committer | Vicent Marti <tanoku@gmail.com> | 2012-07-27 18:39:40 +0200 |
commit | 43b67d496b87a9c6ce38a500b7f3121bf136942e (patch) | |
tree | 11be7d7c3290c598f3a4125df80c2a555130b4a1 /src | |
parent | c0c390255a70fb98f7ef9424c4ee53c471d7f22e (diff) | |
parent | c3be5c5af089683b6c61d1d37d8c2c40ff48e9a8 (diff) | |
download | libgit2-43b67d496b87a9c6ce38a500b7f3121bf136942e.tar.gz |
Merge remote-tracking branch 'nulltoken/topic/reflog-delete' into development
Diffstat (limited to 'src')
-rw-r--r-- | src/reflog.c | 321 | ||||
-rw-r--r-- | src/reflog.h | 3 |
2 files changed, 222 insertions, 102 deletions
diff --git a/src/reflog.c b/src/reflog.c index 004ba936d..f841b2174 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -28,66 +28,68 @@ static int reflog_init(git_reflog **reflog, git_reference *ref) return -1; } + log->owner = git_reference_owner(ref); *reflog = log; return 0; } -static int reflog_write(const char *log_path, const char *oid_old, - const char *oid_new, const git_signature *committer, - const char *msg) +static int serialize_reflog_entry( + git_buf *buf, + const git_oid *oid_old, + const git_oid *oid_new, + const git_signature *committer, + const char *msg) { - int error; - git_buf log = GIT_BUF_INIT; - git_filebuf fbuf = GIT_FILEBUF_INIT; - bool trailing_newline = false; + char raw_old[GIT_OID_HEXSZ+1]; + char raw_new[GIT_OID_HEXSZ+1]; - assert(log_path && oid_old && oid_new && committer); + git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old); + git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new); - if (msg) { - const char *newline = strchr(msg, '\n'); - if (newline) { - if (*(newline + 1) == '\0') - trailing_newline = true; - else { - giterr_set(GITERR_INVALID, "Reflog message cannot contain newline"); - return -1; - } - } - } + git_buf_clear(buf); - git_buf_puts(&log, oid_old); - git_buf_putc(&log, ' '); + git_buf_puts(buf, raw_old); + git_buf_putc(buf, ' '); + git_buf_puts(buf, raw_new); - git_buf_puts(&log, oid_new); + git_signature__writebuf(buf, " ", committer); - git_signature__writebuf(&log, " ", committer); - git_buf_truncate(&log, log.size - 1); /* drop LF */ + /* drop trailing LF */ + git_buf_rtrim(buf); if (msg) { - git_buf_putc(&log, '\t'); - git_buf_puts(&log, msg); + git_buf_putc(buf, '\t'); + git_buf_puts(buf, msg); } - if (!trailing_newline) - git_buf_putc(&log, '\n'); + git_buf_putc(buf, '\n'); - if (git_buf_oom(&log)) { - git_buf_free(&log); - return -1; - } + return git_buf_oom(buf); +} - error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND); - if (!error) { - if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0) - git_filebuf_cleanup(&fbuf); - else - error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE); - } +static int reflog_entry_new(git_reflog_entry **entry) +{ + git_reflog_entry *e; - git_buf_free(&log); + assert(entry); - return error; + e = git__malloc(sizeof(git_reflog_entry)); + GITERR_CHECK_ALLOC(e); + + memset(e, 0, sizeof(git_reflog_entry)); + + *entry = e; + + return 0; +} + +static void reflog_entry_free(git_reflog_entry *entry) +{ + git_signature_free(entry->committer); + + git__free(entry->msg); + git__free(entry); } static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) @@ -105,8 +107,8 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) } while (0) while (buf_size > GIT_REFLOG_SIZE_MIN) { - entry = git__malloc(sizeof(git_reflog_entry)); - GITERR_CHECK_ALLOC(entry); + if (reflog_entry_new(&entry) < 0) + return -1; entry->committer = git__malloc(sizeof(git_signature)); GITERR_CHECK_ALLOC(entry->committer); @@ -153,10 +155,9 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) #undef seek_forward fail: - if (entry) { - git__free(entry->committer); - git__free(entry); - } + if (entry) + reflog_entry_free(entry); + return -1; } @@ -168,10 +169,7 @@ void git_reflog_free(git_reflog *reflog) for (i=0; i < reflog->entries.length; i++) { entry = git_vector_get(&reflog->entries, i); - git_signature_free(entry->committer); - - git__free(entry->msg); - git__free(entry); + reflog_entry_free(entry); } git_vector_free(&reflog->entries); @@ -179,6 +177,24 @@ void git_reflog_free(git_reflog *reflog) git__free(reflog); } +static int retrieve_reflog_path(git_buf *path, git_reference *ref) +{ + return git_buf_join_n(path, '/', 3, + git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR, ref->name); +} + +int create_new_reflog_file(const char *filepath) +{ + int fd; + + if ((fd = p_open(filepath, + O_WRONLY | O_CREAT | O_TRUNC, + GIT_REFLOG_FILE_MODE)) < 0) + return -1; + + return p_close(fd); +} + int git_reflog_read(git_reflog **reflog, git_reference *ref) { int error; @@ -188,83 +204,136 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref) *reflog = NULL; + assert(reflog && ref); + if (reflog_init(&log, ref) < 0) return -1; - error = git_buf_join_n(&log_path, '/', 3, - ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); + if (retrieve_reflog_path(&log_path, ref) < 0) + goto cleanup; + + error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path)); + if (error < 0 && error != GIT_ENOTFOUND) + goto cleanup; + + if ((error == GIT_ENOTFOUND) && + ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0)) + goto cleanup; - if (!error) - error = git_futils_readbuffer(&log_file, log_path.ptr); + if ((error = reflog_parse(log, + git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0) + goto cleanup; - if (!error) - error = reflog_parse(log, log_file.ptr, log_file.size); + *reflog = log; + goto success; - if (!error) - *reflog = log; - else - git_reflog_free(log); +cleanup: + git_reflog_free(log); +success: git_buf_free(&log_file); git_buf_free(&log_path); return error; } -int git_reflog_write(git_reference *ref, const git_oid *oid_old, - const git_signature *committer, const char *msg) +int git_reflog_write(git_reflog *reflog) { - int error; - char old[GIT_OID_HEXSZ+1]; - char new[GIT_OID_HEXSZ+1]; + int error = -1; + unsigned int i; + git_reflog_entry *entry; git_buf log_path = GIT_BUF_INIT; - git_reference *r; - const git_oid *oid; + git_buf log = GIT_BUF_INIT; + git_filebuf fbuf = GIT_FILEBUF_INIT; + + assert(reflog); - if ((error = git_reference_resolve(&r, ref)) < 0) - return error; - oid = git_reference_oid(r); - if (oid == NULL) { - giterr_set(GITERR_REFERENCE, - "Failed to write reflog. Cannot resolve reference `%s`", r->name); - git_reference_free(r); + if (git_buf_join_n(&log_path, '/', 3, + git_repository_path(reflog->owner), GIT_REFLOG_DIR, reflog->ref_name) < 0) return -1; + + if (!git_path_isfile(git_buf_cstr(&log_path))) { + giterr_set(GITERR_INVALID, + "Log file for reference '%s' doesn't exist.", reflog->ref_name); + goto cleanup; + } + + if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0)) < 0) + goto cleanup; + + git_vector_foreach(&reflog->entries, i, entry) { + if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0) + goto cleanup; + + if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0) + goto cleanup; } + + error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE); + goto success; + +cleanup: + git_filebuf_cleanup(&fbuf); + +success: + git_buf_free(&log); + git_buf_free(&log_path); + return error; +} - git_oid_tostr(new, GIT_OID_HEXSZ+1, oid); +int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, + const git_signature *committer, const char *msg) +{ + int count; + git_reflog_entry *entry; + const char *newline; - git_reference_free(r); + assert(reflog && new_oid && committer); - error = git_buf_join_n(&log_path, '/', 3, - ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); - if (error < 0) + if (reflog_entry_new(&entry) < 0) + return -1; + + if ((entry->committer = git_signature_dup(committer)) == NULL) goto cleanup; - if (git_path_exists(log_path.ptr) == false) { - error = git_futils_mkpath2file(log_path.ptr, GIT_REFLOG_DIR_MODE); - } else if (git_path_isfile(log_path.ptr) == false) { - giterr_set(GITERR_REFERENCE, - "Failed to write reflog. `%s` is directory", log_path.ptr); - error = -1; - } else if (oid_old == NULL) { - giterr_set(GITERR_REFERENCE, - "Failed to write reflog. Old OID cannot be NULL for existing reference"); - error = -1; + if (msg != NULL) { + if ((entry->msg = git__strdup(msg)) == NULL) + goto cleanup; + + newline = strchr(msg, '\n'); + + if (newline) { + if (newline[1] != '\0') { + giterr_set(GITERR_INVALID, "Reflog message cannot contain newline"); + goto cleanup; + } + + entry->msg[newline - msg] = '\0'; + } } - if (error < 0) - goto cleanup; - if (oid_old) - git_oid_tostr(old, sizeof(old), oid_old); - else - p_snprintf(old, sizeof(old), "%0*d", GIT_OID_HEXSZ, 0); + count = git_reflog_entrycount(reflog); + + if (count == 0) + git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO); + else { + const git_reflog_entry *previous; - error = reflog_write(log_path.ptr, old, new, committer, msg); + previous = git_reflog_entry_byindex(reflog, count -1); + git_oid_cpy(&entry->oid_old, &previous->oid_cur); + } + + git_oid_cpy(&entry->oid_cur, new_oid); + + if (git_vector_insert(&reflog->entries, entry) < 0) + goto cleanup; + + return 0; cleanup: - git_buf_free(&log_path); - return error; + reflog_entry_free(entry); + return -1; } int git_reflog_rename(git_reference *ref, const char *new_name) @@ -276,7 +345,7 @@ int git_reflog_rename(git_reference *ref, const char *new_name) assert(ref && new_name); - if (git_buf_joinpath(&temp_path, ref->owner->path_repository, GIT_REFLOG_DIR) < 0) + if (git_buf_joinpath(&temp_path, git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR) < 0) return -1; if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), ref->name) < 0) @@ -324,8 +393,7 @@ int git_reflog_delete(git_reference *ref) int error; git_buf path = GIT_BUF_INIT; - error = git_buf_join_n( - &path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); + error = retrieve_reflog_path(&path, ref); if (!error && git_path_exists(path.ptr)) error = p_unlink(path.ptr); @@ -370,3 +438,52 @@ char * git_reflog_entry_msg(const git_reflog_entry *entry) assert(entry); return entry->msg; } + +int git_reflog_entry_drop( + git_reflog *reflog, + unsigned int idx, + int rewrite_previous_entry) +{ + unsigned int entrycount; + git_reflog_entry *entry, *previous; + + assert(reflog); + + entrycount = git_reflog_entrycount(reflog); + + if (idx >= entrycount) + return GIT_ENOTFOUND; + + entry = git_vector_get(&reflog->entries, idx); + reflog_entry_free(entry); + + if (git_vector_remove(&reflog->entries, idx) < 0) + return -1; + + if (!rewrite_previous_entry) + return 0; + + /* No need to rewrite anything when removing the first entry */ + if (idx == 0) + return 0; + + /* There are no more entries in the log */ + if (entrycount == 1) + return 0; + + entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx - 1); + + /* If the last entry has just been removed... */ + if (idx == entrycount - 1) { + /* ...clear the oid_old member of the "new" last entry */ + if (git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO) < 0) + return -1; + + return 0; + } + + previous = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx); + git_oid_cpy(&entry->oid_old, &previous->oid_cur); + + return 0; +} diff --git a/src/reflog.h b/src/reflog.h index 33cf0776c..3bbdf6e10 100644 --- a/src/reflog.h +++ b/src/reflog.h @@ -17,6 +17,8 @@ #define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17) +#define GIT_OID_HEX_ZERO "0000000000000000000000000000000000000000" + struct git_reflog_entry { git_oid oid_old; git_oid oid_cur; @@ -28,6 +30,7 @@ struct git_reflog_entry { struct git_reflog { char *ref_name; + git_repository *owner; git_vector entries; }; |