Skip to content

Commit 4ec6591

Browse files
committed
Merge branch 'mh/write-refs-sooner-2.2' into mh/write-refs-sooner-2.3
* mh/write-refs-sooner-2.2: ref_transaction_commit(): fix atomicity and avoid fd exhaustion ref_transaction_commit(): remove the local flags variable ref_transaction_commit(): inline call to write_ref_sha1() rename_ref(): inline calls to write_ref_sha1() from this function commit_ref_update(): new function, extracted from write_ref_sha1() write_ref_to_lockfile(): new function, extracted from write_ref_sha1() t7004: rename ULIMIT test prerequisite to ULIMIT_STACK_SIZE update-ref: test handling large transactions properly
2 parents 16018ae + 6c34492 commit 4ec6591

File tree

3 files changed

+103
-28
lines changed

3 files changed

+103
-28
lines changed

refs.c

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ static unsigned char refname_disposition[256] = {
3030
* pruned.
3131
*/
3232
#define REF_ISPRUNING 0x0100
33+
34+
/*
35+
* Used as a flag in ref_update::flags when the lockfile needs to be
36+
* committed.
37+
*/
38+
#define REF_NEEDS_COMMIT 0x0200
39+
3340
/*
3441
* Try to read one refname component from the front of refname.
3542
* Return the length of the component found, or -1 if the component is
@@ -2747,8 +2754,9 @@ static int rename_ref_available(const char *oldname, const char *newname)
27472754
return ret;
27482755
}
27492756

2750-
static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1,
2751-
const char *logmsg);
2757+
static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1);
2758+
static int commit_ref_update(struct ref_lock *lock,
2759+
const unsigned char *sha1, const char *logmsg);
27522760

27532761
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
27542762
{
@@ -2807,7 +2815,9 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
28072815
}
28082816
lock->force_write = 1;
28092817
hashcpy(lock->old_sha1, orig_sha1);
2810-
if (write_ref_sha1(lock, orig_sha1, logmsg)) {
2818+
2819+
if (write_ref_to_lockfile(lock, orig_sha1) ||
2820+
commit_ref_update(lock, orig_sha1, logmsg)) {
28112821
error("unable to write current sha1 into %s", newrefname);
28122822
goto rollback;
28132823
}
@@ -2824,7 +2834,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
28242834
lock->force_write = 1;
28252835
flag = log_all_ref_updates;
28262836
log_all_ref_updates = 0;
2827-
if (write_ref_sha1(lock, orig_sha1, NULL))
2837+
if (write_ref_to_lockfile(lock, orig_sha1) ||
2838+
commit_ref_update(lock, orig_sha1, NULL))
28282839
error("unable to write current sha1 into %s", oldrefname);
28292840
log_all_ref_updates = flag;
28302841

@@ -2996,23 +3007,15 @@ int is_branch(const char *refname)
29963007
}
29973008

29983009
/*
2999-
* Write sha1 into the ref specified by the lock. Make sure that errno
3000-
* is sane on error.
3010+
* Write sha1 into the open lockfile, then close the lockfile. On
3011+
* errors, rollback the lockfile and set errno to reflect the problem.
30013012
*/
3002-
static int write_ref_sha1(struct ref_lock *lock,
3003-
const unsigned char *sha1, const char *logmsg)
3013+
static int write_ref_to_lockfile(struct ref_lock *lock,
3014+
const unsigned char *sha1)
30043015
{
30053016
static char term = '\n';
30063017
struct object *o;
30073018

3008-
if (!lock) {
3009-
errno = EINVAL;
3010-
return -1;
3011-
}
3012-
if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
3013-
unlock_ref(lock);
3014-
return 0;
3015-
}
30163019
o = parse_object(sha1);
30173020
if (!o) {
30183021
error("Trying to write ref %s with nonexistent object %s",
@@ -3037,6 +3040,17 @@ static int write_ref_sha1(struct ref_lock *lock,
30373040
errno = save_errno;
30383041
return -1;
30393042
}
3043+
return 0;
3044+
}
3045+
3046+
/*
3047+
* Commit a change to a loose reference that has already been written
3048+
* to the loose reference lockfile. Also update the reflogs if
3049+
* necessary, using the specified lockmsg (which can be NULL).
3050+
*/
3051+
static int commit_ref_update(struct ref_lock *lock,
3052+
const unsigned char *sha1, const char *logmsg)
3053+
{
30403054
clear_loose_ref_cache(&ref_cache);
30413055
if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
30423056
(strcmp(lock->ref_name, lock->orig_ref_name) &&
@@ -3738,19 +3752,23 @@ int ref_transaction_commit(struct ref_transaction *transaction,
37383752
goto cleanup;
37393753
}
37403754

3741-
/* Acquire all locks while verifying old values */
3755+
/*
3756+
* Acquire all locks, verify old values if provided, check
3757+
* that new values are valid, and write new values to the
3758+
* lockfiles, ready to be activated. Only keep one lockfile
3759+
* open at a time to avoid running out of file descriptors.
3760+
*/
37423761
for (i = 0; i < n; i++) {
37433762
struct ref_update *update = updates[i];
3744-
int flags = update->flags;
37453763

37463764
if (is_null_sha1(update->new_sha1))
3747-
flags |= REF_DELETING;
3765+
update->flags |= REF_DELETING;
37483766
update->lock = lock_ref_sha1_basic(update->refname,
37493767
(update->have_old ?
37503768
update->old_sha1 :
37513769
NULL),
37523770
NULL,
3753-
flags,
3771+
update->flags,
37543772
&update->type);
37553773
if (!update->lock) {
37563774
ret = (errno == ENOTDIR)
@@ -3760,30 +3778,59 @@ int ref_transaction_commit(struct ref_transaction *transaction,
37603778
update->refname);
37613779
goto cleanup;
37623780
}
3781+
if (!(update->flags & REF_DELETING) &&
3782+
(update->lock->force_write ||
3783+
hashcmp(update->lock->old_sha1, update->new_sha1))) {
3784+
if (write_ref_to_lockfile(update->lock, update->new_sha1)) {
3785+
/*
3786+
* The lock was freed upon failure of
3787+
* write_ref_to_lockfile():
3788+
*/
3789+
update->lock = NULL;
3790+
strbuf_addf(err, "Cannot update the ref '%s'.",
3791+
update->refname);
3792+
ret = TRANSACTION_GENERIC_ERROR;
3793+
goto cleanup;
3794+
}
3795+
update->flags |= REF_NEEDS_COMMIT;
3796+
} else {
3797+
/*
3798+
* We didn't have to write anything to the lockfile.
3799+
* Close it to free up the file descriptor:
3800+
*/
3801+
if (close_ref(update->lock)) {
3802+
strbuf_addf(err, "Couldn't close %s.lock",
3803+
update->refname);
3804+
goto cleanup;
3805+
}
3806+
}
37633807
}
37643808

37653809
/* Perform updates first so live commits remain referenced */
37663810
for (i = 0; i < n; i++) {
37673811
struct ref_update *update = updates[i];
37683812

3769-
if (!is_null_sha1(update->new_sha1)) {
3770-
if (write_ref_sha1(update->lock, update->new_sha1,
3771-
update->msg)) {
3772-
update->lock = NULL; /* freed by write_ref_sha1 */
3813+
if (update->flags & REF_NEEDS_COMMIT) {
3814+
if (commit_ref_update(update->lock,
3815+
update->new_sha1, update->msg)) {
3816+
/* The lock was freed by commit_ref_update(): */
3817+
update->lock = NULL;
37733818
strbuf_addf(err, "Cannot update the ref '%s'.",
37743819
update->refname);
37753820
ret = TRANSACTION_GENERIC_ERROR;
37763821
goto cleanup;
3822+
} else {
3823+
/* freed by the above call: */
3824+
update->lock = NULL;
37773825
}
3778-
update->lock = NULL; /* freed by write_ref_sha1 */
37793826
}
37803827
}
37813828

37823829
/* Perform deletes now that updates are safely completed */
37833830
for (i = 0; i < n; i++) {
37843831
struct ref_update *update = updates[i];
37853832

3786-
if (update->lock) {
3833+
if (update->flags & REF_DELETING) {
37873834
if (delete_ref_loose(update->lock, update->type, err)) {
37883835
ret = TRANSACTION_GENERIC_ERROR;
37893836
goto cleanup;

t/t1400-update-ref.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,4 +1065,32 @@ test_expect_success 'stdin -z delete refs works with packed and loose refs' '
10651065
test_must_fail git rev-parse --verify -q $c
10661066
'
10671067

1068+
run_with_limited_open_files () {
1069+
(ulimit -n 32 && "$@")
1070+
}
1071+
1072+
test_lazy_prereq ULIMIT_FILE_DESCRIPTORS 'run_with_limited_open_files true'
1073+
1074+
test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' '
1075+
(
1076+
for i in $(test_seq 33)
1077+
do
1078+
echo "create refs/heads/$i HEAD"
1079+
done >large_input &&
1080+
run_with_limited_open_files git update-ref --stdin <large_input &&
1081+
git rev-parse --verify -q refs/heads/33
1082+
)
1083+
'
1084+
1085+
test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches does not burst open file limit' '
1086+
(
1087+
for i in $(test_seq 33)
1088+
do
1089+
echo "delete refs/heads/$i HEAD"
1090+
done >large_input &&
1091+
run_with_limited_open_files git update-ref --stdin <large_input &&
1092+
test_must_fail git rev-parse --verify -q refs/heads/33
1093+
)
1094+
'
1095+
10681096
test_done

t/t7004-tag.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,10 +1463,10 @@ run_with_limited_stack () {
14631463
(ulimit -s 128 && "$@")
14641464
}
14651465
1466-
test_lazy_prereq ULIMIT 'run_with_limited_stack true'
1466+
test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
14671467
14681468
# we require ulimit, this excludes Windows
1469-
test_expect_success ULIMIT '--contains works in a deep repo' '
1469+
test_expect_success ULIMIT_STACK_SIZE '--contains works in a deep repo' '
14701470
>expect &&
14711471
i=1 &&
14721472
while test $i -lt 8000

0 commit comments

Comments
 (0)