Git development
 help / color / mirror / Atom feed
* Re: [patch] git: fix memory leak #2 in read-cache.c
From: Martin Schlemmer @ 2005-04-14 14:25 UTC (permalink / raw)
  To: Ingo Molnar; +Cc: git, Linus Torvalds
In-Reply-To: <20050414132550.GA25496@elte.hu>

[-- Attachment #1: Type: text/plain, Size: 888 bytes --]

On Thu, 2005-04-14 at 15:25 +0200, Ingo Molnar wrote:
> * Ingo Molnar <mingo@elte.hu> wrote:
> 
> > this patch fixes a memory leak in read-cache.c: when there's cache 
> > entry collision we should free the previous one.
> 
> > +		free(active_cache[pos]);
> >  		active_cache[pos] = ce;
> 
> i'm having second thoughs about this one: active_cache entries are not 
> always malloc()-ed - e.g. read_cache() will construct them from the 
> mmap() of the index file. Which must not be free()d!
> 
> one safe solution would be to malloc() all these entries and copy them 
> over from the index file? Slightly slower but safer and free()-able when 
> update-cache.c notices a collision. The (tested) patch below does this.
> 
> this would also make Martin Schlemmer's update-cache.c fix safe.
> 

Ok, bad me it seems - assumed it was malloc'd.


-- 
Martin Schlemmer


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* [PATCH] Git pasky include commit-id in Makefile
From: Darren Williams @ 2005-04-14 14:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Petr Baudis

Currently the commit-id script is not install with
make install, this patches includes it in the SCRIPT
target. This patch is against git-pasky-0.4

Signed-off-by Darren Williams <dsw@gelato.unsw.edu.au>

COPYING:  fe2a4177a760fd110e78788734f167bd633be8de
Makefile:  ca50293c4f211452d999b81f122e99babb9f2987
--- Makefile
+++ Makefile    2005-04-14 23:52:35.701915203 +1000
@@ -19,7 +19,7 @@
 SCRIPT=        parent-id tree-id git gitXnormid.sh gitadd.sh gitaddremote.sh \
        gitcommit.sh gitdiff-do gitdiff.sh gitlog.sh gitls.sh gitlsobj.sh \
        gitmerge.sh gitpull.sh gitrm.sh gittag.sh gittrack.sh gitexport.sh \
-       gitapply.sh gitcancel.sh gitlntree.sh
+       gitapply.sh gitcancel.sh gitlntree.sh commit-id

 GEN_SCRIPT= gitversion.sh

README:  ded1a3b20e9bbe1f40e487ba5f9361719a1b6b85
VERSION:  c27bd67cd632cc15dd520fbfbf807d482efa2dcf
cache.h:  4d382549041d3281f8d44aa2e52f9f8ec47dd420
cat-file.c:  45be1badaa8517d4e3a69e0bf1cac2e90191e475
check-files.c:  927b0b9aca742183fc8e7ccd73d73d8d5427e98f
checkout-cache.c:  f06871cdbc1b18ea93bdf4e17126aeb4cca1373e
commit-id:  65c81756c8f10d513d073ecbd741a3244663c4c9
commit-tree.c:  12196c79f31d004dff0df1f50dda67d8204f5568
diff-tree.c:  7dcc9eb7782fa176e27f1677b161ce78ac1d2070
fsck-cache.c:  9c900fe458cecd2bdb4c4571a584115b5cf24f22
git:  2c557dcf2032325acc265b577ee104e605fdaede
gitXnormid.sh:  a5d7a9f4a6e8d4860f35f69500965c2a493d80de
gitadd.sh:  3ed93ea0fcb995673ba9ee1982e0e7abdbe35982
gitaddremote.sh:  bf1f28823da5b5270aa8fa05b321faa514a57a11
gitapply.sh:  d0e3c46e2ce1ee74e1a87ee6137955fa9b35c27b
gitcancel.sh:  ec58f7444a42cd3cbaae919fc68c70a3866420c0
gitcommit.sh:  3629f67bbd3f171d091552814908b67af7537f4d
gitdiff-do:  d6174abceab34d22010c36a8453a6c3f3f184fe0
gitdiff.sh:  5e47c4779d73c3f2f39f6be714c0145175933197
gitexport.sh:  dad00bf251b38ce522c593ea9631f842d8ccc934
gitlntree.sh:  17c4966ea64aeced96ae4f1b00f3775c1904b0f1
gitlog.sh:  177c6d12dd9fa4b4920b08451ffe4badde544a39
gitls.sh:  b6f15d82f16c1e9982c5031f3be22eb5430273af
gitlsobj.sh:  128461d3de6a42cfaaa989fc6401bebdfa885b3f
gitmerge.sh:  23e4a3ff342c6005928ceea598a2f52de6fb9817
gitpull.sh:  0883898dda579e3fa44944b7b1d909257f6dc63e
gitrm.sh:  5c18c38a890c9fd9ad2b866ee7b529539d2f3f8f
gittag.sh:  c8cb31385d5a9622e95a4e0b2d6a4198038a659c
gittrack.sh:  03d6db1fb3a70605ef249c632c04e542457f0808
init-db.c:  aa00fbb1b95624f6c30090a17354c9c08a6ac596
ls-tree.c:  3e2a6c7d183a42e41f1073dfec6794e8f8a5e75c
parent-id:  1801c6fe426592832e7250f8b760fb9d2e65220f
read-cache.c:  7a6ae8b9b489f6b67c82e065dedd5716a6bfc0ef
read-tree.c:  eb548148aa6d212f05c2c622ffbe62a06cd072f9
rev-tree.c:  395b0b3bfadb0537ae0c62744b25ead4b487f3f6
show-diff.c:  a531ca4078525d1c8dcf84aae0bfa89fed6e5d96
show-files.c:  a9fa6767a418f870a34b39379f417bf37b17ee18
tree-id:  cb70e2c508a18107abe305633612ed702aa3ee4f
update-cache.c:  62d0a6c41560d40863c44599355af10d9e089312
write-tree.c:  1534477c91169ebddcf953e3f4d2872495477f6b
 
---------------------------------------------------
Darren Williams <dsw AT gelato.unsw.edu.au>
Gelato@UNSW <www.gelato.unsw.edu.au>
--------------------------------------------------

^ permalink raw reply

* [patch] git: fix memory leak #3 in update-cache.c
From: Ingo Molnar @ 2005-04-14 13:31 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414130309.GA18703@elte.hu>


the patch below fixes a third memory leak in update-cache.c. (the 
mmap-ed file needs to be unmapped too) Ontop of the previous 
update-cache.c patches.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- update-cache.c.orig
+++ update-cache.c
@@ -32,6 +32,8 @@ static int index_fd(const char *path, in
 	if (!out || (int)(long)in == -1) {
 		free(out);
 		free(metadata);
+		if ((int)(long)in != -1 && size)
+			munmap(in, size);
 		return -1;
 	}
 
@@ -66,6 +68,8 @@ static int index_fd(const char *path, in
 		
 	free(out);
 	free(metadata);
+	if (size)
+		munmap(in, size);
 
 	return ret;
 }

^ permalink raw reply

* Re: [patch] git: fix memory leak #2 in read-cache.c
From: Ingo Molnar @ 2005-04-14 13:25 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds, Martin Schlemmer
In-Reply-To: <20050414123934.GA15420@elte.hu>


* Ingo Molnar <mingo@elte.hu> wrote:

> this patch fixes a memory leak in read-cache.c: when there's cache 
> entry collision we should free the previous one.

> +		free(active_cache[pos]);
>  		active_cache[pos] = ce;

i'm having second thoughs about this one: active_cache entries are not 
always malloc()-ed - e.g. read_cache() will construct them from the 
mmap() of the index file. Which must not be free()d!

one safe solution would be to malloc() all these entries and copy them 
over from the index file? Slightly slower but safer and free()-able when 
update-cache.c notices a collision. The (tested) patch below does this.

this would also make Martin Schlemmer's update-cache.c fix safe.

(without this second patch, free(active_cache[pos]) might crash, and 
that crash is would possibly be remote exploitable via a special 
repository that tricks the index file to look in a certain way.)

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- read-cache.c.orig
+++ read-cache.c
@@ -453,10 +453,17 @@ int read_cache(void)
 
 	offset = sizeof(*hdr);
 	for (i = 0; i < hdr->entries; i++) {
-		struct cache_entry *ce = map + offset;
+		struct cache_entry *ce = map + offset, *tmp;
 		offset = offset + ce_size(ce);
-		active_cache[i] = ce;
+
+		tmp = malloc(ce_size(ce));
+		if (!tmp)
+			return error("malloc failed");
+		memcpy(tmp, ce, ce_size(ce));
+		active_cache[i] = tmp;
 	}
+	munmap(map, size);
+
 	return active_nr;
 
 unmap:


^ permalink raw reply

* [patch] git: fix memory leak in write-tree.c
From: Ingo Molnar @ 2005-04-14 13:12 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414120834.GA14290@elte.hu>


this patch fixes a memory leak in write-tree.c's write_tree() function - 
we didnt free the temporary output buffer. Depending on the size of the 
tree written out this could leak a significant amount of RAM.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- write-tree.c.orig
+++ write-tree.c
@@ -33,12 +33,12 @@ static int write_tree(struct cache_entry
 {
 	unsigned char subdir_sha1[20];
 	unsigned long size, offset;
-	char *buffer;
+	char *buffer0, *buffer;
 	int i, nr;
 
 	/* Guess at some random initial size */
 	size = 8192;
-	buffer = malloc(size);
+	buffer0 = buffer = malloc(size);
 	offset = ORIG_OFFSET;
 
 	nr = 0;
@@ -97,6 +97,8 @@ static int write_tree(struct cache_entry
 	offset -= i;
 
 	write_sha1_file(buffer, offset, returnsha1);
+	free(buffer0);
+
 	return nr;
 }
 

^ permalink raw reply

* [patch] git: clean up add_file_to_cache() in update-cache.c
From: Ingo Molnar @ 2005-04-14 13:08 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414130309.GA18703@elte.hu>


this patch cleans up add_file_to_cache() to free up all memory it 
allocates. This has no significance right now as the only user of 
add_file_to_cache() die()s immediately in the 'leak' paths - but if the 
function is ever used without dying then this uncleanliness could lead 
to a memory leak.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- update-cache.c.orig
+++ update-cache.c
@@ -120,10 +120,17 @@ static int add_file_to_cache(char *path)
 	ce->st_size = st.st_size;
 	ce->namelen = namelen;
 
-	if (index_fd(path, namelen, ce, fd, &st) < 0)
+	if (index_fd(path, namelen, ce, fd, &st) < 0) {
+		free(ce);
 		return -1;
+	}
 
-	return add_cache_entry(ce, allow_add);
+	if (add_cache_entry(ce, allow_add)) {
+		free(ce);
+		return -1;
+	}
+
+	return 0;
 }
 
 static int match_data(int fd, void *buffer, unsigned long size)


^ permalink raw reply

* [patch] git: fix memory leaks in update-cache.c
From: Ingo Molnar @ 2005-04-14 13:03 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414121819.GA14380@elte.hu>


this patch fixes two memory leaks in update-cache.c: we didnt free the 
temporary input and output buffers used for compression.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- update-cache.c.orig
+++ update-cache.c
@@ -23,13 +23,17 @@ static int index_fd(const char *path, in
 	void *metadata = malloc(namelen + 200);
 	void *in;
 	SHA_CTX c;
+	int ret;
 
 	in = "";
 	if (size)
 		in = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 	close(fd);
-	if (!out || (int)(long)in == -1)
+	if (!out || (int)(long)in == -1) {
+		free(out);
+		free(metadata);
 		return -1;
+	}
 
 	memset(&stream, 0, sizeof(stream));
 	deflateInit(&stream, Z_BEST_COMPRESSION);
@@ -58,7 +62,12 @@ static int index_fd(const char *path, in
 	SHA1_Update(&c, out, stream.total_out);
 	SHA1_Final(ce->sha1, &c);
 
-	return write_sha1_buffer(ce->sha1, out, stream.total_out);
+	ret = write_sha1_buffer(ce->sha1, out, stream.total_out);
+		
+	free(out);
+	free(metadata);
+
+	return ret;
 }
 
 /*

^ permalink raw reply

* [patch] git: fix 1-byte overflow in show-files.c
From: Ingo Molnar @ 2005-04-14 12:53 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414121819.GA14380@elte.hu>


this patch fixes a 1-byte overflow in show-files.c (looks narrow is is 
probably not exploitable). A specially crafted db object (tree) might 
trigger this overflow.

'fullname' is an array of 4096+1 bytes, and we do readdir(), which 
produces entries that have strings with a length of 0-255 bytes. With a 
long enough 'base', it's possible to construct a tree with a name in it 
that has directory whose name ends precisely at offset 4095. At that 
point this code:

                        case DT_DIR:
                                memcpy(fullname + baselen + len, "/", 2);

will attempt to append a "/" string to the directory name - resulting in 
a 1-byte overflow (a zero byte is written to offset 4097, which is 
outside the array).

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- show-files.c.orig
+++ show-files.c
@@ -49,7 +49,7 @@ static void read_directory(const char *p
 
 	if (dir) {
 		struct dirent *de;
-		char fullname[MAXPATHLEN + 1];
+		char fullname[MAXPATHLEN + 2]; // +1 byte for trailing slash
 		memcpy(fullname, base, baselen);
 
 		while ((de = readdir(dir)) != NULL) {


^ permalink raw reply

* [patch] git: fix memory leak #2 in read-cache.c
From: Ingo Molnar @ 2005-04-14 12:39 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414123258.GA15148@elte.hu>


this patch fixes a memory leak in read-cache.c: when there's cache entry 
collision we should free the previous one.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- read-cache.c.orig
+++ read-cache.c
@@ -369,6 +369,7 @@ int add_cache_entry(struct cache_entry *
 
 	/* existing match? Just replace it */
 	if (pos >= 0) {
+		free(active_cache[pos]);
 		active_cache[pos] = ce;
 		return 0;
 	}

^ permalink raw reply

* [patch] git: fix memory leaks in read-cache.c
From: Ingo Molnar @ 2005-04-14 12:32 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414120834.GA14290@elte.hu>


this patch fixes a common and a rare memory leak in read-cache.c.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- read-cache.c.orig
+++ read-cache.c
@@ -226,8 +226,11 @@ int write_sha1_file(char *buf, unsigned 
 	SHA1_Update(&c, compressed, size);
 	SHA1_Final(sha1, &c);
 
-	if (write_sha1_buffer(sha1, compressed, size) < 0)
+	if (write_sha1_buffer(sha1, compressed, size) < 0) {
+		free(compressed);
 		return -1;
+	}
+	free(compressed);
 	if (returnsha1)
 		memcpy(returnsha1, sha1, 20);
 	return 0;

^ permalink raw reply

* [patch] cleanup: read_sha1_file() -> malloc_read_sha1_file()
From: Ingo Molnar @ 2005-04-14 12:23 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414121819.GA14380@elte.hu>


this patch renames read_sha1_file() to malloc_read_sha1_file().

There were a handful of memory-leaks related to read_sha1_file(), and 
some of those could possibly have been found sooner if the name 
indicated that an implicit malloc() is occurs within read_sha1_file().  
This patch is ontop of the previous patches i sent.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- cat-file.c.orig
+++ cat-file.c
@@ -14,7 +14,7 @@ int main(int argc, char **argv)
 
 	if (argc != 3 || get_sha1_hex(argv[2], sha1))
 		usage("cat-file [-t | tagname] <sha1>");
-	buf = read_sha1_file(sha1, type, &size);
+	buf = malloc_read_sha1_file(sha1, type, &size);
 	if (!buf)
 		die("cat-file %s: bad file", argv[2]);
 	if (!strcmp("-t", argv[1])) {
--- merge-tree.c.orig
+++ merge-tree.c
@@ -11,7 +11,7 @@ static struct tree_entry *read_tree(unsi
 {
 	char type[20];
 	unsigned long size;
-	void *buf = read_sha1_file(sha1, type, &size);
+	void *buf = malloc_read_sha1_file(sha1, type, &size);
 	struct tree_entry *ret = NULL, **tp = &ret;
 
 	if (!buf || strcmp(type, "tree"))
--- diff-tree.c.orig
+++ diff-tree.c
@@ -62,7 +62,7 @@ static void show_file(const char *prefix
 		char *newbase = malloc_base(base, path, strlen(path));
 		void *tree;
 
-		tree = read_sha1_file(sha1, type, &size);
+		tree = malloc_read_sha1_file(sha1, type, &size);
 		if (!tree || strcmp(type, "tree"))
 			die("corrupt tree sha %s", sha1_to_hex(sha1));
 
@@ -164,10 +164,10 @@ static int diff_tree_sha1(const unsigned
 	char type[20];
 	int retval;
 
-	tree1 = read_sha1_file(old, type, &size1);
+	tree1 = malloc_read_sha1_file(old, type, &size1);
 	if (!tree1 || strcmp(type, "tree"))
 		die("unable to read source tree (%s)", sha1_to_hex(old));
-	tree2 = read_sha1_file(new, type, &size2);
+	tree2 = malloc_read_sha1_file(new, type, &size2);
 	if (!tree2 || strcmp(type, "tree"))
 		die("unable to read destination tree (%s)", sha1_to_hex(new));
 	retval = diff_tree(tree1, size1, tree2, size2, base);
--- show-diff.c.orig
+++ show-diff.c
@@ -25,7 +25,7 @@ static void show_diff_empty(struct cache
 	int lines=0;
 	unsigned char type[20], *p, *end;
 
-	old = read_sha1_file(ce->sha1, type, &size);
+	old = malloc_read_sha1_file(ce->sha1, type, &size);
 	if (size > 0) {
 		int startline = 1;
 		int c = 0;
@@ -99,7 +99,7 @@ int main(int argc, char **argv)
 		if (silent)
 			continue;
 
-		new = read_sha1_file(ce->sha1, type, &size);
+		new = malloc_read_sha1_file(ce->sha1, type, &size);
 		show_differences(ce->name, new, size);
 		free(new);
 	}
--- read-cache.c.orig
+++ read-cache.c
@@ -183,7 +183,7 @@ void * unpack_sha1_file(void *map, unsig
 	return buf;
 }
 
-void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
+void * malloc_read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
 {
 	unsigned long mapsize;
 	void *map, *buf;
--- update-cache.c.orig
+++ update-cache.c
@@ -141,7 +141,7 @@ static int compare_data(struct cache_ent
 		unsigned long size;
 		char type[20];
 
-		buffer = read_sha1_file(ce->sha1, type, &size);
+		buffer = malloc_read_sha1_file(ce->sha1, type, &size);
 		if (buffer) {
 			if (size == expected_size && !strcmp(type, "blob"))
 				match = match_data(fd, buffer, size);
--- ls-tree.c.orig
+++ ls-tree.c
@@ -11,7 +11,7 @@ static int list(unsigned char *sha1)
 	unsigned long size;
 	char type[20];
 
-	buffer = read_sha1_file(sha1, type, &size);
+	buffer = malloc_read_sha1_file(sha1, type, &size);
 	if (!buffer)
 		die("unable to read sha1 file");
 	if (strcmp(type, "tree"))
--- checkout-cache.c.orig
+++ checkout-cache.c
@@ -72,7 +72,7 @@ static int write_entry(struct cache_entr
 	long wrote;
 	char type[20];
 
-	new = read_sha1_file(ce->sha1, type, &size);
+	new = malloc_read_sha1_file(ce->sha1, type, &size);
 	if (!new || strcmp(type, "blob")) {
 		if (new)
 			free(new);
--- cache.h.orig
+++ cache.h
@@ -95,7 +95,7 @@ extern int write_sha1_buffer(const unsig
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern void * map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
-extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
+extern void * malloc_read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
 extern int write_sha1_file(char *buf, unsigned len, unsigned char *return_sha1);
 extern int check_sha1_signature(unsigned char *sha1, void *buf, unsigned long size);
 
--- rev-tree.c.orig
+++ rev-tree.c
@@ -73,7 +73,7 @@ static int parse_commit(unsigned char *s
 		unsigned char parent[20];
 
 		rev->flags |= SEEN;
-		buffer = bufptr = read_sha1_file(sha1, type, &size);
+		buffer = bufptr = malloc_read_sha1_file(sha1, type, &size);
 		if (!buffer || strcmp(type, "commit")) {
 			if (buffer)
 				free(buffer);
--- read-tree.c.orig
+++ read-tree.c
@@ -27,7 +27,7 @@ static int read_tree(unsigned char *sha1
 	unsigned long size;
 	char type[20];
 
-	buffer0 = buffer = read_sha1_file(sha1, type, &size);
+	buffer0 = buffer = malloc_read_sha1_file(sha1, type, &size);
 	if (!buffer)
 		return -1;
 	if (strcmp(type, "tree")) {


^ permalink raw reply

* [patch] git: fix overflow in update-cache.c
From: Ingo Molnar @ 2005-04-14 12:18 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414120834.GA14290@elte.hu>


this patch fixes a 1-byte overflow in update-cache.c (probably not 
exploitable). A specially crafted db object might trigger this overflow.

the bug is that normally the 'type' field is parsed by read_sha1_file(), 
via:

        if (sscanf(buffer, "%10s %lu", type, size) != 2)

i.e. 0-10 long strings, which take 1-11 bytes of space. Normally the 
type strings are stored in char [20] arrays, but in update-cache.c that 
is char [10], so a 1 byte overflow might occur.

This should not happen with a 'friendly' DB, as the longest type string 
("commit") is 7 bytes long. The fix is to use the customary char [20].

(someone might want to clean those open-coded constants up with a 
TYPE_LEN define, they do tend to cause problems like this. I'm not 
against open-coded constants (they make code much more readable), but 
for fields that get filled in from possibly hostile objects this is 
playing with fire.)

hey, this might be the first true security fix for GIT? ;-)

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- update-cache.c.orig
+++ update-cache.c
@@ -139,7 +139,7 @@ static int compare_data(struct cache_ent
 	if (fd >= 0) {
 		void *buffer;
 		unsigned long size;
-		char type[10];
+		char type[20];
 
 		buffer = read_sha1_file(ce->sha1, type, &size);
 		if (buffer) {

^ permalink raw reply

* Re: Re: Merge with git-pasky II.
From: Petr Baudis @ 2005-04-14 12:16 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, git
In-Reply-To: <7vvf6pr4oq.fsf@assigned-by-dhcp.cox.net>

Dear diary, on Thu, Apr 14, 2005 at 01:14:13PM CEST, I got a letter
where Junio C Hamano <junkio@cox.net> told me that...
> Here is a diff to update the git-merge.perl script I showed you
> earlier today ;-).  It contains the following updates against
> your HEAD (bb95843a5a0f397270819462812735ee29796fb4).

Bah, you outran me. ;-)

>  * git-merge.perl command we talked about on the git list.  I've
>    covered the changed-to-the-same case etc.  I still haven't done
>    anything about file-vs-directory case yet.
> 
>    It does warn when it needed to run merge to automerge and let
>    merge give a warning message about conflicts if any.  In
>    modify/remove cases, modified in one but removed in the other
>    files are left in either $path~A~ or $path~B~ in the merge
>    temporary directory, and the script issues a warning at the
>    end.

I think I will take it rather my working git merge implementation - it's
getting insane in bash. ;-)

I'll change it to use the cool git-pasky stuff (commit-id etc) and its
style of committing - that is, it will merely record the update-caches
to be done upon commit, and it will read-tree the branch we are merging
to instead of the ancestor. (So that git diff gives useful output.)

>  * show-files and ls-tree updates to add -z flag to NUL terminate records;
>    this is needed for git-merge.perl to work.
> 
>  * show-diff updates to add -r flag to squelch diffs for files not in
>    the working directory.  This is mainly useful when verifying the
>    result of an automated merge.

-r traditionally means recursive - what's the reasoning behind the
choice of this letter?

>  * update-cache updates to add "--cacheinfo mode sha1" flag to register
>    a file that is not in the current working directory.  Needed for
>    minimum-checkout merging by git-merge.perl.
> 
> 
> Signed-off-by: Junio C Hamano <junkio@cox.net>
> 
> ---
> 
>  git-merge.perl |  247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  ls-tree.c      |    9 +-
>  show-diff.c    |   11 +-
>  show-files.c   |   12 ++
>  update-cache.c |   25 +++++
>  5 files changed, 296 insertions(+), 8 deletions(-)
> 
> diff -x .git -Nru ,,1/git-merge.perl ,,2/git-merge.perl
> --- ,,1/git-merge.perl	1969-12-31 16:00:00.000000000 -0800
> +++ ,,2/git-merge.perl	2005-04-14 04:00:14.000000000 -0700
> @@ -0,0 +1,247 @@
> +#!/usr/bin/perl -w
> +
> +use Getopt::Long;

use strict?

> +
> +my $full_checkout = 0;
> +my $oneside_checkout = 0;
> +GetOptions("full" => \$full_checkout,
> +	   "oneside" => \$oneside_checkout)
> +    or die;
> +
> +if ($full_checkout) {
> +    $oneside_checkout = 1;
> +}
> +
> +sub read_rev_tree {
> +    my (@head) = @_;
> +    my ($fhi);
> +    open $fhi, '-|', 'rev-tree', '--edges', @head
> +	or die "$!: rev-tree --edges @head";
> +    my %common;
> +    while (<$fhi>) {
> +	chomp;
> +	(undef, undef, my @common) = split(/ /, $_);
> +	for (@common) {
> +	    if (s/^([a-f0-f]{40}):3$/$1/) {
> +		$common{$_}++;
> +	    }
> +	}
> +    }
> +    close $fhi;
> +
> +    my @common = (map { $_->[1] }
> +		  sort { $b->[0] <=> $a->[0] }
> +		  map { [ $common{$_} => $_ ] }
> +		  keys %common);
> +
> +    return $common[0];
> +}

It'd be simpler to do just

	my @common = (map { $common{$_} }
	              sort { $b <=> $a }
	              keys %common)

But I really think this is a horrible heuristic. I believe you should
take the latest commit in the --edges output, and from that choose the
base whose rev-tree --edges the_base merged_branch has the least lines
on output. (That is, the path to it is shortest - ideally it's already
part of the merged_branch.)

> +
> +sub read_commit_tree {
> +    my ($commit) = @_;
> +    my ($fhi);
> +    open $fhi, '-|', 'cat-file', 'commit', $commit
> +	or die "$!: cat-file commit $commit";
> +    my $tree = <$fhi>;
> +    close $fhi;
> +    $tree =~ s/^tree //;
> +    return $tree;
> +}
> +
> +# Reads diff-tree -r output and gives a hash that maps a path
> +# to 3-tuple (old-mode new-mode new-sha).
> +# When creating, old-mode is undef.  When removing, new-* are undef.

What about

sub OLDMODE { 0 }
sub NEWMODE { 1 }
sub NEWSHA { 2 }

and then using that when accessing the tuple? Would make the code
much more readable.

> +sub read_diff_tree {
> +    my (@tree) = @_;
> +    my ($fhi);
> +    local ($_, $/);
> +    $/ = "\0"; 
> +    my %path;
> +    open $fhi, '-|', 'diff-tree', '-r', @tree
> +	or die "$!: diff-tree -r @tree";
> +    while (<$fhi>) {
> +	chomp;
> +	if (/^\*([0-7]+)->([0-7]+)\tblob\t[0-9a-f]+->([0-9a-f]{40})\t(.*)$/s) {
> +	    $path{$4} = [$1, $2, $3];
> +	}
> +	elsif (/^\+([0-7]+)\tblob\t([0-9a-f]{40})\t(.*)$/s) {
> +	    $path{$3} = [undef, $1, $2];
> +	}
> +	elsif (/^\-([0-7]+)\tblob\t[0-9a-f]{40}\t(.*)$/s) {
> +	    $path{$2} = [$1, undef, undef];
> +	}
> +	else {
> +	    die "cannot parse diff-tree output: $_";
> +	}
> +    }
> +    close $fhi;
> +    return %path;
> +}
> +
> +sub read_show_files {
> +    my ($fhi);
> +    local ($_, $/);
> +    $/ = "\0"; 
> +    open $fhi, '-|', 'show-files', '-z'
> +	or die "$!: show-files -z";
> +    my (@path) = map { chomp; $_ } <$fhi>;
> +    close $fhi;
> +    return @path;
> +}
> +
> +sub checkout_file {
> +    my ($path, $info) = @_;
> +    my (@elt) = split(/\//, $path);
> +    my $j = '';
> +    my $tail = pop @elt;
> +    my ($fhi, $fho);
> +    for (@elt) {
> +	mkdir "$j$_";
> +	$j = "$j$_/";
> +    }
> +    open $fho, '>', "$path";
> +    open $fhi, '-|', 'cat-file', 'blob', $info->[2]
> +	or die "$!: cat-file blob $info->[2]";
> +    while (<$fhi>) {
> +	print $fho $_;
> +    }
> +    close $fhi;
> +    close $fho;
> +    chmod oct("0$info->[1]"), "$path";
> +}
> +
> +sub record_file {
> +    my ($path, $info) = @_;
> +    system ('update-cache', '--add', '--cacheinfo',
> +	    $info->[1], $info->[2], $path);
> +}
> +
> +sub merge_tree {
> +    my ($path, $info0, $info1) = @_;
> +    checkout_file(',,merge-0', $info0);
> +    checkout_file(',,merge-1', $info1);
> +    system 'checkout-cache', $path;
> +    my ($fhi, $fho);
> +    open $fhi, '-|', 'merge', '-p', ',,merge-0', $path, ',,merge-1';
> +    open $fho, '>', "$path+";
> +    local ($/);
> +    while (<$fhi>) { print $fho $_; }
> +    close $fhi;
> +    close $fho;
> +    unlink ',,merge-0', ',,merge-1';
> +    rename "$path+", $path;
> +    # There is no reason to prefer info0 over info1 but
> +    # we need to pick one.
> +    chmod oct("0$info0->[1]"), "$path";
> +}

It is a good idea to check merge's exit code and give a notice at the
end if there were any conflicts.

> +
> +# Find common ancestor of two trees.
> +my $common = read_rev_tree(@ARGV);
> +print "Common ancestor: $common\n";
> +
> +# Create a temporary directory and go there.
> +system 'rm', '-rf', ',,merge-temp';

Can't we call it just ,,merge?

> +for ((',,merge-temp', '.git')) { mkdir $_; chdir $_; }
> +symlink "../../.git/objects", "objects";
> +chdir '..';
> +
> +my $ancestor_tree = read_commit_tree($common);
> +system 'read-tree', $ancestor_tree;
> +
> +my %tree0 = read_diff_tree($ancestor_tree, read_commit_tree($ARGV[0]));
> +my %tree1 = read_diff_tree($ancestor_tree, read_commit_tree($ARGV[1]));
> +
> +my @ancestor_file = read_show_files();
> +my %ancestor_file = map { $_ => 1 } @ancestor_file;
> +
> +for (@ancestor_file) {
> +    if (! exists $tree0{$_} && ! exists $tree1{$_}) {
> +	if ($full_checkout) {
> +	    system 'checkout-cache', $_;
> +	}
> +	print STDERR "O - $_\n";

Huh, what are you trying to do here? I think you should just record
remove, no? (And I wouldn't do anything with my read-tree. ;-)

> +    }
> +}
> +
> +for my $set ([\%tree0, \%tree1, 'A'], [\%tree1, \%tree0, 'B']) {
> +    my ($treeA, $treeB, $side) = @$set;
> +    while (my ($path, $info) = each %$treeA) {
> +	# In this loop we do not deal with overlaps.
> +	next if (exists $treeB->{$path});
> +
> +	if (! defined $info->[1]) {
> +	    # deleted in this tree only.
> +	    unlink $path;
> +	    system 'update-cache', '--remove', $path;
> +	    print STDERR "$side D $path\n";
> +	}
> +	else {
> +	    # modified or created in this tree only.
> +	    print STDERR "$side M $path\n";
> +	    if ($oneside_checkout) {
> +		checkout_file($path, $info);
> +		system 'update-cache', '--add', "$path";
> +	    } else {
> +		record_file($path, $info);
> +	    }
> +	}
> +    }
> +}
..snip..

Hmm, I think I will just need to play with the script a lot. ;-)

-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor

^ permalink raw reply

* [patch] git: fix memory leak in show-diff.c
From: Ingo Molnar @ 2005-04-14 12:08 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414112638.GA12593@elte.hu>


this patch fixes a memory leak in show-diff.c.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- show-diff.c.orig
+++ show-diff.c
@@ -53,6 +53,7 @@ static void show_diff_empty(struct cache
 			printf("\n");
 		fflush(stdout);
 	}
+	free(old);
 }
 
 int main(int argc, char **argv)

^ permalink raw reply

* [patch] git: report parse_commit() errors in rev-tree.c
From: Ingo Molnar @ 2005-04-14 12:05 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414115800.GB14065@elte.hu>


make actual use of the parse_commit() return value and print a warning, 
instead of silently ignoring it. Should never trigger on a valid DB.

(alternatively we might use a die() in the sanity check place and could 
remove all the return code handling?)

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- rev-tree.c.orig
+++ rev-tree.c
@@ -64,6 +64,7 @@ static unsigned long parse_commit_date(c
 static int parse_commit(unsigned char *sha1)
 {
 	struct revision *rev = lookup_rev(sha1);
+	int ret = 0;
 
 	if (!(rev->flags & SEEN)) {
 		void *buffer, *bufptr;
@@ -81,13 +82,13 @@ static int parse_commit(unsigned char *s
 		bufptr += 46; /* "tree " + "hex sha1" + "\n" */
 		while (!memcmp(bufptr, "parent ", 7) && !get_sha1_hex(bufptr+7, parent)) {
 			add_relationship(rev, parent);
-			parse_commit(parent);
+			ret |= parse_commit(parent);
 			bufptr += 48;	/* "parent " + "hex sha1" + "\n" */
 		}
 		rev->date = parse_commit_date(bufptr);
 		free(buffer);
 	}
-	return 0;	
+	return ret;
 }
 
 static void read_cache_file(const char *path)
@@ -208,7 +209,8 @@ int main(int argc, char **argv)
 		}
 		if (nr >= MAX_COMMITS || get_sha1_hex(arg, sha1[nr]))
 			usage("rev-tree [--edges] [--cache <cache-file>] <commit-id> [<commit-id>]");
-		parse_commit(sha1[nr]);
+		if (parse_commit(sha1[nr]))
+			fprintf(stderr, "warning: rev-tree: bad commit!\n");
 		nr++;
 	}
 

^ permalink raw reply

* [patch] git: fix rare memory leak in rev-tree.c
From: Ingo Molnar @ 2005-04-14 11:58 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414113527.GA13790@elte.hu>


this patch fixes a rare memory leak in rev-tree.c.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- rev-tree.c.orig
+++ rev-tree.c
@@ -73,8 +73,11 @@ static int parse_commit(unsigned char *s
 
 		rev->flags |= SEEN;
 		buffer = bufptr = read_sha1_file(sha1, type, &size);
-		if (!buffer || strcmp(type, "commit"))
+		if (!buffer || strcmp(type, "commit")) {
+			if (buffer)
+				free(buffer);
 			return -1;
+		}
 		bufptr += 46; /* "tree " + "hex sha1" + "\n" */
 		while (!memcmp(bufptr, "parent ", 7) && !get_sha1_hex(bufptr+7, parent)) {
 			add_relationship(rev, parent);

^ permalink raw reply

* [patch] git: fix memory leaks in read-tree.c
From: Ingo Molnar @ 2005-04-14 11:54 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414113527.GA13790@elte.hu>


this patch fixes one common, and 4 rare memory leaks in read-tree.c.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- read-tree.c.orig
+++ read-tree.c
@@ -23,23 +23,27 @@ static int read_one_entry(unsigned char 
 
 static int read_tree(unsigned char *sha1, const char *base, int baselen)
 {
-	void *buffer;
+	void *buffer0, *buffer;
 	unsigned long size;
 	char type[20];
 
-	buffer = read_sha1_file(sha1, type, &size);
+	buffer0 = buffer = read_sha1_file(sha1, type, &size);
 	if (!buffer)
 		return -1;
-	if (strcmp(type, "tree"))
+	if (strcmp(type, "tree")) {
+		free(buffer);
 		return -1;
+	}
 	while (size) {
 		int len = strlen(buffer)+1;
 		unsigned char *sha1 = buffer + len;
 		char *path = strchr(buffer, ' ')+1;
 		unsigned int mode;
 
-		if (size < len + 20 || sscanf(buffer, "%o", &mode) != 1)
+		if (size < len + 20 || sscanf(buffer, "%o", &mode) != 1) {
+			free(buffer0);
 			return -1;
+		}
 
 		buffer = sha1 + 20;
 		size -= len + 20;
@@ -53,13 +57,19 @@ static int read_tree(unsigned char *sha1
 			newbase[baselen + pathlen] = '/';
 			retval = read_tree(sha1, newbase, baselen + pathlen + 1);
 			free(newbase);
-			if (retval)
+			if (retval) {
+				free(buffer0);
 				return -1;
+			}
 			continue;
 		}
-		if (read_one_entry(sha1, base, baselen, path, mode) < 0)
+		if (read_one_entry(sha1, base, baselen, path, mode) < 0) {
+			free(buffer0);
 			return -1;
+		}
 	}
+	free(buffer0);
+
 	return 0;
 }
 

^ permalink raw reply

* [patch] git: cleanup in ls-tree.c
From: Ingo Molnar @ 2005-04-14 11:43 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414113527.GA13790@elte.hu>


cleanup: this patch adds a free() to ls-tree.c.

(Technically it's not a memory leak yet because the buffer is allocated 
once by the function and then the utility exits - but it's a tad cleaner 
to not leave such assumptions in the code, so that if someone reuses the 
function (or extends the utility to include a loop) the uncleanliness 
doesnt develop into a real memory leak.)

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- ls-tree.c.orig
+++ ls-tree.c
@@ -33,6 +33,8 @@ static int list(unsigned char *sha1)
 		type = S_ISDIR(mode) ? "tree" : "blob";
 		printf("%03o\t%s\t%s\t%s\n", mode, type, sha1_to_hex(sha1), path);
 	}
+	free(buffer);
+
 	return 0;
 }
 

^ permalink raw reply

* [patch] git: fix memory leak #2 in checkout-cache.c
From: Ingo Molnar @ 2005-04-14 11:35 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds
In-Reply-To: <20050414112638.GA12593@elte.hu>


this patch fixes another (very rare) memory leak in checkout-cache.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- checkout-cache.c.orig
+++ checkout-cache.c
@@ -74,6 +74,8 @@ static int write_entry(struct cache_entr
 
 	new = read_sha1_file(ce->sha1, type, &size);
 	if (!new || strcmp(type, "blob")) {
+		if (new)
+			free(new);
 		return error("checkout-cache: unable to read sha1 file of %s (%s)",
 			ce->name, sha1_to_hex(ce->sha1));
 	}

^ permalink raw reply

* [patch] git: fix memory leak in checkout-cache.c
From: Ingo Molnar @ 2005-04-14 11:26 UTC (permalink / raw)
  To: git; +Cc: Linus Torvalds


this patch fixes a memory leak in checkout-cache.

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

--- checkout-cache.c.orig
+++ checkout-cache.c
@@ -48,6 +48,7 @@ static void create_directories(const cha
 		buf[len] = 0;
 		mkdir(buf, 0755);
 	}
+	free(buf);
 }
 
 static int create_file(const char *path, unsigned int mode)

^ permalink raw reply

* Re: Merge with git-pasky II.
From: Junio C Hamano @ 2005-04-14 11:14 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.58.0504140201130.7211@ppc970.osdl.org>

Here is a diff to update the git-merge.perl script I showed you
earlier today ;-).  It contains the following updates against
your HEAD (bb95843a5a0f397270819462812735ee29796fb4).

 * git-merge.perl command we talked about on the git list.  I've
   covered the changed-to-the-same case etc.  I still haven't done
   anything about file-vs-directory case yet.

   It does warn when it needed to run merge to automerge and let
   merge give a warning message about conflicts if any.  In
   modify/remove cases, modified in one but removed in the other
   files are left in either $path~A~ or $path~B~ in the merge
   temporary directory, and the script issues a warning at the
   end.

 * show-files and ls-tree updates to add -z flag to NUL terminate records;
   this is needed for git-merge.perl to work.

 * show-diff updates to add -r flag to squelch diffs for files not in
   the working directory.  This is mainly useful when verifying the
   result of an automated merge.

 * update-cache updates to add "--cacheinfo mode sha1" flag to register
   a file that is not in the current working directory.  Needed for
   minimum-checkout merging by git-merge.perl.


Signed-off-by: Junio C Hamano <junkio@cox.net>

---

 git-merge.perl |  247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ls-tree.c      |    9 +-
 show-diff.c    |   11 +-
 show-files.c   |   12 ++
 update-cache.c |   25 +++++
 5 files changed, 296 insertions(+), 8 deletions(-)

diff -x .git -Nru ,,1/git-merge.perl ,,2/git-merge.perl
--- ,,1/git-merge.perl	1969-12-31 16:00:00.000000000 -0800
+++ ,,2/git-merge.perl	2005-04-14 04:00:14.000000000 -0700
@@ -0,0 +1,247 @@
+#!/usr/bin/perl -w
+
+use Getopt::Long;
+
+my $full_checkout = 0;
+my $oneside_checkout = 0;
+GetOptions("full" => \$full_checkout,
+	   "oneside" => \$oneside_checkout)
+    or die;
+
+if ($full_checkout) {
+    $oneside_checkout = 1;
+}
+
+sub read_rev_tree {
+    my (@head) = @_;
+    my ($fhi);
+    open $fhi, '-|', 'rev-tree', '--edges', @head
+	or die "$!: rev-tree --edges @head";
+    my %common;
+    while (<$fhi>) {
+	chomp;
+	(undef, undef, my @common) = split(/ /, $_);
+	for (@common) {
+	    if (s/^([a-f0-f]{40}):3$/$1/) {
+		$common{$_}++;
+	    }
+	}
+    }
+    close $fhi;
+
+    my @common = (map { $_->[1] }
+		  sort { $b->[0] <=> $a->[0] }
+		  map { [ $common{$_} => $_ ] }
+		  keys %common);
+
+    return $common[0];
+}
+
+sub read_commit_tree {
+    my ($commit) = @_;
+    my ($fhi);
+    open $fhi, '-|', 'cat-file', 'commit', $commit
+	or die "$!: cat-file commit $commit";
+    my $tree = <$fhi>;
+    close $fhi;
+    $tree =~ s/^tree //;
+    return $tree;
+}
+
+# Reads diff-tree -r output and gives a hash that maps a path
+# to 3-tuple (old-mode new-mode new-sha).
+# When creating, old-mode is undef.  When removing, new-* are undef.
+sub read_diff_tree {
+    my (@tree) = @_;
+    my ($fhi);
+    local ($_, $/);
+    $/ = "\0"; 
+    my %path;
+    open $fhi, '-|', 'diff-tree', '-r', @tree
+	or die "$!: diff-tree -r @tree";
+    while (<$fhi>) {
+	chomp;
+	if (/^\*([0-7]+)->([0-7]+)\tblob\t[0-9a-f]+->([0-9a-f]{40})\t(.*)$/s) {
+	    $path{$4} = [$1, $2, $3];
+	}
+	elsif (/^\+([0-7]+)\tblob\t([0-9a-f]{40})\t(.*)$/s) {
+	    $path{$3} = [undef, $1, $2];
+	}
+	elsif (/^\-([0-7]+)\tblob\t[0-9a-f]{40}\t(.*)$/s) {
+	    $path{$2} = [$1, undef, undef];
+	}
+	else {
+	    die "cannot parse diff-tree output: $_";
+	}
+    }
+    close $fhi;
+    return %path;
+}
+
+sub read_show_files {
+    my ($fhi);
+    local ($_, $/);
+    $/ = "\0"; 
+    open $fhi, '-|', 'show-files', '-z'
+	or die "$!: show-files -z";
+    my (@path) = map { chomp; $_ } <$fhi>;
+    close $fhi;
+    return @path;
+}
+
+sub checkout_file {
+    my ($path, $info) = @_;
+    my (@elt) = split(/\//, $path);
+    my $j = '';
+    my $tail = pop @elt;
+    my ($fhi, $fho);
+    for (@elt) {
+	mkdir "$j$_";
+	$j = "$j$_/";
+    }
+    open $fho, '>', "$path";
+    open $fhi, '-|', 'cat-file', 'blob', $info->[2]
+	or die "$!: cat-file blob $info->[2]";
+    while (<$fhi>) {
+	print $fho $_;
+    }
+    close $fhi;
+    close $fho;
+    chmod oct("0$info->[1]"), "$path";
+}
+
+sub record_file {
+    my ($path, $info) = @_;
+    system ('update-cache', '--add', '--cacheinfo',
+	    $info->[1], $info->[2], $path);
+}
+
+sub merge_tree {
+    my ($path, $info0, $info1) = @_;
+    checkout_file(',,merge-0', $info0);
+    checkout_file(',,merge-1', $info1);
+    system 'checkout-cache', $path;
+    my ($fhi, $fho);
+    open $fhi, '-|', 'merge', '-p', ',,merge-0', $path, ',,merge-1';
+    open $fho, '>', "$path+";
+    local ($/);
+    while (<$fhi>) { print $fho $_; }
+    close $fhi;
+    close $fho;
+    unlink ',,merge-0', ',,merge-1';
+    rename "$path+", $path;
+    # There is no reason to prefer info0 over info1 but
+    # we need to pick one.
+    chmod oct("0$info0->[1]"), "$path";
+}
+
+# Find common ancestor of two trees.
+my $common = read_rev_tree(@ARGV);
+print "Common ancestor: $common\n";
+
+# Create a temporary directory and go there.
+system 'rm', '-rf', ',,merge-temp';
+for ((',,merge-temp', '.git')) { mkdir $_; chdir $_; }
+symlink "../../.git/objects", "objects";
+chdir '..';
+
+my $ancestor_tree = read_commit_tree($common);
+system 'read-tree', $ancestor_tree;
+
+my %tree0 = read_diff_tree($ancestor_tree, read_commit_tree($ARGV[0]));
+my %tree1 = read_diff_tree($ancestor_tree, read_commit_tree($ARGV[1]));
+
+my @ancestor_file = read_show_files();
+my %ancestor_file = map { $_ => 1 } @ancestor_file;
+
+for (@ancestor_file) {
+    if (! exists $tree0{$_} && ! exists $tree1{$_}) {
+	if ($full_checkout) {
+	    system 'checkout-cache', $_;
+	}
+	print STDERR "O - $_\n";
+    }
+}
+
+for my $set ([\%tree0, \%tree1, 'A'], [\%tree1, \%tree0, 'B']) {
+    my ($treeA, $treeB, $side) = @$set;
+    while (my ($path, $info) = each %$treeA) {
+	# In this loop we do not deal with overlaps.
+	next if (exists $treeB->{$path});
+
+	if (! defined $info->[1]) {
+	    # deleted in this tree only.
+	    unlink $path;
+	    system 'update-cache', '--remove', $path;
+	    print STDERR "$side D $path\n";
+	}
+	else {
+	    # modified or created in this tree only.
+	    print STDERR "$side M $path\n";
+	    if ($oneside_checkout) {
+		checkout_file($path, $info);
+		system 'update-cache', '--add', "$path";
+	    } else {
+		record_file($path, $info);
+	    }
+	}
+    }
+}
+
+my @warning = ();
+
+while (my ($path, $info0) = each %tree0) {
+    # We need to deal only with overlaps.
+    next if (!exists $tree1{$path});
+
+    my $info1 = $tree1{$path};
+    if (! defined $info0->[1]) {
+	# deleted in this tree.
+	if (! defined $info1->[1]) {
+	    # deleted in both trees.  Obvious.
+	    print STDERR "*DD $path\n";
+	    unlink $path;
+	    system 'update-cache', '--remove', $path;
+	}
+	else {
+	    # oops.  tree0 wants to remove but tree1 wants to modify it.
+	    print STDERR "*DM $path\n";
+	    checkout_file("$path~B~", $info1);
+	    push @warning, $path;
+	}
+    }
+    else {
+	# modified or created in tree0
+	if (! defined $info1->[1]) {
+	    # oops.  tree0 wants to modify but tree1 wants to remove it.
+	    print STDERR "*MD $path\n";
+	    checkout_file("$path~A~", $info0);
+	    push @warning, $path;
+	}
+	else {
+	    # modified both in tree0 and tree1
+	    # are they modifying to the same contents?
+	    if ($info0->[2] eq $info1->[2]) {
+		# just mode changes (or no changes)
+		# we prefer tree0 over tree1 for no particular reason.
+		print STDERR "*MM $path\n";
+		record_file($path, $info0);
+	    }
+	    else {
+		# modified in both.  Needs merge.
+		print STDERR "MRG $path\n";
+		merge_tree($path, $info0, $info1);
+	    }
+	}
+    }
+}
+
+if (@warning) {
+    print "\nThere are some files that were deleted in one branch and\n"
+	. "modified in another.  Please examine them carefully:\n";
+    for (@warning) {
+	print "$_\n";
+    }
+}
+
+# system 'show-diff';


diff -x .git -Nru ,,1/ls-tree.c ,,2/ls-tree.c
--- ,,1/ls-tree.c	2005-04-14 03:47:18.000000000 -0700
+++ ,,2/ls-tree.c	2005-04-14 04:00:14.000000000 -0700
@@ -5,6 +5,8 @@
  */
 #include "cache.h"
 
+int line_termination = '\n';
+
 static int list(unsigned char *sha1)
 {
 	void *buffer;
@@ -31,7 +33,8 @@
 		 * It seems not worth it to read each file just to get this
 		 * and the file size. -- pasky@ucw.cz */
 		type = S_ISDIR(mode) ? "tree" : "blob";
-		printf("%03o\t%s\t%s\t%s\n", mode, type, sha1_to_hex(sha1), path);
+		printf("%03o\t%s\t%s\t%s%c", mode, type, sha1_to_hex(sha1),
+		       path, line_termination);
 	}
 	return 0;
 }
@@ -40,6 +43,10 @@
 {
 	unsigned char sha1[20];
 
+	if (argc == 3 && !strcmp(argv[1], "-z")) {
+	  line_termination = 0;
+	  argc--; argv++;
+	}
 	if (argc != 2)
 		usage("ls-tree <key>");
 	if (get_sha1_hex(argv[1], sha1) < 0)


diff -x .git -Nru ,,1/show-diff.c ,,2/show-diff.c
--- ,,1/show-diff.c	2005-04-14 03:47:18.000000000 -0700
+++ ,,2/show-diff.c	2005-04-14 04:00:14.000000000 -0700
@@ -58,15 +58,20 @@
 int main(int argc, char **argv)
 {
 	int silent = 0;
+	int silent_on_nonexisting_files = 0;
 	int entries = read_cache();
 	int i;
 
 	while (argc-- > 1) {
 		if (!strcmp(argv[1], "-s")) {
-			silent = 1;
+			silent_on_nonexisting_files = silent = 1;
 			continue;
 		}
-		usage("show-diff [-s]");
+		if (!strcmp(argv[1], "-r")) {
+			silent_on_nonexisting_files = 1;
+			continue;
+		}
+		usage("show-diff [-s] [-r]");
 	}
 
 	if (entries < 0) {
@@ -83,7 +88,7 @@
 
 		if (stat(ce->name, &st) < 0) {
 			printf("%s: %s\n", ce->name, strerror(errno));
-			if (errno == ENOENT && !silent)
+			if (errno == ENOENT && !silent_on_nonexisting_files)
 				show_diff_empty(ce);
 			continue;
 		}


diff -x .git -Nru ,,1/show-files.c ,,2/show-files.c
--- ,,1/show-files.c	2005-04-14 03:47:18.000000000 -0700
+++ ,,2/show-files.c	2005-04-14 04:00:14.000000000 -0700
@@ -14,6 +14,7 @@
 static int show_cached = 0;
 static int show_others = 0;
 static int show_ignored = 0;
+static int line_terminator = '\n';
 
 static const char **dir;
 static int nr_dir;
@@ -105,12 +106,12 @@
 	}
 	if (show_others) {
 		for (i = 0; i < nr_dir; i++)
-			printf("%s\n", dir[i]);
+			printf("%s%c", dir[i], line_terminator);
 	}
 	if (show_cached) {
 		for (i = 0; i < active_nr; i++) {
 			struct cache_entry *ce = active_cache[i];
-			printf("%s\n", ce->name);
+			printf("%s%c", ce->name, line_terminator);
 		}
 	}
 	if (show_deleted) {
@@ -119,7 +120,7 @@
 			struct stat st;
 			if (!stat(ce->name, &st))
 				continue;
-			printf("%s\n", ce->name);
+			printf("%s%c", ce->name, line_terminator);
 		}
 	}
 	if (show_ignored) {
@@ -134,6 +135,11 @@
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
+		if (!strcmp(arg, "-z")) {
+			line_terminator = 0;
+			continue;
+		}
+
 		if (!strcmp(arg, "--cached")) {
 			show_cached = 1;
 			continue;


diff -x .git -Nru ,,1/update-cache.c ,,2/update-cache.c
--- ,,1/update-cache.c	2005-04-14 03:47:18.000000000 -0700
+++ ,,2/update-cache.c	2005-04-14 04:00:14.000000000 -0700
@@ -250,6 +250,8 @@
 {
 	int i, newfd, entries;
 	int allow_options = 1;
+	const char *sha1_force = NULL;
+	const char *mode_force = NULL;
 
 	newfd = open(".git/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600);
 	if (newfd < 0)
@@ -282,14 +284,35 @@
 				refresh_cache();
 				continue;
 			}
+			if (!strcmp(path, "--cacheinfo")) {
+				mode_force = argv[++i];
+				sha1_force = argv[++i];
+				continue;
+			}
 			die("unknown option %s", path);
 		}
 		if (!verify_path(path)) {
 			fprintf(stderr, "Ignoring path %s\n", argv[i]);
 			continue;
 		}
-		if (add_file_to_cache(path))
+		if (sha1_force && mode_force) {
+			struct cache_entry *ce;
+			int namelen = strlen(path);
+			int mode;
+			int size = cache_entry_size(namelen);
+			sscanf(mode_force, "%o", &mode);
+			ce = malloc(size);
+			memset(ce, 0, size);
+			memcpy(ce->name, path, namelen);
+			ce->namelen = namelen;
+			ce->st_mode = mode;
+			get_sha1_hex(sha1_force, ce->sha1);
+
+			add_cache_entry(ce, 1);
+		}
+		else if (add_file_to_cache(path))
 			die("Unable to add %s to database", path);
+		mode_force = sha1_force = NULL;
 	}
 	if (write_cache(newfd, active_cache, active_nr) ||
 	    rename(".git/index.lock", ".git/index"))


^ permalink raw reply

* cache-cold repository performance
From: Ingo Molnar @ 2005-04-14 10:50 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Florian Weimer, H. Peter Anvin, git
In-Reply-To: <20050414070422.GA3226@elte.hu>


* Ingo Molnar <mingo@elte.hu> wrote:

> i'd be surprised if it was twice as fast - cache-cold linear checkouts 
> are _seek_ limited, and it doesnt matter whether after a 1-2 msec 
> track-to-track disk seek the DMA engine spends another 30 microseconds 
> DMA-ing 60K uncompressed data instead of 30K compressed... (there are 
> other factors, but this is the main thing.)

i've benchmarked cache-cold compressed vs. uncompressed performance, to 
shed some more light on the performance differences between flat and 
compressed repositories.

i did alot of testing, and i primarily concentrated on being able to 
_trust_ the benchmark results, not to generate some quick numbers. The 
major problem was that the timing of the reads associated with 'checking 
out a large tree' is very unstable, even on a completely isolated 
testsystem with very common (and predictable) IO hardware.

the content i tested was a vanilla 2.6.10 kernel tree, with 19042 files 
in it, taking 246 MB uncompressed, and 110 MB compressed (via gzip -9).  
Average file size is 13.2 KB uncompressed, 5.9 KB compressed.

Firstly, the timings are very sensitive to the way the tree was created.  
To have a 'fair' on-disk layout the trees have to be created in an 
identical fashion: e.g. it is not valid to copy the uncompressed tree 
and run gzip over it - that will create a 'sparse' on-disk layout 
penalizing the compressed layout and making it 30% slower than the 
uncompressed layout! I first created the two trees, then i "cp -a"-ed 
them over into a new directory one after each other, so that they get on 
similar on-disk positions as well. I also created 2 more pairs of such 
trees to make sure disk layout is fair.

all timings were taken fresh after reboot, on a UP 1 GB RAM Athlon64 
3200+, using a large, top of the line IDE disk. The kernel was 
2.6.12-rc2, the filesystem was ext3 with enough free space to not be 
fragmented, both noatime and nodiratime was specified so that no write 
activities whatever occur during the 'checkout'.

the operation timed was a simple:

        time find . -type f | xargs cat > /dev/null

done in the root of the given tree. This generates the very same 
readonly IO pattern for each test. I've run the tests 10 times (i.e.  
have done 10 fresh reboots), but after every reboot i permutated the 
order of trees tested - to make sure there is no interaction between 
trees. (there was no interaction)

here are the raw numbers, elapsed real time in seconds:

 flat-1:  29.7 29.5 29.4 29.4 29.5 29.5 29.7 29.6 29.4 29.6 29.5 29.4:  29.5
 gzip-1:  41.2 40.9 40.7 40.7 40.5 41.7 41.0 40.3 40.6 40.8 40.8 40.9:  40.8

 flat-2:  28.0 28.2 27.7 27.9 27.8 27.9 27.7 27.9 27.9 28.1 27.9 28.0:  27.9
 gzip-2:  27.2 27.4 27.4 27.2 27.2 27.2 27.2 27.2 27.1 27.3 27.2 27.4:  27.2
 flat-3:  27.0 27.8 27.6 27.7 27.8 27.8 27.8 27.7 27.8 27.6 27.8 27.8:  27.6
 gzip-3:  25.8 26.8 26.6 26.5 26.5 26.5 26.6 26.4 26.5 26.7 26.6 26.7:  26.5

The final column is the average. (Standard deviation is below 0.1 sec, 
less than 0.3%.)

flat-1 is the original tree, created via tar. gzip-1 is a cp -a copy of 
it, per-file compressed afterwards. flat-2 is a cp -a copy of flat-1, 
gzip-2 is a cp -a copy of gzip-1. flat-3/gzip-3 are cp -a copies of 
flat-2/gzip-2.

note that gzip-1 is ~40% slower due to the 'sparse layout', so its 
results approximate a repository with 'bad' file layout. I'd not expect 
GIT repositories to have such a layout normally, so we can disregard it.

flat-2/3 and gzip-2/3 can be directly compared. Firstly, the results 
show that the on-disk layout cannot be constructed reliably - there's a 
1% systematic difference between flat-2 and flat-3, and a 3% systematic 
difference between gzip-2 and gzip-3 - both systematic errors are larger 
than the 0.5% standard deviation, so they are not measurement errors but 
real layout properties of these trees.

the most interesting result is that gzip-2 is 2.5% faster than flat-2, 
and gzip-3 is 4% faster than flat-3. These differences are close to the 
layout-related systematic error, but slightly above it, so i'd conclude 
that a compressed repository is 3% faster on this hardware.

(since these results were in line with my expectations i double-checked 
everything again and did another 10 reboot tests - same results.)

conclusion [*]: there's a negligible cache-cold performance hit from 
using an uncompressed repository, because cache-cold performance is 
dominated by number of seeks, which is almost identical in the two 
cases.

	Ingo

[*] lots of conditionals apply: these werent flat/compressed GIT 
repositories (although they were quite similar to it), nor was the GIT 
workload measured (although the one measured should be quite close to 
it).


^ permalink raw reply

* Re: [ANNOUNCE] git-pasky-0.4
From: Russell King @ 2005-04-14 10:33 UTC (permalink / raw)
  To: Petr Baudis; +Cc: git
In-Reply-To: <20050414001938.GR25711@pasky.ji.cz>

On Thu, Apr 14, 2005 at 02:19:38AM +0200, Petr Baudis wrote:
>   I'm happy to announce git-pasky-0.4, my set of scripts upon Linus
> Torvald's git, which aims to provide a humanly usable interface, to a
> degree similar to a SCM tool.

Here's my updated patch, against latest git-pasky.  Now using
hton*/ntoh* directly rather than wrapping them.  Enjoy.

--- cache.h
+++ cache.h	Thu Apr 14 11:27:22 2005
@@ -14,6 +14,8 @@
 #include <openssl/sha.h>
 #include <zlib.h>
 
+#include <netinet/in.h>
+
 /*
  * Basic data structures for the directory cache
  *
@@ -67,7 +69,7 @@
 #define DEFAULT_DB_ENVIRONMENT ".git/objects"
 
 #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
-#define ce_size(ce) cache_entry_size((ce)->namelen)
+#define ce_size(ce) cache_entry_size(ntohs((ce)->namelen))
 
 #define alloc_nr(x) (((x)+16)*3/2)
 
--- checkout-cache.c
+++ checkout-cache.c	Thu Apr 14 11:25:40 2005
@@ -77,7 +77,7 @@
 		return error("checkout-cache: unable to read sha1 file of %s (%s)",
 			ce->name, sha1_to_hex(ce->sha1));
 	}
-	fd = create_file(ce->name, ce->st_mode);
+	fd = create_file(ce->name, ntohl(ce->st_mode));
 	if (fd < 0) {
 		free(new);
 		return error("checkout-cache: unable to create %s (%s)",
--- read-cache.c
+++ read-cache.c	Thu Apr 14 11:25:40 2005
@@ -288,27 +288,34 @@
 	/* nsec seems unreliable - not all filesystems support it, so
 	 * as long as it is in the inode cache you get right nsec
 	 * but after it gets flushed, you get zero nsec. */
-	if (ce->mtime.sec  != (unsigned int)st->st_mtim.tv_sec
+#if 0
+	if (ntohl(ce->mtime.sec)  != (unsigned int)st->st_mtim.tv_sec
 #ifdef NSEC
-	    || ce->mtime.nsec != (unsigned int)st->st_mtim.tv_nsec
+	    || ntohl(ce->mtime.nsec) != (unsigned int)st->st_mtim.tv_nsec
 #endif
 	    )
 		changed |= MTIME_CHANGED;
-	if (ce->ctime.sec  != (unsigned int)st->st_ctim.tv_sec
+	if (ntohl(ce->ctime.sec)  != (unsigned int)st->st_ctim.tv_sec
 #ifdef NSEC
-	    || ce->ctime.nsec != (unsigned int)st->st_ctim.tv_nsec
+	    || ntohl(ce->ctime.nsec) != (unsigned int)st->st_ctim.tv_nsec
 #endif
 	    )
 		changed |= CTIME_CHANGED;
-	if (ce->st_uid != (unsigned int)st->st_uid ||
-	    ce->st_gid != (unsigned int)st->st_gid)
+#else
+	if (ntohl(ce->mtime.sec)  != (unsigned int)st->st_mtime)
+		changed |= MTIME_CHANGED;
+	if (ntohl(ce->ctime.sec)  != (unsigned int)st->st_ctime)
+		changed |= CTIME_CHANGED;
+#endif
+	if (ntohl(ce->st_uid) != (unsigned int)st->st_uid ||
+	    ntohl(ce->st_gid) != (unsigned int)st->st_gid)
 		changed |= OWNER_CHANGED;
-	if (ce->st_mode != (unsigned int)st->st_mode)
+	if (ntohl(ce->st_mode) != (unsigned int)st->st_mode)
 		changed |= MODE_CHANGED;
-	if (ce->st_dev != (unsigned int)st->st_dev ||
-	    ce->st_ino != (unsigned int)st->st_ino)
+	if (ntohl(ce->st_dev) != (unsigned int)st->st_dev ||
+	    ntohl(ce->st_ino) != (unsigned int)st->st_ino)
 		changed |= INODE_CHANGED;
-	if (ce->st_size != (unsigned int)st->st_size)
+	if (ntohl(ce->st_size) != (unsigned int)st->st_size)
 		changed |= DATA_CHANGED;
 	return changed;
 }
@@ -337,7 +344,7 @@
 	while (last > first) {
 		int next = (last + first) >> 1;
 		struct cache_entry *ce = active_cache[next];
-		int cmp = cache_name_compare(name, namelen, ce->name, ce->namelen);
+		int cmp = cache_name_compare(name, namelen, ce->name, ntohs(ce->namelen));
 		if (!cmp)
 			return next;
 		if (cmp < 0) {
@@ -364,7 +371,7 @@
 {
 	int pos;
 
-	pos = cache_name_pos(ce->name, ce->namelen);
+	pos = cache_name_pos(ce->name, ntohs(ce->namelen));
 
 	/* existing match? Just replace it */
 	if (pos >= 0) {
@@ -395,9 +402,9 @@
 	SHA_CTX c;
 	unsigned char sha1[20];
 
-	if (hdr->signature != CACHE_SIGNATURE)
+	if (hdr->signature != htonl(CACHE_SIGNATURE))
 		return error("bad signature");
-	if (hdr->version != 1)
+	if (hdr->version != htonl(1))
 		return error("bad version");
 	SHA1_Init(&c);
 	SHA1_Update(&c, hdr, offsetof(struct cache_header, sha1));
@@ -445,12 +452,12 @@
 	if (verify_hdr(hdr, size) < 0)
 		goto unmap;
 
-	active_nr = hdr->entries;
+	active_nr = ntohl(hdr->entries);
 	active_alloc = alloc_nr(active_nr);
 	active_cache = calloc(active_alloc, sizeof(struct cache_entry *));
 
 	offset = sizeof(*hdr);
-	for (i = 0; i < hdr->entries; i++) {
+	for (i = 0; i < ntohl(hdr->entries); i++) {
 		struct cache_entry *ce = map + offset;
 		offset = offset + ce_size(ce);
 		active_cache[i] = ce;
@@ -469,9 +476,9 @@
 	struct cache_header hdr;
 	int i;
 
-	hdr.signature = CACHE_SIGNATURE;
-	hdr.version = 1;
-	hdr.entries = entries;
+	hdr.signature = htonl(CACHE_SIGNATURE);
+	hdr.version = htonl(1);
+	hdr.entries = htonl(entries);
 
 	SHA1_Init(&c);
 	SHA1_Update(&c, &hdr, offsetof(struct cache_header, sha1));
--- read-tree.c
+++ read-tree.c	Thu Apr 14 11:25:11 2005
@@ -13,8 +13,8 @@
 
 	memset(ce, 0, size);
 
-	ce->st_mode = mode;
-	ce->namelen = baselen + len;
+	ce->st_mode = htonl(mode);
+	ce->namelen = htons(baselen + len);
 	memcpy(ce->name, base, baselen);
 	memcpy(ce->name + baselen, pathname, len+1);
 	memcpy(ce->sha1, sha1, 20);
--- show-diff.c
+++ show-diff.c	Thu Apr 14 11:25:28 2005
@@ -90,7 +90,7 @@
 		changed = cache_match_stat(ce, &st);
 		if (!changed)
 			continue;
-		printf("%.*s:  ", ce->namelen, ce->name);
+		printf("%.*s:  ", ntohs(ce->namelen), ce->name);
 		for (n = 0; n < 20; n++)
 			printf("%02x", ce->sha1[n]);
 		printf("\n");
--- update-cache.c
+++ update-cache.c	Thu Apr 14 11:30:07 2005
@@ -68,18 +68,18 @@
  */
 static void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 {
-	ce->ctime.sec = st->st_ctime;
+	ce->ctime.sec = htonl(st->st_ctime);
 #ifdef NSEC
-	ce->ctime.nsec = st->st_ctim.tv_nsec;
+	ce->ctime.nsec = htonl(st->st_ctim.tv_nsec);
 #endif
-	ce->mtime.sec = st->st_mtime;
+	ce->mtime.sec = htonl(st->st_mtime);
 #ifdef NSEC
-	ce->mtime.nsec = st->st_mtim.tv_nsec;
+	ce->mtime.nsec = htonl(st->st_mtim.tv_nsec);
 #endif
-	ce->st_dev = st->st_dev;
-	ce->st_ino = st->st_ino;
-	ce->st_uid = st->st_uid;
-	ce->st_gid = st->st_gid;
+	ce->st_dev = htonl(st->st_dev);
+	ce->st_ino = htonl(st->st_ino);
+	ce->st_uid = htonl(st->st_uid);
+	ce->st_gid = htonl(st->st_gid);
 }
 
 static int add_file_to_cache(char *path)
@@ -107,9 +107,9 @@
 	memset(ce, 0, size);
 	memcpy(ce->name, path, namelen);
 	fill_stat_cache_info(ce, &st);
-	ce->st_mode = st.st_mode;
-	ce->st_size = st.st_size;
-	ce->namelen = namelen;
+	ce->st_mode = htonl(st.st_mode);
+	ce->st_size = htonl(st.st_size);
+	ce->namelen = htons(namelen);
 
 	if (index_fd(path, namelen, ce, fd, &st) < 0)
 		return -1;
@@ -190,7 +190,7 @@
 	updated = malloc(size);
 	memcpy(updated, ce, size);
 	fill_stat_cache_info(updated, &st);
-	updated->st_size = st.st_size;
+	updated->st_size = htonl(st.st_size);
 	return updated;
 }
 
--- write-tree.c
+++ write-tree.c	Thu Apr 14 11:25:40 2005
@@ -45,7 +45,7 @@
 	do {
 		struct cache_entry *ce = cachep[nr];
 		const char *pathname = ce->name, *filename, *dirname;
-		int pathlen = ce->namelen, entrylen;
+		int pathlen = ntohs(ce->namelen), entrylen;
 		unsigned char *sha1;
 		unsigned int mode;
 
@@ -54,7 +54,7 @@
 			break;
 
 		sha1 = ce->sha1;
-		mode = ce->st_mode;
+		mode = ntohl(ce->st_mode);
 
 		/* Do we have _further_ subdirectories? */
 		filename = pathname + baselen;

-- 
Russell King


^ permalink raw reply

* Re: Re: Re: Re: Remove need to untrack before tracking new branch
From: Martin Schlemmer @ 2005-04-14  9:55 UTC (permalink / raw)
  To: Petr Baudis; +Cc: git
In-Reply-To: <1113471609.23299.95.camel@nosferatu.lan>


[-- Attachment #1.1: Type: text/plain, Size: 5242 bytes --]

On Thu, 2005-04-14 at 11:40 +0200, Martin Schlemmer wrote:
> On Thu, 2005-04-14 at 11:11 +0200, Petr Baudis wrote:
> > Please trim the replied mails a bit, snipping old and irrelevant parts.
> > This is insane. :-)
> > 
> > Dear diary, on Thu, Apr 14, 2005 at 10:38:25AM CEST, I got a letter
> > where Martin Schlemmer <azarah@nosferatu.za.org> told me that...
> > ..snip..
> > > Normalize show-diff output, add --update-modes target to update-cache,
> > > and make sure we only show real changes after changing the tracked
> > > branch, as well as update the file modes according to the cache.
> > 
> > I'm lost. Why do you do --update-modes? That makes no sense to me.
> > You introduce them to the cache out-of-order w.r.t. commits, that means
> > in the normal git usage they are already unrevertable.
> > 
> 
> Right, afterwards I thought I did add it to the wrong place.
> 
> 
> > What are you trying to do? Mode changes _are_ real changes. You _don't_
> > want to silence them. What you want is to even show them more explicitly
> > in show-diff.
> > 
> 
> No, you do not understand.  If you actually change the mode, it will
> show.  What now happens, is that say I track the 'linus' branch, then
> untrack, and then track 'pasky' again, the Patches will be applied, but
> not the mode changes which are stored in the cache ...
>
> So basically the modes that are stored in the cache are not applied ...
> Although, yes, I prob should add the relevant code to checkout-cache.
> 

Ok, this should be a better one I think.

----

Normalize show-diff output, add --update-modes target to update-cache,
and make sure we only show real changes after changing the tracked
branch, as well as update the file modes according to the cache.

Signed-off-by: Martin Schlemmer <azarah@nosferatu.za.org>

checkout-cache.c:  f06871cdbc1b18ea93bdf4e17126aeb4cca1373e
--- f06871cdbc1b18ea93bdf4e17126aeb4cca1373e/checkout-cache.c
+++ uncommitted/checkout-cache.c
@@ -34,7 +34,7 @@
  */
 #include "cache.h"

-static int force = 0, quiet = 0;
+static int force = 0, quiet = 0, update_mode = 0;

 static void create_directories(const char *path)
 {
@@ -99,6 +99,8 @@
                unsigned changed = cache_match_stat(ce, &st);
                if (!changed)
                        return 0;
+               if (update_mode && changed & MODE_CHANGED)
+                       chmod(ce->name, ce->st_mode);
                if (!force) {
                        if (!quiet)
                                fprintf(stderr, "checkout-cache: %s already exists\n", ce->name);
@@ -158,6 +160,10 @@
                                quiet = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "-m")) {
+                               update_mode = 1;
+                               continue;
+                       }
                }
                checkout_file(arg);
        }
gitcancel.sh:  ec58f7444a42cd3cbaae919fc68c70a3866420c0
--- ec58f7444a42cd3cbaae919fc68c70a3866420c0/gitcancel.sh
+++ uncommitted/gitcancel.sh
@@ -12,7 +12,8 @@

 # FIXME: Does not revert mode changes!

-show-diff | patch -p0 -R
+show-diff | patch -p1 -R
 rm -f .git/add-queue .git/rm-queue .git/merged

+checkout-cache -q -m -a
 update-cache --refresh
gittrack.sh:  03d6db1fb3a70605ef249c632c04e542457f0808
--- 03d6db1fb3a70605ef249c632c04e542457f0808/gittrack.sh
+++ uncommitted/gittrack.sh
@@ -51,6 +51,8 @@

        read-tree $(tree-id "$name")
        gitdiff.sh local "$name" | gitapply.sh
+       checkout-cache -q -m -a
+       update-cache --refresh

 else
        [ "$tracking" ] || \
@@ -61,6 +63,8 @@
        if [ -s ".git/HEAD.local" ]; then
                gitdiff.sh "$tracking" local | gitapply.sh
                read-tree $(tree-id local)
+               checkout-cache -q -m -a
+               update-cache --refresh

                head=$(cat .git/HEAD)
                branchhead=$(cat .git/heads/$tracking)
show-diff.c:  a531ca4078525d1c8dcf84aae0bfa89fed6e5d96
--- a531ca4078525d1c8dcf84aae0bfa89fed6e5d96/show-diff.c
+++ uncommitted/show-diff.c
@@ -5,13 +5,18 @@
  */
 #include "cache.h"

-static void show_differences(char *name,
+static void show_differences(struct cache_entry *ce,
        void *old_contents, unsigned long long old_size)
 {
        static char cmd[1000];
+       static char sha1[41];
+       int n;
        FILE *f;

-       snprintf(cmd, sizeof(cmd), "diff -L %s -u -N  - %s", name, name);
+       for (n = 0; n < 20; n++)
+               snprintf(&(sha1[n*2]), 3, "%02x", ce->sha1[n]);
+       snprintf(cmd, sizeof(cmd), "diff -L %s/%s -L uncommitted/%s -u -N  - %s",
+               sha1, ce->name, ce->name, ce->name);
        f = popen(cmd, "w");
        if (old_size)
                fwrite(old_contents, old_size, 1, f);
@@ -99,7 +104,7 @@
                        continue;

                new = read_sha1_file(ce->sha1, type, &size);
-               show_differences(ce->name, new, size);
+               show_differences(ce, new, size);
                free(new);
        }
        return 0;


-- 
Martin Schlemmer


[-- Attachment #1.2: git-show-diff.patch --]
[-- Type: text/x-patch, Size: 2745 bytes --]

checkout-cache.c:  f06871cdbc1b18ea93bdf4e17126aeb4cca1373e
--- f06871cdbc1b18ea93bdf4e17126aeb4cca1373e/checkout-cache.c
+++ uncommitted/checkout-cache.c
@@ -34,7 +34,7 @@
  */
 #include "cache.h"
 
-static int force = 0, quiet = 0;
+static int force = 0, quiet = 0, update_mode = 0;
 
 static void create_directories(const char *path)
 {
@@ -99,6 +99,8 @@
 		unsigned changed = cache_match_stat(ce, &st);
 		if (!changed)
 			return 0;
+		if (update_mode && changed & MODE_CHANGED)
+			chmod(ce->name, ce->st_mode);
 		if (!force) {
 			if (!quiet)
 				fprintf(stderr, "checkout-cache: %s already exists\n", ce->name);
@@ -158,6 +160,10 @@
 				quiet = 1;
 				continue;
 			}
+			if (!strcmp(arg, "-m")) {
+				update_mode = 1;
+				continue;
+			}
 		}
 		checkout_file(arg);
 	}
gitcancel.sh:  ec58f7444a42cd3cbaae919fc68c70a3866420c0
--- ec58f7444a42cd3cbaae919fc68c70a3866420c0/gitcancel.sh
+++ uncommitted/gitcancel.sh
@@ -12,7 +12,8 @@
 
 # FIXME: Does not revert mode changes!
 
-show-diff | patch -p0 -R
+show-diff | patch -p1 -R
 rm -f .git/add-queue .git/rm-queue .git/merged
 
+checkout-cache -q -m -a
 update-cache --refresh
gittrack.sh:  03d6db1fb3a70605ef249c632c04e542457f0808
--- 03d6db1fb3a70605ef249c632c04e542457f0808/gittrack.sh
+++ uncommitted/gittrack.sh
@@ -51,6 +51,8 @@
 
 	read-tree $(tree-id "$name")
 	gitdiff.sh local "$name" | gitapply.sh
+	checkout-cache -q -m -a
+	update-cache --refresh
 
 else
 	[ "$tracking" ] || \
@@ -61,6 +63,8 @@
 	if [ -s ".git/HEAD.local" ]; then
 		gitdiff.sh "$tracking" local | gitapply.sh
 		read-tree $(tree-id local)
+		checkout-cache -q -m -a
+		update-cache --refresh
 
 		head=$(cat .git/HEAD)
 		branchhead=$(cat .git/heads/$tracking)
show-diff.c:  a531ca4078525d1c8dcf84aae0bfa89fed6e5d96
--- a531ca4078525d1c8dcf84aae0bfa89fed6e5d96/show-diff.c
+++ uncommitted/show-diff.c
@@ -5,13 +5,18 @@
  */
 #include "cache.h"
 
-static void show_differences(char *name,
+static void show_differences(struct cache_entry *ce,
 	void *old_contents, unsigned long long old_size)
 {
 	static char cmd[1000];
+	static char sha1[41];
+	int n;
 	FILE *f;
 
-	snprintf(cmd, sizeof(cmd), "diff -L %s -u -N  - %s", name, name);
+	for (n = 0; n < 20; n++)
+		snprintf(&(sha1[n*2]), 3, "%02x", ce->sha1[n]);
+	snprintf(cmd, sizeof(cmd), "diff -L %s/%s -L uncommitted/%s -u -N  - %s",
+		sha1, ce->name, ce->name, ce->name);
 	f = popen(cmd, "w");
 	if (old_size)
 		fwrite(old_contents, old_size, 1, f);
@@ -99,7 +104,7 @@
 			continue;
 
 		new = read_sha1_file(ce->sha1, type, &size);
-		show_differences(ce->name, new, size);
+		show_differences(ce, new, size);
 		free(new);
 	}
 	return 0;

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: Re: Re: Re: Remove need to untrack before tracking new branch
From: Martin Schlemmer @ 2005-04-14  9:40 UTC (permalink / raw)
  To: Petr Baudis; +Cc: git
In-Reply-To: <20050414091106.GX25711@pasky.ji.cz>

[-- Attachment #1: Type: text/plain, Size: 7340 bytes --]

(PS, can you check the fact that your mail client keeps on adding a 'Re:
' ...)

On Thu, 2005-04-14 at 11:11 +0200, Petr Baudis wrote:
> Please trim the replied mails a bit, snipping old and irrelevant parts.
> This is insane. :-)
> 
> Dear diary, on Thu, Apr 14, 2005 at 10:38:25AM CEST, I got a letter
> where Martin Schlemmer <azarah@nosferatu.za.org> told me that...
> ..snip..
> > Normalize show-diff output, add --update-modes target to update-cache,
> > and make sure we only show real changes after changing the tracked
> > branch, as well as update the file modes according to the cache.
> 
> I'm lost. Why do you do --update-modes? That makes no sense to me.
> You introduce them to the cache out-of-order w.r.t. commits, that means
> in the normal git usage they are already unrevertable.
> 

Right, afterwards I thought I did add it to the wrong place.


> What are you trying to do? Mode changes _are_ real changes. You _don't_
> want to silence them. What you want is to even show them more explicitly
> in show-diff.
> 

No, you do not understand.  If you actually change the mode, it will
show.  What now happens, is that say I track the 'linus' branch, then
untrack, and then track 'pasky' again, the Patches will be applied, but
not the mode changes which are stored in the cache ...  Let me show you:

-----
$ ls -l $(./show-diff -s | cut -d: -f1)
-rw-r--r--  1 root root  168 Apr 14 11:33 commit-id
-rw-r--r--  1 root root 2213 Apr 14 11:33 git
-rw-r--r--  1 root root 1168 Apr 14 11:33 gitXnormid.sh
-rw-r--r--  1 root root  403 Apr 14 11:33 gitadd.sh
-rw-r--r--  1 root root  844 Apr 14 11:33 gitaddremote.sh
-rw-r--r--  1 root root 1899 Apr 14 11:33 gitapply.sh
-rw-r--r--  1 root root  479 Apr 14 11:33 gitcancel.sh
-rw-r--r--  1 root root 2512 Apr 14 11:33 gitcommit.sh
-rw-r--r--  1 root root 2152 Apr 14 11:33 gitdiff-do
-rw-r--r--  1 root root  819 Apr 14 11:33 gitdiff.sh
-rw-r--r--  1 root root  717 Apr 14 11:33 gitexport.sh
-rw-r--r--  1 root root  524 Apr 14 11:33 gitlog.sh
-rw-r--r--  1 root root  228 Apr 14 11:33 gitls.sh
-rw-r--r--  1 root root  904 Apr 14 11:33 gitlsobj.sh
-rw-r--r--  1 root root  665 Apr 14 11:33 gitmerge.sh
-rw-r--r--  1 root root 2044 Apr 14 11:33 gitpull.sh
-rw-r--r--  1 root root  433 Apr 14 11:33 gitrm.sh
-rw-r--r--  1 root root  614 Apr 14 11:33 gittag.sh
-rw-r--r--  1 root root 2272 Apr 14 11:33 gittrack.sh
-rw-r--r--  1 root root  284 Apr 14 11:33 parent-id
-rw-r--r--  1 root root  177 Apr 14 11:33 tree-id
-----

(Note no 'x' bit ...)

And that is _after_ doing:

 $ git track linus; git track

So basically the modes that are stored in the cache are not applied ...
Although, yes, I prob should add the relevant code to checkout-cache.

> The --refreshes are fine.
> 
> > Signed-off-by: Martin Schlemmer <azarah@nosferatu.za.org>
> > 
> > gitcancel.sh:  ec58f7444a42cd3cbaae919fc68c70a3866420c0
> > --- ec58f7444a42cd3cbaae919fc68c70a3866420c0/gitcancel.sh
> > +++ uncommitted/gitcancel.sh
> > @@ -12,7 +12,8 @@
> > 
> >  # FIXME: Does not revert mode changes!
> > 
> > -show-diff | patch -p0 -R
> > +show-diff | patch -p1 -R
> >  rm -f .git/add-queue .git/rm-queue .git/merged
> > 
> > -update-cache --refresh
> > +# --update-modes need to be before --refresh
> > +update-cache --update-modes --refresh
> 
> Here, e.g., you should do the very opposite - change the modes back to
> how are they in the cache.
> 

Uhm, that is what it did ...  Like I said above, the wrong place though
to add the code to update-cache.

> > show-diff.c:  a531ca4078525d1c8dcf84aae0bfa89fed6e5d96
> > --- a531ca4078525d1c8dcf84aae0bfa89fed6e5d96/show-diff.c
> > +++ uncommitted/show-diff.c
> > @@ -5,13 +5,18 @@
> >   */
> >  #include "cache.h"
> > 
> > -static void show_differences(char *name,
> > +static void show_differences(struct cache_entry *ce,
> >         void *old_contents, unsigned long long old_size)
> >  {
> >         static char cmd[1000];
> > +       static char sha1[41];
> > +       int n;
> >         FILE *f;
> > 
> > -       snprintf(cmd, sizeof(cmd), "diff -L %s -u -N  - %s", name, name);
> > +       for (n = 0; n < 20; n++)
> > +               snprintf(&(sha1[n*2]), 3, "%02x", ce->sha1[n]);
> > +       snprintf(cmd, sizeof(cmd), "diff -L %s/%s -L uncommitted/%s -u -N  - %s",
> > +               sha1, ce->name, ce->name, ce->name);
> 
> The "directory" sha1 is the sha1 of the tree, not of the particular
> file - that one is in the "attributes" list (parentheses after the
> filename), together with mode.
> 

Does it really matter?  It is more just to get the patch prefix right,
and I did it as it went nicely with the printed:

----
show-diff.c:  a531ca4078525d1c8dcf84aae0bfa89fed6e5d96
----

for example ...

> >         f = popen(cmd, "w");
> >         if (old_size)
> >                 fwrite(old_contents, old_size, 1, f);
> > @@ -99,7 +104,7 @@
> >                         continue;
> > 
> >                 new = read_sha1_file(ce->sha1, type, &size);
> > -               show_differences(ce->name, new, size);
> > +               show_differences(ce, new, size);
> >                 free(new);
> >         }
> >         return 0;
> > update-cache.c:  62d0a6c41560d40863c44599355af10d9e089312
> > --- 62d0a6c41560d40863c44599355af10d9e089312/update-cache.c
> > +++ uncommitted/update-cache.c
> > @@ -210,6 +210,39 @@
> >         }
> >  }
> > 
> > +static struct cache_entry *update_file_mode(struct cache_entry *ce)
> > +{
> > +       struct stat st;
> > +       int changed;
> > +
> > +       if (stat(ce->name, &st) < 0)
> > +               return NULL;
> > +
> > +       changed = cache_match_stat(ce, &st);
> > +       if (!changed)
> > +               return ce;
> > +
> > +       if (changed & MODE_CHANGED)
> > +               if (chmod(ce->name, ce->st_mode))
> > +                       return NULL;
> > +
> > +       return ce;
> > +}
> > +
> > +static void update_modes(void)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < active_nr; i++) {
> > +               struct cache_entry *ce = active_cache[i];
> > +
> > +               if (!update_file_mode(ce)) {
> > +                       printf("%s: needs update\n", ce->name);
> > +                       continue;
> > +               }
> > +       }
> > +}
> > +
> >  /*
> >   * We fundamentally don't like some paths: we don't want
> >   * dot or dot-dot anywhere, and in fact, we don't even want
> 
> Oh. So you are actually doing the _opposite_! But that's very wrong too.
> update-cache is for the direction tree -> index. For the opposite
> direction, you should use checkout-cache.
> 

Right, will do.

> > @@ -282,6 +315,10 @@
> >                                 refresh_cache();
> >                                 continue;
> >                         }
> > +                       if (!strcmp(path, "--update-modes")) {
> > +                               update_modes();
> > +                               continue;
> > +                       }
> >                         die("unknown option %s", path);
> >                 }
> >                 if (!verify_path(path)) {
> > 
> > 
> > -- 
> > Martin Schlemmer
> > 
> 


-- 
Martin Schlemmer


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox