# [PATCH] C version of git-count-objects

12 messages from 2006-04-27 to 2006-04-28. Participants: Peter Hagervall, Morten Welinder, Nicolas Pitre, Junio C Hamano, Linus Torvalds.
Thread: https://gitlist.dev/t/3973

## Peter Hagervall, 2006-04-27 10:12

Subject: [PATCH] C version of git-count-objects
Message-ID: <20060427101254.GA22769@peppar.cs.umu.se>
URL: https://gitlist.dev/e/20060427101254.GA22769%40peppar.cs.umu.se

```
Answering the call Linus made[1], sort of, but for a completely
different program.

Anyway, it ought to be at least as portable as the shell script, and a
whole lot faster, however much that matters.

Signed-off-by: Peter Hagervall <hager@cs.umu.se>

[1] http://article.gmane.org/gmane.comp.version-control.git/19073

---

 Makefile             |    5 +--
 count-objects.c      |   56 +++++++++++++++++++++++++++++++++++++
 git-count-objects.sh |   31 --------------------
 3 files changed, 59 insertions(+), 33 deletions(-)


diff --git a/Makefile b/Makefile
index 8ce27a6..53e7591 100644
--- a/Makefile
+++ b/Makefile
@@ -115,7 +115,7 @@ ### --- END CONFIGURATION SECTION ---
 SCRIPT_SH = \
 	git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
 	git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
-	git-count-objects.sh git-diff.sh git-fetch.sh \
+	git-diff.sh git-fetch.sh \
 	git-format-patch.sh git-ls-remote.sh \
 	git-merge-one-file.sh git-parse-remote.sh \
 	git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
@@ -165,7 +165,8 @@ PROGRAMS = \
 	git-upload-pack$X git-verify-pack$X git-write-tree$X \
 	git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
 	git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
-	git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
+	git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
+	git-count-objects$X
 
 BUILT_INS = git-log$X
 
diff --git a/count-objects.c b/count-objects.c
new file mode 100644
index 0000000..67ab6f0
--- /dev/null
+++ b/count-objects.c
@@ -0,0 +1,56 @@
+#include "cache.h"
+#include "git-compat-util.h"
+
+static char pathname[PATH_MAX + 1];
+static int numobjects, numblocks;
+static const char hex_digits[] = "0123456789abcdef";
+
+void count_objects(void)
+{
+	char subdir[3];
+	int i, j;
+	struct stat statbuf;
+	struct dirent *dirp;
+	DIR *dp;
+	subdir[2] = '\0';
+	for (i = 0; i < 16; i++) {
+		subdir[0] = hex_digits[i];
+		for (j = 0; j < 16; j++) {
+			subdir[1] = hex_digits[j];
+			if (access(subdir, R_OK | X_OK))
+				continue;
+			chdir(subdir);
+			if (!(dp = opendir("."))) {
+				error("can't open subdir %s", subdir);
+				continue;
+			}
+			while ((dirp = readdir(dp))) {
+				if (!strcmp(dirp->d_name, ".") ||
+					!strcmp(dirp->d_name, ".."))
+					continue;
+				if (lstat(dirp->d_name, &statbuf)) {
+					error("can't stat file %s", dirp->d_name);
+					continue;
+				}
+				numblocks += statbuf.st_blocks;
+				numobjects++;
+			}
+			closedir(dp);
+			chdir("..");
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	setup_git_directory();
+
+	if (chdir(".git/objects"))
+		die("%s", strerror(errno));
+
+	count_objects();
+
+	printf("%d objects, %d kilobytes\n", numobjects, numblocks / 2);
+
+	return 0;
+}
diff --git a/git-count-objects.sh b/git-count-objects.sh
deleted file mode 100755
index 40c58ef..0000000
--- a/git-count-objects.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-GIT_DIR=`git-rev-parse --git-dir` || exit $?
-
-dc </dev/null 2>/dev/null || {
-	# This is not a real DC at all -- it just knows how
-	# this script feeds DC and does the computation itself.
-	dc () {
-		while read a b
-		do
-			case $a,$b in
-			0,)	acc=0 ;;
-			*,+)	acc=$(($acc + $a)) ;;
-			p,)	echo "$acc" ;;
-			esac
-		done
-	}
-}
-
-echo $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \
-$({
-    echo 0
-    # "no-such" is to help Darwin folks by not using xargs -r.
-    find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null |
-    xargs du -k "$GIT_DIR/objects/no-such" 2>/dev/null |
-    sed -e 's/[ 	].*/ +/'
-    echo p
-} | dc) kilobytes

```

