@matheusbsbs / matheus-bagliano / commits / fb10ca5b54
Fork 0

sparse-checkout: write using lockfile

If two 'git sparse-checkout set' subcommands are launched at the same time, the behavior can be unexpected as they compete to write the sparse-checkout file and update the working directory. Take a lockfile around the writes to the sparse-checkout file. In addition, acquire this lock around the working directory update to avoid two commands updating the working directory in different ways. Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Nov 21, 2019 at 22:04 UTC fb10ca5b54362e6f860e1e9049e03924fcf5f05b
2 files changed +18 -4
builtin/sparse-checkout.c
+11 -4
@@ -170,25 +170,32 @@ static int write_patterns_and_update(struct pattern_list *pl)
170 {
171 char *sparse_filename;
172 FILE *fp;
173 + int fd;
174 + struct lock_file lk = LOCK_INIT;
175 int result;
176
175 - result = update_working_directory(pl);
177 + sparse_filename = get_sparse_checkout_filename();
178 + fd = hold_lock_file_for_update(&lk, sparse_filename,
179 + LOCK_DIE_ON_ERROR);
180
181 + result = update_working_directory(pl);
182 if (result) {
183 + rollback_lock_file(&lk);
184 + free(sparse_filename);
185 clear_pattern_list(pl);
186 update_working_directory(NULL);
187 return result;
188 }
189
183 - sparse_filename = get_sparse_checkout_filename();
184 - fp = fopen(sparse_filename, "w");
190 + fp = xfdopen(fd, "w");
191
192 if (core_sparse_checkout_cone)
193 write_cone_to_file(fp, pl);
194 else
195 write_patterns_to_file(fp, pl);
196
191 - fclose(fp);
197 + fflush(fp);
198 + commit_lock_file(&lk);
199
200 free(sparse_filename);
201 clear_pattern_list(pl);
t/t1091-sparse-checkout-builtin.sh
+7
@@ -277,4 +277,11 @@ test_expect_success 'revert to old sparse-checkout on empty update' '
277 )
278 '
279
280 +test_expect_success 'fail when lock is taken' '
281 + test_when_finished rm -rf repo/.git/info/sparse-checkout.lock &&
282 + touch repo/.git/info/sparse-checkout.lock &&
283 + test_must_fail git -C repo sparse-checkout set deep 2>err &&
284 + test_i18ngrep "File exists" err
285 +'
286 +
287 test_done