walker.c: use ref transaction for ref updates

Switch to using ref transactions in walker_fetch(). As part of the refactoring to use ref transactions we also fix a potential memory leak where in the original code if write_ref_sha1() would fail we would end up returning from the function without free()ing the msg string. Note that this function is only called when fetching from a remote HTTP repository onto the local (most of the time single-user) repository which likely means that the type of collisions that the previous locking would protect against and cause the fetch to fail for are even more rare. Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ronnie Sahlberg committed Apr 17, 2014 at 11:31 UTC b6b10bb44c31b5a830c1636c7eb8b122f9b67858
1 file changed +40 -30
walker.c
+40 -30
@@ -251,40 +251,40 @@ void walker_targets_free(int targets, char **target, const char **write_ref)
251 int walker_fetch(struct walker *walker, int targets, char **target,
252 const char **write_ref, const char *write_ref_log_details)
253 {
254 - struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
254 + struct strbuf refname = STRBUF_INIT;
255 + struct strbuf err = STRBUF_INIT;
256 + struct ref_transaction *transaction = NULL;
257 unsigned char *sha1 = xmalloc(targets * 20);
256 - char *msg;
257 - int ret;
258 - int i;
258 + char *msg = NULL;
259 + int i, ret = -1;
260
261 save_commit_buffer = 0;
262
262 - for (i = 0; i < targets; i++) {
263 - if (!write_ref || !write_ref[i])
264 - continue;
265 -
266 - lock[i] = lock_ref_sha1(write_ref[i], NULL);
267 - if (!lock[i]) {
268 - error("Can't lock ref %s", write_ref[i]);
269 - goto unlock_and_fail;
263 + if (write_ref) {
264 + transaction = ref_transaction_begin(&err);
265 + if (!transaction) {
266 + error("%s", err.buf);
267 + goto done;
268 }
269 }
272 -
270 if (!walker->get_recover)
271 for_each_ref(mark_complete, NULL);
272
273 for (i = 0; i < targets; i++) {
274 if (interpret_target(walker, target[i], &sha1[20 * i])) {
275 error("Could not interpret response from server '%s' as something to pull", target[i]);
279 - goto unlock_and_fail;
276 + goto done;
277 }
278 if (process(walker, lookup_unknown_object(&sha1[20 * i])))
282 - goto unlock_and_fail;
279 + goto done;
280 }
281
282 if (loop(walker))
286 - goto unlock_and_fail;
287 -
283 + goto done;
284 + if (!write_ref) {
285 + ret = 0;
286 + goto done;
287 + }
288 if (write_ref_log_details) {
289 msg = xmalloc(strlen(write_ref_log_details) + 12);
290 sprintf(msg, "fetch from %s", write_ref_log_details);
@@ -292,23 +292,33 @@ int walker_fetch(struct walker *walker, int targets, char **target,
292 msg = NULL;
293 }
294 for (i = 0; i < targets; i++) {
295 - if (!write_ref || !write_ref[i])
295 + if (!write_ref[i])
296 continue;
297 - ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
298 - lock[i] = NULL;
299 - if (ret)
300 - goto unlock_and_fail;
297 + strbuf_reset(&refname);
298 + strbuf_addf(&refname, "refs/%s", write_ref[i]);
299 + if (ref_transaction_update(transaction, refname.buf,
300 + &sha1[20 * i], NULL, 0, 0,
301 + &err)) {
302 + error("%s", err.buf);
303 + goto done;
304 + }
305 + }
306 + if (ref_transaction_commit(transaction,
307 + msg ? msg : "fetch (unknown)",
308 + &err)) {
309 + error("%s", err.buf);
310 + goto done;
311 }
302 - free(msg);
303 -
304 - return 0;
312
306 -unlock_and_fail:
307 - for (i = 0; i < targets; i++)
308 - if (lock[i])
309 - unlock_ref(lock[i]);
313 + ret = 0;
314
311 - return -1;
315 +done:
316 + ref_transaction_free(transaction);
317 + free(msg);
318 + free(sha1);
319 + strbuf_release(&err);
320 + strbuf_release(&refname);
321 + return ret;
322 }
323
324 void walker_free(struct walker *walker)