## Morten Welinder, 2006-04-27 13:16

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <118833cc0604270616u2961ab10waef5807c429be784@mail.gmail.com>
URL: https://gitlist.dev/e/118833cc0604270616u2961ab10waef5807c429be784%40mail.gmail.com
In-Reply-To: <20060427101254.GA22769@peppar.cs.umu.se>

```
> +                       if (access(subdir, R_OK | X_OK))
> +                               continue;
> +                       chdir(subdir);

You've got yourself a needless race condition right there.  Just
do the chdir and check the return value.  (And besides, access
checks with the wrong set of permissions, should this ever end
up in set[ug]id context.)

Morten

```

## Nicolas Pitre, 2006-04-27 13:23

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <Pine.LNX.4.64.0604270914570.18816@localhost.localdomain>
URL: https://gitlist.dev/e/Pine.LNX.4.64.0604270914570.18816%40localhost.localdomain
In-Reply-To: <20060427101254.GA22769@peppar.cs.umu.se>

```
On Thu, 27 Apr 2006, Peter Hagervall wrote:

> Answering the call Linus made[1], sort of, but for a completely
> different program.
> 
> Anyway, it ought to be at least as portable as the shell script, and a
> whole lot faster, however much that matters.
> 
[...]
> +	for (i = 0; i < 16; i++) {
> +		subdir[0] = hex_digits[i];
> +		for (j = 0; j < 16; j++) {
> +			subdir[1] = hex_digits[j];
> +			if (access(subdir, R_OK | X_OK))
> +				continue;
> +			chdir(subdir);
> +			if (!(dp = opendir("."))) {
> +				error("can't open subdir %s", subdir);
> +				continue;
> +			}

Looks like you're missing a chdir(".."); there.


Nicolas

```

## Peter Hagervall, 2006-04-27 14:07

Subject: [PATCH] C version of git-count-objects, second try
Message-ID: <20060427140718.GA19993@brainysmurf.cs.umu.se>
URL: https://gitlist.dev/e/20060427140718.GA19993%40brainysmurf.cs.umu.se
In-Reply-To: <Pine.LNX.4.64.0604270914570.18816@localhost.localdomain>

