Add a new transaction function transaction_rename_reflog. Signed-off-by: Ronnie Sahlberg <sahlberg@xxxxxxxxxx> --- refs.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- refs.h | 8 ++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/refs.c b/refs.c index e9e321e..8ca6add 100644 --- a/refs.c +++ b/refs.c @@ -35,6 +35,11 @@ static unsigned char refname_disposition[256] = { * just use the lock taken by the first update. */ #define UPDATE_REFLOG_NOLOCK 0x0200 +/* + * This update is used to replace a new or existing reflog with new content + * held in update->new_reflog. + */ +#define REFLOG_REPLACE 0x0400 /* * Try to read one refname component from the front of refname. @@ -2821,6 +2826,37 @@ static int rename_ref_available(const char *oldname, const char *newname) static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *logmsg); +/* + * This is an optimized function to read the whole reflog as a blob + * into a strbuf. It is used during ref_rename so that we can use an + * efficient method to read the whole log and later write it back to a + * different file. + */ +static int copy_reflog_into_strbuf(const char *refname, struct strbuf *buf) +{ + struct stat st; + int fd; + + if (lstat(git_path("logs/%s", refname), &st) == -1) + return 1; + if ((fd = open(git_path("logs/%s", refname), O_RDONLY)) == -1) { + error("failed to open reflog %s, %s", + refname, strerror(errno)); + return 1; + } + strbuf_init(buf, st.st_size); + strbuf_setlen(buf, st.st_size); + if (read_in_full(fd, buf->buf, st.st_size) != st.st_size) { + close(fd); + error("failed to read reflog %s, %s", + refname, strerror(errno)); + return 1; + } + close(fd); + + return 0; +} + int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg) { unsigned char sha1[20], orig_sha1[20]; @@ -3561,6 +3597,7 @@ struct ref_update { struct lock_file *reflog_lock; char *committer; struct ref_update *orig_update; /* For UPDATE_REFLOG_NOLOCK */ + struct strbuf new_reflog; const char refname[FLEX_ARRAY]; }; @@ -3607,6 +3644,7 @@ void transaction_free(struct transaction *transaction) return; for (i = 0; i < transaction->nr; i++) { + strbuf_release(&transaction->updates[i]->new_reflog); free(transaction->updates[i]->msg); free(transaction->updates[i]->committer); free(transaction->updates[i]); @@ -3622,6 +3660,7 @@ static struct ref_update *add_update(struct transaction *transaction, size_t len = strlen(refname); struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1); + strbuf_init(&update->new_reflog, 0); strcpy((char *)update->refname, refname); update->update_type = update_type; ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc); @@ -3681,6 +3720,27 @@ int transaction_update_reflog(struct transaction *transaction, return 0; } +int transaction_rename_reflog(struct transaction *transaction, + const char *oldrefname, + const char *newrefname, + struct strbuf *err) +{ + struct ref_update *update; + + if (transaction->state != TRANSACTION_OPEN) + die("BUG: transaction_replace_reflog called for transaction " + "that is not open"); + + update = add_update(transaction, newrefname, UPDATE_LOG); + update->flags = REFLOG_REPLACE; + update->reflog_lock = xcalloc(1, sizeof(struct lock_file)); + if (copy_reflog_into_strbuf(oldrefname, &update->new_reflog)) + die("BUG: failed to read old reflog in " + "transaction_rename_reflog"); + + return 0; +} + int transaction_update_ref(struct transaction *transaction, const char *refname, const unsigned char *new_sha1, @@ -4012,7 +4072,7 @@ int transaction_commit(struct transaction *transaction, continue; if (update->reflog_fd == -1) continue; - if (update->flags & REFLOG_TRUNCATE) + if (update->flags & (REFLOG_TRUNCATE|REFLOG_REPLACE)) if (lseek(update->reflog_fd, 0, SEEK_SET) < 0 || ftruncate(update->reflog_fd, 0)) { error("Could not truncate reflog: %s. %s", @@ -4030,6 +4090,16 @@ int transaction_commit(struct transaction *transaction, rollback_lock_file(update->reflog_lock); update->reflog_fd = -1; } + if (update->flags & REFLOG_REPLACE) + if (write_in_full(update->reflog_fd, + update->new_reflog.buf, + update->new_reflog.len) != + update->new_reflog.len) { + error("Could write to reflog: %s. %s", + update->refname, strerror(errno)); + rollback_lock_file(update->reflog_lock); + update->reflog_fd = -1; + } } /* Commit all reflog files */ diff --git a/refs.h b/refs.h index d174380..0ba078e 100644 --- a/refs.h +++ b/refs.h @@ -354,6 +354,14 @@ int transaction_update_reflog(struct transaction *transaction, struct strbuf *err); /* + * Rename a reflog. + */ +int transaction_rename_reflog(struct transaction *transaction, + const char *oldrefname, + const char *newrefname, + struct strbuf *err); + +/* * Commit all of the changes that have been queued in transaction, as * atomically as possible. * -- 2.1.0.rc2.206.gedb03e5 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html