All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>, "Jeff King" <peff@peff.net>,
	"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH] pack-refs: remove all empty dirs under .git/{refs,logs/refs}
Date: Sat, 11 Feb 2012 18:08:04 +0700	[thread overview]
Message-ID: <1328958484-4202-1-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1328946907-31650-1-git-send-email-pclouds@gmail.com>

"git pack-refs" tries to remove directory that becomes empty but it
does not try to do so hard enough. Only empty directories created
because a ref is packed are considered.

This patch introduces a global switch, which instructs ref machinery
to collect all empty directories (or ones containing only empty
directories) in removable order. "git pack-refs" uses this information
to clean $GIT_DIR/refs and $GIT_DIR/logs/refs.

Some directories are kept by this patch even if they are empty: refs,
refs/heads and refs/tags. The first one is one of git repository
signature. The rest is created by init-db, one may expect them to always
be there.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 v3, no second look at $GIT_DIR/refs and also clean
 $GIT_DIR/logs/refs. Not really fond of the global switch, but it does
 not look very intrusive to refs.c

 builtin/pack-refs.c  |    2 ++
 pack-refs.c          |   10 ++++++++++
 refs.c               |   35 +++++++++++++++++++++++++++++++----
 refs.h               |    4 ++++
 t/t3210-pack-refs.sh |   10 ++++++++++
 5 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index 39a9d89..044ae8f 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "parse-options.h"
 #include "pack-refs.h"
+#include "refs.h"
 
 static char const * const pack_refs_usage[] = {
 	"git pack-refs [options]",
@@ -15,6 +16,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
 		OPT_BIT(0, "prune", &flags, "prune loose refs (default)", PACK_REFS_PRUNE),
 		OPT_END(),
 	};
+	save_empty_ref_directories = 1;
 	if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
 		usage_with_options(pack_refs_usage, opts);
 	return pack_refs(flags);
diff --git a/pack-refs.c b/pack-refs.c
index f09a054..76d3408 100644
--- a/pack-refs.c
+++ b/pack-refs.c
@@ -2,6 +2,7 @@
 #include "refs.h"
 #include "tag.h"
 #include "pack-refs.h"
+#include "string-list.h"
 
 struct ref_to_prune {
 	struct ref_to_prune *next;
@@ -105,10 +106,19 @@ static void prune_ref(struct ref_to_prune *r)
 
 static void prune_refs(struct ref_to_prune *r)
 {
+	struct string_list *list = get_empty_ref_directories();;
+	int i;
+
 	while (r) {
 		prune_ref(r);
 		r = r->next;
 	}
+
+	for (i = 0; i < list->nr; i++) {
+		const char *s = list->items[i].string;
+		rmdir(git_path("%s", s));
+		rmdir(git_path("logs/%s", s));
+	}
 }
 
 static struct lock_file packed;
diff --git a/refs.c b/refs.c
index b8843bb..7e9a250 100644
--- a/refs.c
+++ b/refs.c
@@ -3,6 +3,7 @@
 #include "object.h"
 #include "tag.h"
 #include "dir.h"
+#include "string-list.h"
 
 /* ISSYMREF=0x01, ISPACKED=0x02 and ISBROKEN=0x04 are public interfaces */
 #define REF_KNOWS_PEELED 0x10
@@ -29,6 +30,8 @@ struct ref_array {
 	struct ref_entry **refs;
 };
 
+int save_empty_ref_directories;
+
 /*
  * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
  * Return a pointer to the refname within the line (null-terminated),
@@ -177,6 +180,7 @@ static struct ref_cache {
 	char did_packed;
 	struct ref_array loose;
 	struct ref_array packed;
+	struct string_list empty_dirs;
 	/* The submodule name, or "" for the main repo. */
 	char name[FLEX_ARRAY];
 } *ref_cache;