```
On Thu, Apr 27, 2006 at 09:23:47AM -0400, Nicolas Pitre wrote:
> On Thu, 27 Apr 2006, Peter Hagervall wrote:
> 
> > Answering the call Linus made[1], sort of, but for a completely
> > different program.
> > 
> > Anyway, it ought to be at least as portable as the shell script, and a
> > whole lot faster, however much that matters.
> > 
> [...]
> > +	for (i = 0; i < 16; i++) {
> > +		subdir[0] = hex_digits[i];
> > +		for (j = 0; j < 16; j++) {
> > +			subdir[1] = hex_digits[j];
> > +			if (access(subdir, R_OK | X_OK))
> > +				continue;
> > +			chdir(subdir);
> > +			if (!(dp = opendir("."))) {
> > +				error("can't open subdir %s", subdir);
> > +				continue;
> > +			}
> 
> Looks like you're missing a chdir(".."); there.
> 

Thanks, I overlooked that one (and the race condition pointed out by
Morten). Anyway, fixed those now, and removed an unused array.

Signed-off-by: Peter Hagervall <hager@cs.umu.se>

---

 Makefile             |    5 ++-
 count-objects.c      |   55 +++++++++++++++++++++++++++++++++++++
 git-count-objects.sh |   31 --------------------
 3 files changed, 58 insertions(+), 33 deletions(-)


diff --git a/Makefile b/Makefile
index 8ce27a6..53e7591 100644
--- a/Makefile
+++ b/Makefile
@@ -115,7 +115,7 @@ ### --- END CONFIGURATION SECTION ---
 SCRIPT_SH = \
 	git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
 	git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
-	git-count-objects.sh git-diff.sh git-fetch.sh \
+	git-diff.sh git-fetch.sh \
 	git-format-patch.sh git-ls-remote.sh \
 	git-merge-one-file.sh git-parse-remote.sh \
 	git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
@@ -165,7 +165,8 @@ PROGRAMS = \
 	git-upload-pack$X git-verify-pack$X git-write-tree$X \
 	git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
 	git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
-	git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
+	git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
+	git-count-objects$X
 
 BUILT_INS = git-log$X
 
diff --git a/count-objects.c b/count-objects.c
new file mode 100644
index 0000000..beaa4d9
--- /dev/null
+++ b/count-objects.c
@@ -0,0 +1,55 @@
+#include "cache.h"
+#include "git-compat-util.h"
+
+static int numobjects, numblocks;
+static const char hex_digits[] = "0123456789abcdef";
+
+void count_objects(void)
+{
+	char subdir[3];
+	int i, j;
+	struct stat statbuf;
+	struct dirent *dirp;
+	DIR *dp;
+	subdir[2] = '\0';
+	for (i = 0; i < 16; i++) {
+		subdir[0] = hex_digits[i];
+		for (j = 0; j < 16; j++) {
+			subdir[1] = hex_digits[j];
+			if (chdir(subdir))
+				continue;
+			if (!(dp = opendir("."))) {
+				error("can't open subdir %s", subdir);
+				chdir("..");
+				continue;
+			}
+			while ((dirp = readdir(dp))) {
+				if (!strcmp(dirp->d_name, ".") ||
+					!strcmp(dirp->d_name, ".."))
+					continue;
+				if (lstat(dirp->d_name, &statbuf)) {
+					error("can't stat file %s", dirp->d_name);
+					continue;
+				}
+				numblocks += statbuf.st_blocks;
+				numobjects++;
+			}
+			closedir(dp);
+			chdir("..");
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	setup_git_directory();
+
+	if (chdir(".git/objects"))
+		die("%s", strerror(errno));
+
+	count_objects();
+
+	printf("%d objects, %d kilobytes\n", numobjects, numblocks / 2);
+
+	return 0;
+}
diff --git a/git-count-objects.sh b/git-count-objects.sh
deleted file mode 100755
index 40c58ef..0000000
--- a/git-count-objects.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-GIT_DIR=`git-rev-parse --git-dir` || exit $?
-
-dc </dev/null 2>/dev/null || {
-	# This is not a real DC at all -- it just knows how
-	# this script feeds DC and does the computation itself.
-	dc () {
-		while read a b
-		do
-			case $a,$b in
-			0,)	acc=0 ;;
-			*,+)	acc=$(($acc + $a)) ;;
-			p,)	echo "$acc" ;;
-			esac
-		done
-	}
-}
-
-echo $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \
-$({
-    echo 0
-    # "no-such" is to help Darwin folks by not using xargs -r.
-    find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null |
-    xargs du -k "$GIT_DIR/objects/no-such" 2>/dev/null |
-    sed -e 's/[ 	].*/ +/'
-    echo p
-} | dc) kilobytes

```

## Junio C Hamano, 2006-04-27 18:56

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <7vhd4ekfu1.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vhd4ekfu1.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <Pine.LNX.4.64.0604270914570.18816@localhost.localdomain>

```
Nicolas Pitre <nico@cam.org> writes:

> On Thu, 27 Apr 2006, Peter Hagervall wrote:
>
>> Answering the call Linus made[1], sort of, but for a completely
>> different program.
>> 
>> Anyway, it ought to be at least as portable as the shell script, and a
>> whole lot faster, however much that matters.
>> 
> [...]
>> +	for (i = 0; i < 16; i++) {
>> +		subdir[0] = hex_digits[i];
>> +		for (j = 0; j < 16; j++) {
>> +			subdir[1] = hex_digits[j];
>> +			if (access(subdir, R_OK | X_OK))
>> +				continue;
>> +			chdir(subdir);
>> +			if (!(dp = opendir("."))) {
>> +				error("can't open subdir %s", subdir);
>> +				continue;
>> +			}
>
> Looks like you're missing a chdir(".."); there.

Why would you even _need_ to chdir() anywhere, anyway?

```

## Nicolas Pitre, 2006-04-27 19:39

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <Pine.LNX.4.64.0604271535460.18816@localhost.localdomain>
URL: https://gitlist.dev/e/Pine.LNX.4.64.0604271535460.18816%40localhost.localdomain
In-Reply-To: <7vhd4ekfu1.fsf@assigned-by-dhcp.cox.net>

