diff options
| author | schu <schu-github@schulog.org> | 2011-06-28 14:13:12 +0200 |
|---|---|---|
| committer | Vicent Marti <tanoku@gmail.com> | 2011-07-09 02:40:16 +0200 |
| commit | 27df4275f2b3301f117f538929f51f0e86f9f716 (patch) | |
| tree | 0a3eebdec579d67f98be75a2637fa7aabee5ee77 /src/reflog.c | |
| parent | 6727e30028b66ab0037dc25a268db18484603251 (diff) | |
| download | libgit2-27df4275f2b3301f117f538929f51f0e86f9f716.tar.gz | |
reflog: add API to read or write a reference log
So far libgit2 didn't support reference logs (reflog). Add a new
git_reflog_* API for basic reading and writing of reflogs:
* git_reflog_read
* git_reflog_write
* git_reflog_free
Signed-off-by: schu <schu-github@schulog.org>
Diffstat (limited to 'src/reflog.c')
| -rw-r--r-- | src/reflog.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/src/reflog.c b/src/reflog.c new file mode 100644 index 000000000..63c4c5005 --- /dev/null +++ b/src/reflog.c @@ -0,0 +1,282 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "reflog.h" +#include "repository.h" +#include "filebuf.h" +#include "signature.h" + +static int reflog_init(git_reflog **reflog, git_reference *ref) +{ + git_reflog *log; + + *reflog = NULL; + + log = git__malloc(sizeof(git_reflog)); + if (log == NULL) + return GIT_ENOMEM; + + memset(log, 0x0, sizeof(git_reflog)); + + log->ref_name = git__strdup(ref->name); + + if (git_vector_init(&log->entries, 0, NULL) < 0) { + free(log->ref_name); + free(log); + return GIT_ENOMEM; + } + + *reflog = log; + + return GIT_SUCCESS; +} + +static int reflog_write(git_repository *repo, const char *ref_name, + const char *oid_old, const char *oid_new, + const git_signature *committer, const char *msg) +{ + int error; + char log_path[GIT_PATH_MAX]; + char *sig = NULL; + git_filebuf log; + + assert(repo && ref_name && oid_old && oid_new && committer); + + git_path_join_n(log_path, 3, repo->path_repository, GIT_REFLOG_DIR, ref_name); + + if (git_futils_exists(log_path)) { + if ((error = git_futils_mkpath2file(log_path)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to write reflog. Cannot create reflog directory"); + + } else if (git_futils_isfile(log_path)) + return git__throw(GIT_ERROR, "Failed to write reflog. `%s` is directory", log_path); + + if ((error = git_filebuf_open(&log, log_path, GIT_FILEBUF_APPEND)) < GIT_SUCCESS) + return git__throw(GIT_ERROR, "Failed to write reflog. Cannot open reflog `%s`", log_path); + + if ((error = git_signature__write(&sig, NULL, committer)) < GIT_SUCCESS) + goto cleanup; + + sig[strlen(sig)-1] = '\0'; /* drop LF */ + + if ((error = git_filebuf_printf(&log, "%s %s %s", oid_old, oid_new, sig)) < GIT_SUCCESS) + goto cleanup; + + if (msg) { + if (strchr(msg, '\n')) { + error = git__throw(GIT_ERROR, "msg must not contain newline"); + goto cleanup; + } + + if ((error = git_filebuf_printf(&log, "\t%s", msg)) < GIT_SUCCESS) + goto cleanup; + } + + error = git_filebuf_printf(&log, "\n"); + +cleanup: + if (error < GIT_SUCCESS) + git_filebuf_cleanup(&log); + else + error = git_filebuf_commit(&log); + + if (sig) + free(sig); + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog"); +} + +static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) +{ + int error; + const char *ptr; + git_reflog_entry *entry; + +#define seek_forward(_increase) { \ + if (_increase >= buf_size) \ + return git__throw(GIT_ERROR, "Failed to seek forward. Buffer size exceeded"); \ + buf += _increase; \ + buf_size -= _increase; \ +} + + while (buf_size > GIT_REFLOG_SIZE_MIN) { + entry = git__malloc(sizeof(git_reflog_entry)); + if (entry == NULL) + return GIT_ENOMEM; + + entry->oid_old = git__strndup(buf, GIT_OID_HEXSZ); + seek_forward(GIT_OID_HEXSZ+1); + + entry->oid_cur = git__strndup(buf, GIT_OID_HEXSZ); + seek_forward(GIT_OID_HEXSZ+1); + + ptr = buf; + + /* Seek forward to the end of the signature. */ + while (*buf && *buf != '\t' && *buf != '\n') + seek_forward(1); + + entry->committer = git__malloc(sizeof(git_signature)); + if (entry->committer == NULL) + return GIT_ENOMEM; + + if ((error = git_signature__parse(entry->committer, &ptr, buf + buf_size, NULL)) < GIT_SUCCESS) + goto cleanup; + + if (*buf == '\t') { + /* We got a message. Read everything till we reach LF. */ + seek_forward(1); + entry->msg = (char *)buf; + + while (*buf && *buf != '\n') + seek_forward(1); + + entry->msg = git__strndup(entry->msg, buf - entry->msg); + } else + entry->msg = NULL; + + while (*buf && *buf == '\n' && buf_size > 1) + seek_forward(1); + + if ((error = git_vector_insert(&log->entries, entry)) < GIT_SUCCESS) + goto cleanup; + } + +#undef seek_forward + +cleanup: + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse reflog"); +} + +void git_reflog_free(git_reflog *reflog) +{ + unsigned int i; + git_reflog_entry *entry; + + for (i=0; i < reflog->entries.length; i++) { + entry = git_vector_get(&reflog->entries, i); + + free(entry->oid_old); + free(entry->oid_cur); + + git_signature_free(entry->committer); + + free(entry->msg); + free(entry); + } + + git_vector_free(&reflog->entries); + free(reflog->ref_name); + free(reflog); +} + +int git_reflog_read(git_reflog **reflog, git_reference *ref) +{ + int error; + char log_path[GIT_PATH_MAX]; + git_fbuffer log_file = GIT_FBUFFER_INIT; + git_reflog *log = NULL; + + *reflog = NULL; + + if ((error = reflog_init(&log, ref)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read reflog. Cannot init reflog"); + + git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); + + if ((error = git_futils_readbuffer(&log_file, log_path)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path); + + error = reflog_parse(log, log_file.data, log_file.len); + + git_futils_freebuffer(&log_file); + + if (error == GIT_SUCCESS) + *reflog = log; + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read reflog"); +} + +int git_reflog_write(git_reference *ref, const git_oid *oid_old, + const git_signature *committer, const char *msg) +{ + int error; + char old[GIT_OID_HEXSZ+1]; + char new[GIT_OID_HEXSZ+1]; + git_reference *r; + const git_oid *oid; + + if ((error = git_reference_resolve(&r, ref)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to write reflog. Cannot resolve reference `%s`", ref->name); + + oid = git_reference_oid(r); + if (oid == NULL) + return git__throw(GIT_ERROR, "Failed to write reflog. Cannot resolve reference `%s`", r->name); + + git_oid_to_string(new, GIT_OID_HEXSZ+1, oid); + + if (oid_old) + git_oid_to_string(old, GIT_OID_HEXSZ+1, oid_old); + else + snprintf(old, GIT_OID_HEXSZ+1, "%0*d", GIT_OID_HEXSZ, 0); + + return reflog_write(ref->owner, ref->name, old, new, committer, msg); +} + +unsigned int git_reflog_entrycount(git_reflog *reflog) +{ + assert(reflog); + return reflog->entries.length; +} + +const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx) +{ + assert(reflog); + return git_vector_get(&reflog->entries, idx); +} + +char * git_reflog_entry_oidold(const git_reflog_entry *entry) +{ + assert(entry); + return entry->oid_old; +} + +char * git_reflog_entry_oidnew(const git_reflog_entry *entry) +{ + assert(entry); + return entry->oid_cur; +} + +git_signature * git_reflog_entry_committer(const git_reflog_entry *entry) +{ + assert(entry); + return entry->committer; +} + +char * git_reflog_entry_msg(const git_reflog_entry *entry) +{ + assert(entry); + return entry->msg; +} |