@@ -326,7 +330,7 @@ void add_packed_ref(const char *refname, const unsigned char *sha1)
 }
 
 static void get_ref_dir(struct ref_cache *refs, const char *base,
-			struct ref_array *array)
+			struct ref_array *array, int *would_be_empty)
 {
 	DIR *dir;
 	const char *path;
@@ -343,6 +347,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,
 		struct dirent *de;
 		int baselen = strlen(base);
 		char *refname = xmalloc(baselen + 257);
+		int nr = 0;
 
 		memcpy(refname, base, baselen);
 		if (baselen && base[baselen-1] != '/')
@@ -355,8 +360,13 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,
 			int namelen;
 			const char *refdir;
 
-			if (de->d_name[0] == '.')
+			if (de->d_name[0] == '.') {
+				if (strcmp(de->d_name, "..") &&
+				    strcmp(de->d_name, "."))
+					nr++;
 				continue;
+			}
+			nr++;
 			namelen = strlen(de->d_name);
 			if (namelen > 255)
 				continue;
@@ -369,7 +379,10 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,
 			if (stat(refdir, &st) < 0)
 				continue;
 			if (S_ISDIR(st.st_mode)) {
-				get_ref_dir(refs, refname, array);
+				int empty = 0;
+				get_ref_dir(refs, refname, array, &empty);
+				if (empty)
+					nr--;
 				continue;
 			}
 			if (*refs->name) {
@@ -387,6 +400,15 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,
 		}
 		free(refname);
 		closedir(dir);
+		if (save_empty_ref_directories &&
+		    nr == 0 &&
+		    strcmp(base, "refs") &&
+		    strcmp(base, "refs/heads") &&
+		    strcmp(base, "refs/tags")) {
+			string_list_append(&refs->empty_dirs, xstrdup(base));
+			if (would_be_empty)
+				*would_be_empty = 1;
+		}
 	}
 }
 
@@ -427,12 +449,17 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 static struct ref_array *get_loose_refs(struct ref_cache *refs)
 {
 	if (!refs->did_loose) {
-		get_ref_dir(refs, "refs", &refs->loose);
+		get_ref_dir(refs, "refs", &refs->loose, NULL);
 		refs->did_loose = 1;
 	}
 	return &refs->loose;
 }
 
+struct string_list *get_empty_ref_directories()
+{
+	return &get_ref_cache(NULL)->empty_dirs;
+}
+
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define MAXDEPTH 5
 #define MAXREFLEN (1024)
diff --git a/refs.h b/refs.h
index 00ba1e2..21a2a00 100644
--- a/refs.h
+++ b/refs.h
@@ -14,6 +14,10 @@ struct ref_lock {
 #define REF_ISPACKED 0x02
 #define REF_ISBROKEN 0x04
 
+struct string_list;
+extern int save_empty_ref_directories;
+extern struct string_list *get_empty_ref_directories();
+
 /*
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index cd04361..40fcd54 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -66,6 +66,16 @@ test_expect_success 'see if git pack-refs --prune removes empty dirs' '
      ! test -e .git/refs/heads/r
 '
 
+test_expect_success 'pack-refs --prune removes all empty dirs in refs and logs' '
+     mkdir -p .git/refs/empty/outside/heads &&
+     mkdir -p .git/refs/heads/empty/dir/ectory &&
+     mkdir -p .git/logs/refs/heads/empty/dir/ectory &&
+     git pack-refs --all --prune &&
+     ! test -e .git/refs/empty &&
+     ! test -e .git/refs/heads/empty &&
+     ! test -e .git/logs/refs/heads/empty
+'
+
 test_expect_success \
     'git branch g should work when git branch g/h has been deleted' \
     'git branch g/h &&
-- 
1.7.8.36.g69ee2

  parent reply	other threads:[~2012-02-11 11:02 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-10 16:25 [PATCH] Remove empty ref directories while reading loose refs Nguyễn Thái Ngọc Duy
2012-02-10 19:09 ` Junio C Hamano
2012-02-10 20:53 ` Jeff King
2012-02-11  7:55 ` [PATCH 1/2] pack-refs: remove all empty directories under $GIT_DIR/refs Nguyễn Thái Ngọc Duy
2012-02-11  7:55   ` [PATCH 2/2] Revert be7c6d4 (pack-refs: remove newly empty directories) Nguyễn Thái Ngọc Duy
2012-02-11  8:26   ` [PATCH 1/2] pack-refs: remove all empty directories under $GIT_DIR/refs Junio C Hamano
2012-02-11  8:55     ` Nguyen Thai Ngoc Duy
2012-02-11 17:59       ` Junio C Hamano
2012-02-11 11:08   ` Nguyễn Thái Ngọc Duy [this message]
2012-02-11 11:27     ` [PATCH] pack-refs: remove all empty dirs under .git/{refs,logs/refs} Thomas Adam

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1328958484-4202-1-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=peff@peff.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.