```
On Thu, 27 Apr 2006, Junio C Hamano wrote:

> Nicolas Pitre <nico@cam.org> writes:
> 
> > On Thu, 27 Apr 2006, Peter Hagervall wrote:
> >
> >> Answering the call Linus made[1], sort of, but for a completely
> >> different program.
> >> 
> >> Anyway, it ought to be at least as portable as the shell script, and a
> >> whole lot faster, however much that matters.
> >> 
> > [...]
> >> +	for (i = 0; i < 16; i++) {
> >> +		subdir[0] = hex_digits[i];
> >> +		for (j = 0; j < 16; j++) {
> >> +			subdir[1] = hex_digits[j];
> >> +			if (access(subdir, R_OK | X_OK))
> >> +				continue;
> >> +			chdir(subdir);
> >> +			if (!(dp = opendir("."))) {
> >> +				error("can't open subdir %s", subdir);
> >> +				continue;
> >> +			}
> >
> > Looks like you're missing a chdir(".."); there.
> 
> Why would you even _need_ to chdir() anywhere, anyway?

To avoid appending the filename to the path before each lstat() I'd 
guess.


Nicolas

```

## Peter Hagervall, 2006-04-27 19:46

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <20060427194559.GA26386@brainysmurf.cs.umu.se>
URL: https://gitlist.dev/e/20060427194559.GA26386%40brainysmurf.cs.umu.se
In-Reply-To: <Pine.LNX.4.64.0604271535460.18816@localhost.localdomain>

```
On Thu, Apr 27, 2006 at 03:39:14PM -0400, Nicolas Pitre wrote:
> On Thu, 27 Apr 2006, Junio C Hamano wrote:
> 
> > Nicolas Pitre <nico@cam.org> writes:
> > 
> > > On Thu, 27 Apr 2006, Peter Hagervall wrote:
> > >
> > >> Answering the call Linus made[1], sort of, but for a completely
> > >> different program.
> > >> 
> > >> Anyway, it ought to be at least as portable as the shell script, and a
> > >> whole lot faster, however much that matters.
> > >> 
> > > [...]
> > >> +	for (i = 0; i < 16; i++) {
> > >> +		subdir[0] = hex_digits[i];
> > >> +		for (j = 0; j < 16; j++) {
> > >> +			subdir[1] = hex_digits[j];
> > >> +			if (access(subdir, R_OK | X_OK))
> > >> +				continue;
> > >> +			chdir(subdir);
> > >> +			if (!(dp = opendir("."))) {
> > >> +				error("can't open subdir %s", subdir);
> > >> +				continue;
> > >> +			}
> > >
> > > Looks like you're missing a chdir(".."); there.
> > 
> > Why would you even _need_ to chdir() anywhere, anyway?
> 
> To avoid appending the filename to the path before each lstat() I'd 
> guess.

Yes, that's pretty much the reason.

	Peter

```

## Linus Torvalds, 2006-04-27 20:07

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <Pine.LNX.4.64.0604271257010.3701@g5.osdl.org>
URL: https://gitlist.dev/e/Pine.LNX.4.64.0604271257010.3701%40g5.osdl.org
In-Reply-To: <20060427194559.GA26386@brainysmurf.cs.umu.se>

```


On Thu, 27 Apr 2006, Peter Hagervall wrote:
> > 
> > To avoid appending the filename to the path before each lstat() I'd 
> > guess.
> 
> Yes, that's pretty much the reason.

It's a bad reason, though.

For one thing, it just doesn't work. You'll have to chdir() back, and you 
can't use ".." in case the user has set up some symlink thing. So you end 
up doing other really strange things.

You can do this much more efficiently with something like this:

	const char *obj = git_object_directory();
	int len = strlen(obj);
	char *dir = malloc(len + 300);

	memcpy(dir, obj, len);
	if (len && obj[len-1] != '/')
		dir[len++] = '/';
	dir[len+2] = 0;
	for (i = 0; i < 16; i++) {
		dir[len] = hexdigit[i];
		for (j = 0; j < 16; j+) {
			dir[len+1] = hexdigit[j];
			dir[len+2] = 0;
			DIR *d = opendir(dir);
			if (!d)
				continue;
			nr += count(d, dir, len+2);
			closedir(d);
		}
	}

where the "count()" function just ends up doing something like

	int count(DIR *d, const char *prefix, int len)
	{
		int nr = 0;
		struct dirent *de;

		prefix[len++] = '/';
		while ((de = readdir(d)) != NULL) {
			int fd;
			if (de->d_name[0] == '.')
				continue;
			strcpy(prefix + len, de->d_name);
			fd = open(prefix, O_RDONLY);
			.. check if it's ok, perhaps.. ?
			if (ok)
				nr++;
			close(fd);
		}
		return nr;
	}

and you're done. Efficient, and it's easy to add the endign to the 
pathname, because you're passing in a buffer that is big enough, and 
you're telling people where they should put their suffixes..

And no, the above has never been compiled or tested, and I wrote it with 
one eye closed, while drinking heavily and experimenting with some funky 
'shrooms. So caveat emptor.

		Linus

```

