From bc9f2925fba2fee13f75bf0f149c0da7a4688bc2 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 5 Dec 2014 00:08:13 +0100 Subject: refs.c: make ref_transaction_create a wrapper for ref_transaction_update The ref_transaction_update function can already be used to create refs by passing null_sha1 as the old_sha1 parameter. Simplify by replacing transaction_create with a thin wrapper. Signed-off-by: Ronnie Sahlberg Signed-off-by: Stefan Beller Reviewed-by: Michael Haggerty Reviewed-by: Jonathan Nieder Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 5ff457ebfc..005eb182d9 100644 --- a/refs.c +++ b/refs.c @@ -3623,31 +3623,8 @@ int ref_transaction_create(struct ref_transaction *transaction, int flags, const char *msg, struct strbuf *err) { - struct ref_update *update; - - assert(err); - - if (transaction->state != REF_TRANSACTION_OPEN) - die("BUG: create called for transaction that is not open"); - - if (!new_sha1 || is_null_sha1(new_sha1)) - die("BUG: create ref with null new_sha1"); - - if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { - strbuf_addf(err, "refusing to create ref with bad name %s", - refname); - return -1; - } - - update = add_update(transaction, refname); - - hashcpy(update->new_sha1, new_sha1); - hashclr(update->old_sha1); - update->flags = flags; - update->have_old = 1; - if (msg) - update->msg = xstrdup(msg); - return 0; + return ref_transaction_update(transaction, refname, new_sha1, + null_sha1, flags, 1, msg, err); } int ref_transaction_delete(struct ref_transaction *transaction, -- cgit v1.2.1 From a785d3f77c9922846f25d09fb04af9a3400ffbd1 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 5 Dec 2014 00:08:14 +0100 Subject: refs.c: make ref_transaction_delete a wrapper for ref_transaction_update Signed-off-by: Ronnie Sahlberg Signed-off-by: Stefan Beller Reviewed-by: Michael Haggerty Reviewed-by: Jonathan Nieder Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 005eb182d9..05cb299f53 100644 --- a/refs.c +++ b/refs.c @@ -3633,26 +3633,8 @@ int ref_transaction_delete(struct ref_transaction *transaction, int flags, int have_old, const char *msg, struct strbuf *err) { - struct ref_update *update; - - assert(err); - - if (transaction->state != REF_TRANSACTION_OPEN) - die("BUG: delete called for transaction that is not open"); - - if (have_old && !old_sha1) - die("BUG: have_old is true but old_sha1 is NULL"); - - update = add_update(transaction, refname); - update->flags = flags; - update->have_old = have_old; - if (have_old) { - assert(!is_null_sha1(old_sha1)); - hashcpy(update->old_sha1, old_sha1); - } - if (msg) - update->msg = xstrdup(msg); - return 0; + return ref_transaction_update(transaction, refname, null_sha1, + old_sha1, flags, have_old, msg, err); } int update_ref(const char *action, const char *refname, -- cgit v1.2.1 From 2c6207abbd6c430be06ddfef97d145d7e21446fb Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 12 Dec 2014 09:56:42 +0100 Subject: refs.c: add a function to append a reflog entry to a fd Break out the code to create the string and writing it to the file descriptor from log_ref_write and add it into a dedicated function log_ref_write_fd. It is a nice unit of work. For now this is only used from log_ref_write, but in the future it might have other callers. Signed-off-by: Ronnie Sahlberg Signed-off-by: Stefan Beller Reviewed-by: Jonathan Nieder Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 05cb299f53..150c980247 100644 --- a/refs.c +++ b/refs.c @@ -2990,15 +2990,37 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize) return 0; } +static int log_ref_write_fd(int fd, const unsigned char *old_sha1, + const unsigned char *new_sha1, + const char *committer, const char *msg) +{ + int msglen, written; + unsigned maxlen, len; + char *logrec; + + msglen = msg ? strlen(msg) : 0; + maxlen = strlen(committer) + msglen + 100; + logrec = xmalloc(maxlen); + len = sprintf(logrec, "%s %s %s\n", + sha1_to_hex(old_sha1), + sha1_to_hex(new_sha1), + committer); + if (msglen) + len += copy_msg(logrec + len - 1, msg) - 1; + + written = len <= maxlen ? write_in_full(fd, logrec, len) : -1; + free(logrec); + if (written != len) + return -1; + + return 0; +} + static int log_ref_write(const char *refname, const unsigned char *old_sha1, const unsigned char *new_sha1, const char *msg) { - int logfd, result, written, oflags = O_APPEND | O_WRONLY; - unsigned maxlen, len; - int msglen; + int logfd, result, oflags = O_APPEND | O_WRONLY; char log_file[PATH_MAX]; - char *logrec; - const char *committer; if (log_all_ref_updates < 0) log_all_ref_updates = !is_bare_repository(); @@ -3010,19 +3032,9 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1, logfd = open(log_file, oflags); if (logfd < 0) return 0; - msglen = msg ? strlen(msg) : 0; - committer = git_committer_info(0); - maxlen = strlen(committer) + msglen + 100; - logrec = xmalloc(maxlen); - len = sprintf(logrec, "%s %s %s\n", - sha1_to_hex(old_sha1), - sha1_to_hex(new_sha1), - committer); - if (msglen) - len += copy_msg(logrec + len - 1, msg) - 1; - written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1; - free(logrec); - if (written != len) { + result = log_ref_write_fd(logfd, old_sha1, new_sha1, + git_committer_info(0), msg); + if (result) { int save_errno = errno; close(logfd); error("Unable to append to %s", log_file); -- cgit v1.2.1 From fa5b1830b0605b39295631f6e167ddb133f2dbde Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 12 Dec 2014 09:56:59 +0100 Subject: reflog_expire(): new function in the reference API Move expire_reflog() into refs.c and rename it to reflog_expire(). Turn the three policy functions into function pointers that are passed into reflog_expire(). Add function prototypes and documentation to refs.h. [jc: squashed in $gmane/261582, drop "extern" in function definition] Signed-off-by: Michael Haggerty Reviewed-by: Stefan Beller Tweaked-by: Ramsay Jones Signed-off-by: Junio C Hamano --- refs.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 150c980247..4b27a1ba8e 100644 --- a/refs.c +++ b/refs.c @@ -3943,3 +3943,132 @@ int ref_is_hidden(const char *refname) } return 0; } + +struct expire_reflog_cb { + unsigned int flags; + reflog_expiry_should_prune_fn *should_prune_fn; + void *policy_cb; + FILE *newlog; + unsigned char last_kept_sha1[20]; +}; + +static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, int tz, + const char *message, void *cb_data) +{ + struct expire_reflog_cb *cb = cb_data; + struct expire_reflog_policy_cb *policy_cb = cb->policy_cb; + + if (cb->flags & EXPIRE_REFLOGS_REWRITE) + osha1 = cb->last_kept_sha1; + + if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz, + message, policy_cb)) { + if (!cb->newlog) + printf("would prune %s", message); + else if (cb->flags & EXPIRE_REFLOGS_VERBOSE) + printf("prune %s", message); + } else { + if (cb->newlog) { + char sign = (tz < 0) ? '-' : '+'; + int zone = (tz < 0) ? (-tz) : tz; + fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s", + sha1_to_hex(osha1), sha1_to_hex(nsha1), + email, timestamp, sign, zone, + message); + hashcpy(cb->last_kept_sha1, nsha1); + } + if (cb->flags & EXPIRE_REFLOGS_VERBOSE) + printf("keep %s", message); + } + return 0; +} + +int reflog_expire(const char *refname, const unsigned char *sha1, + unsigned int flags, + reflog_expiry_prepare_fn prepare_fn, + reflog_expiry_should_prune_fn should_prune_fn, + reflog_expiry_cleanup_fn cleanup_fn, + void *policy_cb_data) +{ + static struct lock_file reflog_lock; + struct expire_reflog_cb cb; + struct ref_lock *lock; + char *log_file; + int status = 0; + + memset(&cb, 0, sizeof(cb)); + cb.flags = flags; + cb.policy_cb = policy_cb_data; + cb.should_prune_fn = should_prune_fn; + + /* + * The reflog file is locked by holding the lock on the + * reference itself, plus we might need to update the + * reference if --updateref was specified: + */ + lock = lock_any_ref_for_update(refname, sha1, 0, NULL); + if (!lock) + return error("cannot lock ref '%s'", refname); + if (!reflog_exists(refname)) { + unlock_ref(lock); + return 0; + } + + log_file = git_pathdup("logs/%s", refname); + if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) { + /* + * Even though holding $GIT_DIR/logs/$reflog.lock has + * no locking implications, we use the lock_file + * machinery here anyway because it does a lot of the + * work we need, including cleaning up if the program + * exits unexpectedly. + */ + if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) { + struct strbuf err = STRBUF_INIT; + unable_to_lock_message(log_file, errno, &err); + error("%s", err.buf); + strbuf_release(&err); + goto failure; + } + cb.newlog = fdopen_lock_file(&reflog_lock, "w"); + if (!cb.newlog) { + error("cannot fdopen %s (%s)", + reflog_lock.filename.buf, strerror(errno)); + goto failure; + } + } + + (*prepare_fn)(refname, sha1, cb.policy_cb); + for_each_reflog_ent(refname, expire_reflog_ent, &cb); + (*cleanup_fn)(cb.policy_cb); + + if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) { + if (close_lock_file(&reflog_lock)) { + status |= error("couldn't write %s: %s", log_file, + strerror(errno)); + } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) && + (write_in_full(lock->lock_fd, + sha1_to_hex(cb.last_kept_sha1), 40) != 40 || + write_str_in_full(lock->lock_fd, "\n") != 1 || + close_ref(lock) < 0)) { + status |= error("couldn't write %s", + lock->lk->filename.buf); + rollback_lock_file(&reflog_lock); + } else if (commit_lock_file(&reflog_lock)) { + status |= error("unable to commit reflog '%s' (%s)", + log_file, strerror(errno)); + } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) && commit_ref(lock)) { + status |= error("couldn't set %s", lock->ref_name); + } + } + free(log_file); + unlock_ref(lock); + return status; + + failure: + rollback_lock_file(&reflog_lock); + free(log_file); + unlock_ref(lock); + return -1; +} -- cgit v1.2.1 From 0b1e654801e52243b63b57e27b50acdfe7c8f853 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 12 Dec 2014 09:57:00 +0100 Subject: refs.c: remove unlock_ref/close_ref/commit_ref from the refs api unlock|close|commit_ref can be made static since there are no more external callers. Signed-off-by: Ronnie Sahlberg Signed-off-by: Stefan Beller Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 4b27a1ba8e..bf152aab32 100644 --- a/refs.c +++ b/refs.c @@ -2090,6 +2090,16 @@ int refname_match(const char *abbrev_name, const char *full_name) return 0; } +static void unlock_ref(struct ref_lock *lock) +{ + /* Do not free lock->lk -- atexit() still looks at them */ + if (lock->lk) + rollback_lock_file(lock->lk); + free(lock->ref_name); + free(lock->orig_ref_name); + free(lock); +} + /* This function should make sure errno is meaningful on error */ static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) @@ -2888,7 +2898,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms return 1; } -int close_ref(struct ref_lock *lock) +static int close_ref(struct ref_lock *lock) { if (close_lock_file(lock->lk)) return -1; @@ -2896,7 +2906,7 @@ int close_ref(struct ref_lock *lock) return 0; } -int commit_ref(struct ref_lock *lock) +static int commit_ref(struct ref_lock *lock) { if (commit_lock_file(lock->lk)) return -1; @@ -2904,16 +2914,6 @@ int commit_ref(struct ref_lock *lock) return 0; } -void unlock_ref(struct ref_lock *lock) -{ - /* Do not free lock->lk -- atexit() still looks at them */ - if (lock->lk) - rollback_lock_file(lock->lk); - free(lock->ref_name); - free(lock->orig_ref_name); - free(lock); -} - /* * copy the reflog message msg to buf, which has been allocated sufficiently * large, while cleaning up the whitespaces. Especially, convert LF to space, -- cgit v1.2.1 From 31e07f76a946831265e741333a3d9e0646c793ad Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 12 Dec 2014 09:57:01 +0100 Subject: lock_any_ref_for_update(): inline function Inline the function at its one remaining caller (which is within refs.c) and remove it. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index bf152aab32..55a79429bd 100644 --- a/refs.c +++ b/refs.c @@ -2346,13 +2346,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, return NULL; } -struct ref_lock *lock_any_ref_for_update(const char *refname, - const unsigned char *old_sha1, - int flags, int *type_p) -{ - return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p); -} - /* * Write an entry to the packed-refs file for the specified refname. * If peeled is non-NULL, write it as the entry's peeled value. @@ -4007,7 +4000,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1, * reference itself, plus we might need to update the * reference if --updateref was specified: */ - lock = lock_any_ref_for_update(refname, sha1, 0, NULL); + lock = lock_ref_sha1_basic(refname, sha1, NULL, 0, NULL); if (!lock) return error("cannot lock ref '%s'", refname); if (!reflog_exists(refname)) { -- cgit v1.2.1 From 3581d793351dba4e4a2f779a72f39d6ae84c1c2c Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 12 Dec 2014 09:57:02 +0100 Subject: refs.c: don't expose the internal struct ref_lock in the header file Now the struct ref_lock is used completely internally, so let's remove it from the header file. Signed-off-by: Stefan Beller Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 55a79429bd..048fbbf50f 100644 --- a/refs.c +++ b/refs.c @@ -6,6 +6,15 @@ #include "dir.h" #include "string-list.h" +struct ref_lock { + char *ref_name; + char *orig_ref_name; + struct lock_file *lk; + unsigned char old_sha1[20]; + int lock_fd; + int force_write; +}; + /* * How to handle various characters in refnames: * 0: An acceptable character for refs -- cgit v1.2.1 From c653e0343d04ba89040c8ba18d7ca2e55ece6338 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 12 Dec 2014 09:57:03 +0100 Subject: refs.c: let fprintf handle the formatting Instead of calculating whether to put a plus or minus sign, offload the responsibilty to the fprintf function. Signed-off-by: Stefan Beller Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 048fbbf50f..14e52caea5 100644 --- a/refs.c +++ b/refs.c @@ -3972,12 +3972,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, printf("prune %s", message); } else { if (cb->newlog) { - char sign = (tz < 0) ? '-' : '+'; - int zone = (tz < 0) ? (-tz) : tz; - fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s", + fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s", sha1_to_hex(osha1), sha1_to_hex(nsha1), - email, timestamp, sign, zone, - message); + email, timestamp, tz, message); hashcpy(cb->last_kept_sha1, nsha1); } if (cb->flags & EXPIRE_REFLOGS_VERBOSE) -- cgit v1.2.1