Merge branch 'mh/packed-ref-transactions'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 01:47:56 +0000 (10:47 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 01:47:56 +0000 (10:47 +0900)
Implement transactional update to the packed-ref representation of
references.

* mh/packed-ref-transactions:
files_transaction_finish(): delete reflogs before references
packed-backend: rip out some now-unused code
files_ref_store: use a transaction to update packed refs
t1404: demonstrate two problems with reference transactions
files_initial_transaction_commit(): use a transaction for packed refs
prune_refs(): also free the linked list
files_pack_refs(): use a reference transaction to write packed refs
packed_delete_refs(): implement method
packed_ref_store: implement reference transactions
struct ref_transaction: add a place for backends to store data
packed-backend: don't adjust the reference count on lock/unlock

1  2 
refs/files-backend.c
refs/packed-backend.c
index a7cc65d0dee2dc5e8d3af6e447fc337e5958930f,961424a4ea7e9d08f65e053243ad2def93456feb..32663a999ea030f76f400608ae1ed6dbaebbc20c
@@@ -2418,19 -2388,20 +2434,24 @@@ static int lock_ref_for_update(struct f
                 * the lockfile is still open. Close it to
                 * free up the file descriptor:
                 */
 -              if (close_ref(lock)) {
 +              if (close_ref_gently(lock)) {
                        strbuf_addf(err, "couldn't close '%s.lock'",
                                    update->refname);
 -                      return TRANSACTION_GENERIC_ERROR;
 +                      ret = TRANSACTION_GENERIC_ERROR;
 +                      goto out;
                }
        }
 -      return 0;
 +
 +out:
 +      strbuf_release(&referent);
 +      return ret;
  }
  
+ struct files_transaction_backend_data {
+       struct ref_transaction *packed_transaction;
+       int packed_refs_locked;
+ };
  /*
   * Unlock any references in `transaction` that are still locked, and
   * mark the transaction closed.
index 321608a11472821ee69ef38a309d497ac92594df,0279aeceeaeb7026feb33b91b53e361ef5413b6b..3bc47ffd5ea4e82f2505a8649b162d18ebf11a21
@@@ -628,8 -586,8 +587,9 @@@ static int write_with_updates(struct pa
         */
        packed_refs_path = get_locked_file_path(&refs->lock);
        strbuf_addf(&sb, "%s.new", packed_refs_path);
 -      if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
+       free(packed_refs_path);
 +      refs->tempfile = create_tempfile(sb.buf);
 +      if (!refs->tempfile) {
                strbuf_addf(err, "unable to create file %s: %s",
                            sb.buf, strerror(errno));
                strbuf_release(&sb);
                goto error;
        }
  
-       if (rename_tempfile(&refs->tempfile, packed_refs_path)) {
-               strbuf_addf(err, "error replacing %s: %s",
-                           refs->path, strerror(errno));
-               goto out;
 -      if (close_tempfile(&refs->tempfile)) {
++      if (close_tempfile_gently(refs->tempfile)) {
+               strbuf_addf(err, "error closing file %s: %s",
 -                          get_tempfile_path(&refs->tempfile),
++                          get_tempfile_path(refs->tempfile),
+                           strerror(errno));
+               strbuf_release(&sb);
++              delete_tempfile(&refs->tempfile);
+               return -1;
        }
  
-       ret = 0;
-       goto out;
+       return 0;
+ write_error:
+       strbuf_addf(err, "error writing to %s: %s",
 -                  get_tempfile_path(&refs->tempfile), strerror(errno));
++                  get_tempfile_path(refs->tempfile), strerror(errno));
  
  error:
-       delete_tempfile(&refs->tempfile);
+       if (iter)
+               ref_iterator_abort(iter);
  
- out:
-       free(packed_refs_path);
-       return ret;
+       delete_tempfile(&refs->tempfile);
+       return -1;
  }
  
- /*
-  * Rewrite the packed-refs file, omitting any refs listed in
-  * 'refnames'. On error, leave packed-refs unchanged, write an error
-  * message to 'err', and return a nonzero value. The packed refs lock
-  * must be held when calling this function; it will still be held when
-  * the function returns.
-  *
-  * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
-  */
- int repack_without_refs(struct ref_store *ref_store,
-                       struct string_list *refnames, struct strbuf *err)
+ struct packed_transaction_backend_data {
+       /* True iff the transaction owns the packed-refs lock. */
+       int own_lock;
+       struct string_list updates;
+ };
+ static void packed_transaction_cleanup(struct packed_ref_store *refs,
+                                      struct ref_transaction *transaction)
  {
-       struct packed_ref_store *refs =
-               packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
-                               "repack_without_refs");
-       struct ref_dir *packed;
-       struct string_list_item *refname;
-       int needs_repacking = 0, removed = 0;
+       struct packed_transaction_backend_data *data = transaction->backend_data;
  
-       packed_assert_main_repository(refs, "repack_without_refs");
-       assert(err);
+       if (data) {
+               string_list_clear(&data->updates, 0);
  
-       if (!is_lock_file_locked(&refs->lock))
-               die("BUG: repack_without_refs called without holding lock");
 -              if (is_tempfile_active(&refs->tempfile))
++              if (is_tempfile_active(refs->tempfile))
+                       delete_tempfile(&refs->tempfile);
  
-       /* Look for a packed ref */
-       for_each_string_list_item(refname, refnames) {
-               if (get_packed_ref(refs, refname->string)) {
-                       needs_repacking = 1;
-                       break;
+               if (data->own_lock && is_lock_file_locked(&refs->lock)) {
+                       packed_refs_unlock(&refs->base);
+                       data->own_lock = 0;
                }
-       }
  
-       /* Avoid locking if we have nothing to do */
-       if (!needs_repacking)
-               return 0; /* no refname exists in packed refs */
-       packed = get_packed_refs(refs);
-       /* Remove refnames from the cache */
-       for_each_string_list_item(refname, refnames)
-               if (remove_entry_from_dir(packed, refname->string) != -1)
-                       removed = 1;
-       if (!removed) {
-               /*
-                * All packed entries disappeared while we were
-                * acquiring the lock.
-                */
-               clear_packed_ref_cache(refs);
-               return 0;
+               free(data);
+               transaction->backend_data = NULL;
        }
  
-       /* Write what remains */
-       return commit_packed_refs(&refs->base, err);
- }
- static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)
- {
-       /* Nothing to do. */
-       return 0;
+       transaction->state = REF_TRANSACTION_CLOSED;
  }
  
  static int packed_transaction_prepare(struct ref_store *ref_store,