## Peter Hagervall, 2006-04-27 20:51

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <20060427205155.GA26856@brainysmurf.cs.umu.se>
URL: https://gitlist.dev/e/20060427205155.GA26856%40brainysmurf.cs.umu.se
In-Reply-To: <Pine.LNX.4.64.0604271257010.3701@g5.osdl.org>

```
On Thu, Apr 27, 2006 at 01:07:27PM -0700, Linus Torvalds wrote:
> 
> 
> On Thu, 27 Apr 2006, Peter Hagervall wrote:
> > > 
> > > To avoid appending the filename to the path before each lstat() I'd 
> > > guess.
> > 
> > Yes, that's pretty much the reason.
> 
> It's a bad reason, though.
> 
> For one thing, it just doesn't work. You'll have to chdir() back, and you 
> can't use ".." in case the user has set up some symlink thing. So you end 
> up doing other really strange things.
> 
> You can do this much more efficiently with something like this:
> 
> 	const char *obj = git_object_directory();
> 	int len = strlen(obj);

<snip>

> 				continue;
> 			strcpy(prefix + len, de->d_name);
> 			fd = open(prefix, O_RDONLY);
> 			.. check if it's ok, perhaps.. ?
> 			if (ok)
> 				nr++;
> 			close(fd);
> 		}
> 		return nr;
> 	}
> 
> and you're done. Efficient, and it's easy to add the endign to the 
> pathname, because you're passing in a buffer that is big enough, and 
> you're telling people where they should put their suffixes..

Thanks, I'll make a third stab at it tomorrow, if anyone is interested
that is?

	Peter

```

## Junio C Hamano, 2006-04-27 22:07

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <7vaca6k6za.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vaca6k6za.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <20060427205155.GA26856@brainysmurf.cs.umu.se>

