summaryrefslogtreecommitdiff
path: root/refs.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c28
1 files changed, 28 insertions, 0 deletions
diff --git a/refs.c b/refs.c
index 4f495bd762..7ce7b97ac9 100644
--- a/refs.c
+++ b/refs.c
@@ -3041,6 +3041,13 @@ static int write_ref_sha1(struct ref_lock *lock,
errno = EINVAL;
return -1;
}
+ if (lock->lk->fd == -1 && reopen_lock_file(lock->lk) == -1) {
+ int save_errno = errno;
+ error("Couldn't reopen %s", lock->lk->filename.buf);
+ unlock_ref(lock);
+ errno = save_errno;
+ return -1;
+ }
if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
write_in_full(lock->lk->fd, &term, 1) != 1 ||
close_ref(lock) < 0) {
@@ -3718,6 +3725,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err)
{
int ret = 0, i;
+ unsigned int remaining_fds;
int n = transaction->nr;
struct ref_update **updates = transaction->updates;
struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
@@ -3733,6 +3741,20 @@ int ref_transaction_commit(struct ref_transaction *transaction,
return 0;
}
+ /*
+ * We need to open many files in a large transaction, so come up with
+ * a reasonable maximum. We still keep some spares for stdin/out and
+ * other open files. Experiments determined we need more fds when
+ * running inside our test suite than directly in the shell. It's
+ * unclear where these fds come from. 25 should be a reasonable large
+ * number though.
+ */
+ remaining_fds = get_max_fd_limit();
+ if (remaining_fds > 25)
+ remaining_fds -= 25;
+ else
+ remaining_fds = 0;
+
/* Copy, sort, and reject duplicate refs */
qsort(updates, n, sizeof(*updates), ref_update_compare);
if (ref_update_reject_duplicates(updates, n, err)) {
@@ -3762,6 +3784,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
update->refname);
goto cleanup;
}
+ if (!(flags & REF_HAVE_NEW) ||
+ is_null_sha1(update->new_sha1) ||
+ remaining_fds == 0)
+ close_lock_file(update->lock->lk);
+ else
+ remaining_fds--;
}
/* Perform updates first so live commits remain referenced */