Merge branch 'mm/merge-rename-delete-message'
[gitweb.git] / refs / files-backend.c
index 88f8c7aa4b2be898712f90be4394e6233dee9f80..21e116d4e620b9de77def18bf89054399477cf62 100644 (file)
@@ -697,7 +697,7 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
 
        if (peel_entry(entry, 0))
                return -1;
-       hashcpy(peeled->hash, entry->u.value.peeled.hash);
+       oidcpy(peeled, &entry->u.value.peeled);
        return 0;
 }
 
@@ -2280,10 +2280,18 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
        return 0;
 }
 
+enum {
+       REMOVE_EMPTY_PARENTS_REF = 0x01,
+       REMOVE_EMPTY_PARENTS_REFLOG = 0x02
+};
+
 /*
- * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Remove empty parent directories associated with the specified
+ * reference and/or its reflog, but spare [logs/]refs/ and immediate
+ * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or
+ * REMOVE_EMPTY_PARENTS_REFLOG.
  */
-static void try_remove_empty_parents(const char *refname)
+static void try_remove_empty_parents(const char *refname, unsigned int flags)
 {
        struct strbuf buf = STRBUF_INIT;
        char *p, *q;
@@ -2299,7 +2307,7 @@ static void try_remove_empty_parents(const char *refname)
                        p++;
        }
        q = buf.buf + buf.len;
-       while (1) {
+       while (flags & (REMOVE_EMPTY_PARENTS_REF | REMOVE_EMPTY_PARENTS_REFLOG)) {
                while (q > p && *q != '/')
                        q--;
                while (q > p && *(q-1) == '/')
@@ -2307,8 +2315,12 @@ static void try_remove_empty_parents(const char *refname)
                if (q == p)
                        break;
                strbuf_setlen(&buf, q - buf.buf);
-               if (rmdir(git_path("%s", buf.buf)))
-                       break;
+               if ((flags & REMOVE_EMPTY_PARENTS_REF) &&
+                   rmdir(git_path("%s", buf.buf)))
+                       flags &= ~REMOVE_EMPTY_PARENTS_REF;
+               if ((flags & REMOVE_EMPTY_PARENTS_REFLOG) &&
+                   rmdir(git_path("logs/%s", buf.buf)))
+                       flags &= ~REMOVE_EMPTY_PARENTS_REFLOG;
        }
        strbuf_release(&buf);
 }
@@ -2334,7 +2346,6 @@ static void prune_ref(struct ref_to_prune *r)
        }
        ref_transaction_free(transaction);
        strbuf_release(&err);
-       try_remove_empty_parents(r->name);
 }
 
 static void prune_refs(struct ref_to_prune *r)
@@ -2636,7 +2647,7 @@ static int files_rename_ref(struct ref_store *ref_store,
        }
 
        flag = log_all_ref_updates;
-       log_all_ref_updates = 0;
+       log_all_ref_updates = LOG_REFS_NONE;
        if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
            commit_ref_update(refs, lock, orig_sha1, NULL, &err)) {
                error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
@@ -2811,8 +2822,8 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
 {
        int logfd, result;
 
-       if (log_all_ref_updates < 0)
-               log_all_ref_updates = !is_bare_repository();
+       if (log_all_ref_updates == LOG_REFS_UNSET)
+               log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
 
        result = log_ref_setup(refname, flags & REF_FORCE_CREATE_REFLOG,
                               &logfd, err);
@@ -3782,6 +3793,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
                                        ret = TRANSACTION_GENERIC_ERROR;
                                        goto cleanup;
                                }
+                               update->flags |= REF_DELETED_LOOSE;
                        }
 
                        if (!(update->flags & REF_ISPRUNING))
@@ -3794,16 +3806,38 @@ static int files_transaction_commit(struct ref_store *ref_store,
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
-       for_each_string_list_item(ref_to_delete, &refs_to_delete)
-               unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
+
+       /* Delete the reflogs of any references that were deleted: */
+       for_each_string_list_item(ref_to_delete, &refs_to_delete) {
+               if (!unlink_or_warn(git_path("logs/%s", ref_to_delete->string)))
+                       try_remove_empty_parents(ref_to_delete->string,
+                                                REMOVE_EMPTY_PARENTS_REFLOG);
+       }
+
        clear_loose_ref_cache(refs);
 
 cleanup:
        transaction->state = REF_TRANSACTION_CLOSED;
 
-       for (i = 0; i < transaction->nr; i++)
-               if (transaction->updates[i]->backend_data)
-                       unlock_ref(transaction->updates[i]->backend_data);
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+               struct ref_lock *lock = update->backend_data;
+
+               if (lock)
+                       unlock_ref(lock);
+
+               if (update->flags & REF_DELETED_LOOSE) {
+                       /*
+                        * The loose reference was deleted. Delete any
+                        * empty parent directories. (Note that this
+                        * can only work because we have already
+                        * removed the lockfile.)
+                        */
+                       try_remove_empty_parents(update->refname,
+                                                REMOVE_EMPTY_PARENTS_REF);
+               }
+       }
+
        string_list_clear(&refs_to_delete, 0);
        free(head_ref);
        string_list_clear(&affected_refnames, 0);