```
Peter Hagervall <hager@cs.umu.se> writes:

> Thanks, I'll make a third stab at it tomorrow, if anyone is interested
> that is?

How about something like this instead?

-- >8 --

 Makefile        |    2 -
 builtin-count.c |  124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 builtin.h       |    1 
 git.c           |    1 
 4 files changed, 127 insertions(+), 1 deletions(-)

diff --git a/Makefile b/Makefile
index 8ce27a6..14193aa 100644
--- a/Makefile
+++ b/Makefile
@@ -214,7 +214,7 @@ LIB_OBJS = \
 	$(DIFF_OBJS)
 
 BUILTIN_OBJS = \
-	builtin-log.o builtin-help.o
+	builtin-log.o builtin-help.o builtin-count.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
diff --git a/builtin-count.c b/builtin-count.c
new file mode 100644
index 0000000..cbbb7dd
--- /dev/null
+++ b/builtin-count.c
@@ -0,0 +1,124 @@
+/*
+ * Builtin "git count-objects".
+ *
+ * Copyright (c) 2006 Junio C Hamano
+ */
+
+#include "cache.h"
+
+static const char count_objects_usage[] = "git-count-objects [-v]";
+
+static void count_objects(DIR *d, char *path, int len, int verbose, 
+			  unsigned long *loose,
+			  unsigned long *loose_size,
+			  unsigned long *packed_loose,
+			  unsigned long *garbage)
+{
+	struct dirent *ent;
+	while ((ent = readdir(d)) != NULL) {
+		char hex[41];
+		unsigned char sha1[20];
+		const char *cp;
+		int bad = 0;
+
+		if ((ent->d_name[0] == '.') &&
+		    (ent->d_name[1] == 0 ||
+		     ((ent->d_name[1] == '.') && (ent->d_name[2] == 0))))
+			continue;
+		for (cp = ent->d_name; *cp; cp++) {
+			int ch = *cp;
+			if (('0' <= ch && ch <= '9') ||
+			    ('a' <= ch && ch <= 'f'))
+				continue;
+			bad = 1;
+			break;
+		}
+		if (cp - ent->d_name != 38)
+			bad = 1;
+		else {
+			struct stat st;
+			memcpy(path + len + 3, ent->d_name, 38);
+			path[len + 2] = '/';
+			path[len + 41] = 0;
+			if (stat(path, &st)) {
+				die("OOPS <%s>", path);
+				bad = 1;
+			}
+			else
+				(*loose_size) += st.st_blocks;
+		}
+		if (bad) {
+			if (verbose) {
+				error("garbage found: %.*s/%s",
+				      len + 2, path, ent->d_name);
+				(*garbage)++;
+			}
+			continue;
+		}
+		(*loose)++;
+		if (!verbose)
+			continue;
+		memcpy(hex, path+len, 2);
+		memcpy(hex+2, ent->d_name, 38);
+		hex[40] = 0;
+		if (get_sha1_hex(hex, sha1))
+			die("internal error");
+		if (has_sha1_pack(sha1))
+			(*packed_loose)++;
+	}
+}
+
+int cmd_count_objects(int ac, const char **av, char *ep)
+{
+	int i;
+	int verbose = 0;
+	const char *objdir = get_object_directory();
+	int len = strlen(objdir);
+	char *path = xmalloc(len + 50);
+	unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
+	unsigned long loose_size = 0;
+
+	for (i = 1; i < ac; i++) {
+		const char *arg = av[i];
+		if (*arg != '-')
+			break;
+		else if (!strcmp(arg, "-v"))
+			verbose = 1;
+		else
+			usage(count_objects_usage);
+	}
+
+	/* we do not take arguments other than flags for now */
+	if (i < ac)
+		usage(count_objects_usage);
+	memcpy(path, objdir, len);
+	if (len && objdir[len-1] != '/')
+		path[len++] = '/';
+	for (i = 0; i < 256; i++) {
+		DIR *d;
+		sprintf(path + len, "%02x", i);
+		d = opendir(path);
+		if (!d)
+			continue;
+		count_objects(d, path, len, verbose,
+			      &loose, &loose_size, &packed_loose, &garbage);
+		closedir(d);
+	}
+	if (verbose) {
+		struct packed_git *p;
+		for (p = packed_git; p; p = p->next) {
+			if (!p->pack_local)
+				continue;
+			packed += num_packed_objects(p);
+		}
+		printf("count: %lu\n", loose);
+		printf("size: %lu\n", loose_size / 2);
+		printf("in-pack: %lu\n", packed);
+		printf("prune-packable: %lu\n", packed_loose);
+		printf("garbage: %lu\n", garbage);
+	}
+	else
+		printf("%lu objects, %lu kilobytes\n",
+		       loose, loose_size / 2);
+	return 0;
+}
diff --git a/builtin.h b/builtin.h
index 47408a0..76169e3 100644
--- a/builtin.h
+++ b/builtin.h
@@ -19,5 +19,6 @@ extern int cmd_version(int argc, const c
 extern int cmd_whatchanged(int argc, const char **argv, char **envp);
 extern int cmd_show(int argc, const char **argv, char **envp);
 extern int cmd_log(int argc, const char **argv, char **envp);
+extern int cmd_count_objects(int argc, const char **argv, char **envp);
 
 #endif
diff --git a/git.c b/git.c
index 01b7e28..00fb399 100644
--- a/git.c
+++ b/git.c
@@ -46,6 +46,7 @@ static void handle_internal_command(int 
 		{ "log", cmd_log },
 		{ "whatchanged", cmd_whatchanged },
 		{ "show", cmd_show },
+		{ "count-objects", cmd_count_objects },
 	};
 	int i;
 

```

## Peter Hagervall, 2006-04-28 00:10

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <20060428001049.GA28347@brainysmurf.cs.umu.se>
URL: https://gitlist.dev/e/20060428001049.GA28347%40brainysmurf.cs.umu.se
In-Reply-To: <7vaca6k6za.fsf@assigned-by-dhcp.cox.net>

```
On Thu, Apr 27, 2006 at 03:07:37PM -0700, Junio C Hamano wrote:

...

> +int cmd_count_objects(int ac, const char **av, char *ep)
                                                       ^
...

> +extern int cmd_count_objects(int argc, const char **argv, char **envp);
                                                                  ^^
Looks like we have a type mismatch here, no?

	Peter

```

## Junio C Hamano, 2006-04-28 00:25

Subject: Re: [PATCH] C version of git-count-objects
Message-ID: <7vu08eim0t.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vu08eim0t.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <20060428001049.GA28347@brainysmurf.cs.umu.se>

```
Peter Hagervall <hager@cs.umu.se> writes:

> On Thu, Apr 27, 2006 at 03:07:37PM -0700, Junio C Hamano wrote:
>
> ...
>
>> +int cmd_count_objects(int ac, const char **av, char *ep)
>                                                        ^
> ...
>
>> +extern int cmd_count_objects(int argc, const char **argv, char **envp);
>                                                                   ^^
> Looks like we have a type mismatch here, no?

Interesting.  Lack of #include <builtin.h> was causing the
compiler not to notice X-<.

```
