Git development
 help / color / mirror / Atom feed
* [Patch] ls-tree enhancements
From: Junio C Hamano @ 2005-04-15  2:21 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Petr Baudis, git
In-Reply-To: <Pine.LNX.4.58.0504141133260.7211@ppc970.osdl.org>

This adds '-r' (recursive) option and '-z' (NUL terminated)
option to ls-tree.  I need it so that the merge-trees (formerly
known as git-merge.perl) script does not need to create any
temporary dircache while merging.  It used to use show-files on
a temporary dircache to get the list of files in the ancestor
tree, and also used the dircache to store the result of its
automerge.  I probably still need it for the latter reason, but
with this patch not for the former reason anymore.

It is relative to bb95843a5a0f397270819462812735ee29796fb4

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

---

 ls-tree.c |  108 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 90 insertions(+), 18 deletions(-)


--- ,,Linus/ls-tree.c	2005-04-14 19:08:17.000000000 -0700
+++ ,,Siam/ls-tree.c	2005-04-14 19:11:23.000000000 -0700
@@ -5,45 +5,117 @@
  */
 #include "cache.h"
 
-static int list(unsigned char *sha1)
+int line_termination = '\n';
+int recursive = 0;
+
+struct path_prefix {
+	struct path_prefix *prev;
+	const char *name;
+};
+
+static void print_path_prefix(struct path_prefix *prefix)
 {
-	void *buffer;
-	unsigned long size;
-	char type[20];
+	if (prefix) {
+		if (prefix->prev)
+			print_path_prefix(prefix->prev);
+		fputs(prefix->name, stdout);
+		putchar('/');
+	}
+}
+
+static void list_recursive(void *buffer,
+			  unsigned char *type,
+			  unsigned long size,
+			  struct path_prefix *prefix)
+{
+	struct path_prefix this_prefix;
+	this_prefix.prev = prefix;
 
-	buffer = read_sha1_file(sha1, type, &size);
-	if (!buffer)
-		die("unable to read sha1 file");
 	if (strcmp(type, "tree"))
 		die("expected a 'tree' node");
+
 	while (size) {
-		int len = strlen(buffer)+1;
-		unsigned char *sha1 = buffer + len;
-		char *path = strchr(buffer, ' ')+1;
+		int namelen = strlen(buffer)+1;
+		void *eltbuf;
+		char elttype[20];
+		unsigned long eltsize;
+		unsigned char *sha1 = buffer + namelen;
+		char *path = strchr(buffer, ' ') + 1;
 		unsigned int mode;
-		unsigned char *type;
 
-		if (size < len + 20 || sscanf(buffer, "%o", &mode) != 1)
+		if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
 			die("corrupt 'tree' file");
 		buffer = sha1 + 20;
-		size -= len + 20;
+		size -= namelen + 20;
+
 		/* XXX: We do some ugly mode heuristics here.
 		 * 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);
+		 * and the file size. -- pasky@ucw.cz
+		 * ... that is, when we are not recursive -- junkio@cox.net
+		 */
+		eltbuf = (recursive ? read_sha1_file(sha1, elttype, &eltsize) :
+			  NULL);
+		if (! eltbuf) {
+			if (recursive)
+				error("cannot read %s", sha1_to_hex(sha1));
+			type = S_ISDIR(mode) ? "tree" : "blob";
+		}
+		else
+			type = elttype;
+
+		printf("%03o\t%s\t%s\t", mode, type, sha1_to_hex(sha1));
+		print_path_prefix(prefix);
+		fputs(path, stdout);
+		putchar(line_termination);
+
+		if (eltbuf && !strcmp(type, "tree")) {
+			this_prefix.name = path;
+			list_recursive(eltbuf, elttype, eltsize, &this_prefix);
+		}
+		free(eltbuf);
 	}
+}
+
+static int list(unsigned char *sha1)
+{
+	void *buffer;
+	unsigned long size;
+	char type[20];
+
+	buffer = read_sha1_file(sha1, type, &size);
+	if (!buffer)
+		die("unable to read sha1 file");
+	list_recursive(buffer, type, size, NULL);
 	return 0;
 }
 
+static void _usage(void)
+{
+	usage("ls-tree [-r] [-z] <key>");
+}
+
 int main(int argc, char **argv)
 {
 	unsigned char sha1[20];
 
+	while (1 < argc && argv[1][0] == '-') {
+		switch (argv[1][1]) {
+		case 'z':
+			line_termination = 0;
+			break;
+		case 'r':
+			recursive = 1;
+			break;
+		default:
+			_usage();
+		}
+		argc--; argv++;
+	}
+
 	if (argc != 2)
-		usage("ls-tree <key>");
+		_usage();
 	if (get_sha1_hex(argv[1], sha1) < 0)
-		usage("ls-tree <key>");
+		_usage();
 	sha1_file_directory = getenv(DB_ENVIRONMENT);
 	if (!sha1_file_directory)
 		sha1_file_directory = DEFAULT_DB_ENVIRONMENT;


^ permalink raw reply

* RE: Merge with git-pasky II.
From: Barry Silverman @ 2005-04-15  2:33 UTC (permalink / raw)
  To: 'Linus Torvalds', 'Junio C Hamano'
  Cc: 'Petr Baudis', git
In-Reply-To: <Pine.LNX.4.58.0504141728590.7211@ppc970.osdl.org>

>>In particular, if you ever find yourself wanting to graft together two
>>different commit histories, that almost certainly is what you'd want
to >>do. Somebody might have arrived at the exact same tree some other
way, >>starting with a 2.6.12 tar.ball or something, and I think we
should at >>least support the notion of saying "these two totally
unrelated commits >>actually have the same base tree, so let's merge
them in "space" (ie data) >>even if we can't really sanely join them in
"time" (ie "commits").

If this is true - then the tree-id's of the two commits would be
identical, but the commit-id's wouldn't.

Does this imply that common ancestor lookup should work by comparing the
tree-id's (space-wise the same) rather than the commit-ids (time-wise
the same)?

-----Original Message-----
From: git-owner@vger.kernel.org [mailto:git-owner@vger.kernel.org] On
Behalf Of Linus Torvalds
Sent: Thursday, April 14, 2005 8:43 PM
To: Junio C Hamano
Cc: Petr Baudis; git@vger.kernel.org
Subject: Re: Merge with git-pasky II.



On Thu, 14 Apr 2005, Junio C Hamano wrote:
>
> You say "merge these two trees" above (I take it that you mean
> "merge these two trees, taking account of this tree as their
> common ancestor", so actually you are dealing with three trees),

Yes. We're definitely talking three trees.

> and I am tending to agree with the notion of merging trees not
> commits.  However you might get richer context and more sensible
> resulting merge if you say "merge these two commits".  Since
> commit chaining is part of the fundamental git object model you
> may as well use it.

Yes and no. There are real advantages to using the commit state to just 
figure out the trees, and then at least have the _option_ to do the
merge 
at a pure tree object.

In particular, if you ever find yourself wanting to graft together two
different commit histories, that almost certainly is what you'd want to
do. Somebody might have arrived at the exact same tree some other way,
starting with a 2.6.12 tar.ball or something, and I think we should at
least support the notion of saying "these two totally unrelated commits
actually have the same base tree, so let's merge them in "space" (ie
data)
even if we can't really sanely join them in "time" (ie "commits").

I dunno.

And it's also a question of sanity. The fact is, we know how to make
tree 
merges unambiguous, by just totally ignoring the history between them.
Ie 
we know how to merge data. I am pretty damn sure that _nobody_ knows how

to merge "data over time". Maybe BK does. I'm pretty sure it actually 
takes the "over time" into account. But My goal is to get something that

works, and something that is reliable because it is simple and it has 
simple rules.

As you say:

> This however opens up another set of can of worms---it would
> involve not just three trees but all the trees in the commit
> chain in between.

Exactly.  I seriously believe that the model is _broken_, simply because

it gets too complicated. At some point it boils down to "keep it simple,

stupid".

>  That's when you start wondering if it would
> be better to add renames in the git object model, which is the
> topic of another thread.  I have not formed an opinion on that
> one myself yet.

I've not even been convinved that renames are worth it. Nobody has
really 
given a good reason why.

There are two reasons for renames I can think of:

 - space efficiency in delta-based trees. This is a total non-issue for 
   git, and trying to explicitly track renames is going to cause _more_
   space to be wasted rather than less.

 - "annotate". Something git doesn't really handle anyway, and it has 
   little to do with renames. You can fake an annotate, but let's face
it, 
   it's _always_ going to be depending on interpreting a diff. In fact, 
   that ends up how traditional SCM's do it too - they don't really 
   annotate lines, they just interpret the diff.

   I think you might as well interpret the whole object thing. Git
_does_ 
   tell you how the objects changed, and I actually believe that a diff 
   that works in between objects (ie can show "these lines moved from
this
   file X to tjhat file Y") is a _hell_ of a lot more powerful than
   "rename"  is.

   So I'd seriously suggest that instead of worryign about renames,
people 
   think about global diffs that aren't per-file. Git is good at
limiting 
   the changes to a set of objects, and it should be entirely possible
to 
   think of diffs as ways of moving lines _between_ objects and not just
   within objects. It's quite common to move a function from one file to

   another - certainly more so than renaming the whole file.

   In other words, I really believe renames are just a meaningless
special 
   case of a much more interesting problem. Which is just one reason why

   I'm not at all interested in bothering with them other than as a
"data 
   moved" thing, which git already handles very well indeed.

So there,

		Linus
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



^ permalink raw reply

* Re: Yet another base64 patch
From: Paul Jackson @ 2005-04-15  3:58 UTC (permalink / raw)
  To: H. Peter Anvin; +Cc: torvalds, ahu, git, git
In-Reply-To: <425F13C9.5090109@zytor.com>

Earlier, hpa wrote:
> The base64 version has 2^12 subdirectories instead of 2^8 (I just used 2 
> characters as the hash key just like the hex version.)

Later, hpa wrote:
> Ultimately the question is: do we care about old (broken) filesystems?

I'd imagine we care a little - just not alot.

I'd think that going to 2^12 subdirectories, which with 2^12 entries per
subdirectory gets us to 16 million files before the leaf directories get
bigger than the parent, is a good tradeoff.

-- 
                  I won't rest till it's the best ...
                  Programmer, Linux Scalability
                  Paul Jackson <pj@engr.sgi.com> 1.650.933.1373, 1.925.600.0401

^ permalink raw reply

* Re: Date handling.
From: Paul Jackson @ 2005-04-15  5:02 UTC (permalink / raw)
  To: Luck, Tony; +Cc: dwmw2, torvalds, git
In-Reply-To: <B8E391BBE9FE384DAA4C5C003888BE6F03457AE6@scsmsx401.amr.corp.intel.com>

> I'd think the 8:00am-before-the-first-coffee checkins would be the
> most worrying :-)

For me, it was the Friday evening after beer bust checkin.

But my employer can't afford those anymore, so I'm safe.

-- 
                  I won't rest till it's the best ...
                  Programmer, Linux Scalability
                  Paul Jackson <pj@engr.sgi.com> 1.650.933.1373, 1.925.600.0401

^ permalink raw reply

* Re: another perspective on renames.
From: Paul Jackson @ 2005-04-15  5:16 UTC (permalink / raw)
  To: C. Scott Ananian; +Cc: git
In-Reply-To: <Pine.LNX.4.61.0504141759440.7261@cag.csail.mit.edu>

Scott wrote:
> Anyway, maybe it's worth thinking a little about an SCM in which this is a 
> feature, instead of (or in addition to) automatically assuming this is a 
> bug we need to add infrastructure to work around.

Agreed.

To me, the main purpose in tracking renames is to obtain a deeper
history of the line-by-line changes in a file.

  ==> But that doesn't seem relevant here.

Last I looked, git has no such history.  A given file contents
is the indivisable atom of the git world, with no fine structure.

This is quite unlike classic SCM's, built on file formats that
track source lines, not files, as the atomic unit.

To me, rename is a special case of the more general case of a
big chunk of code (a portion of a file) that was in one place
either being moved or copied to another place.

I wonder if there might be someway to use the tools that biologists use
to analyze DNA sequences, to track the evolution of source code,
identifying things like common chunks of code that differ in just a few
mutations, and presenting the history of the evolution, at selectable
levels of detail.

-- 
                  I won't rest till it's the best ...
                  Programmer, Linux Scalability
                  Paul Jackson <pj@engr.sgi.com> 1.650.933.1373, 1.925.600.0401

^ permalink raw reply

* Re: Re: Re: Re: Remove need to untrack before tracking new branch
From: Martin Schlemmer @ 2005-04-15  5:45 UTC (permalink / raw)
  To: Alex Riesen; +Cc: Petr Baudis, git
In-Reply-To: <81b0412b0504141535793cc235@mail.gmail.com>

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

On Fri, 2005-04-15 at 00:35 +0200, Alex Riesen wrote:
> On 4/14/05, Martin Schlemmer <azarah@nosferatu.za.org> wrote:
> > +               if (update_mode && changed & MODE_CHANGED)
> > +                       chmod(ce->name, ce->st_mode);
> 
> it's "if ((update_mode && changed) & MODE_CHANGED)"
> Did you really mean that?
> 

No, '&' have a higher priority (weight?) than '&&'.  Although, yes, it
might be better style to add brackets.

But just to make you happy, let me prove it:

-----
$ cat foo1.c
int main() {
        int foo, bar;

        if (foo && bar & 1)
                return 1;

        return 0;
}
$ cat foo2.c
int main() {
        int foo, bar;

        if (foo && (bar & 1))
                return 1;

        return 0;
}
$ cat foo3.c
int main() {
        int foo, bar;

        if ((foo && bar) & 1)
                return 1;

        return 0;
}
$ gcc -c -S foo1.c -o foo1.S
$ gcc -c -S foo2.c -o foo2.S
$ gcc -c -S foo3.c -o foo3.S
$ diff -u foo1.S foo2.S
--- foo1.S      2005-04-15 07:42:27.000000000 +0200
+++ foo2.S      2005-04-15 07:42:32.000000000 +0200
@@ -1,4 +1,4 @@
-       .file   "foo1.c"
+       .file   "foo2.c"
        .text
 .globl main
        .type   main, @function
$ diff -u foo1.S foo3.S
--- foo1.S      2005-04-15 07:42:27.000000000 +0200
+++ foo3.S      2005-04-15 07:42:35.000000000 +0200
@@ -1,4 +1,4 @@
-       .file   "foo1.c"
+       .file   "foo3.c"
        .text
 .globl main
        .type   main, @function
@@ -9,9 +9,14 @@
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
+       movl    $0, -16(%ebp)
        cmpl    $0, -4(%ebp)
-       je      .L2
-       movl    -8(%ebp), %eax
+       je      .L3
+       cmpl    $0, -8(%ebp)
+       je      .L3
+       movl    $1, -16(%ebp)
+.L3:
+       movl    -16(%ebp), %eax
        andl    $1, %eax
        testl   %eax, %eax
        je      .L2
-----


-- 
Martin Schlemmer


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

^ permalink raw reply

* [PATCH 0/4] Merging merge-trees changes to pasky-0.4
From: Junio C Hamano @ 2005-04-15  6:00 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Linus Torvalds, git

I finally sync'ed up with Pasky 0.4.  Reviewing the diffs
between Linus tree and Pasky tree for the core part you seem to
have picked up some good changes (especially the byteorder one),
so I decided to rebase my changes.  So here it comes...

What follows are the 3 patches to the core part to support the
three-tree merge script, and another to introduce the script
itself.  I used to call it git-merge.perl, but now it is called
merge-trees (per request from Pasky to drop git- prefix, and
Linus has merge-tree that does not recurse while this one does
subdirectories).  The core functinality has not changed much.
The changes from the previous version at this point is still
code and interface cleanup only.

My next step will be to make it possible to tell it not to do
anything but just output recipe.

[PATCH 1/4] Add --cacheinfo option to update-cache
[PATCH 2/4] Add -z option to show-files
[PATCH 3/4] Add -r and -z options to ls-tree
[PATCH 4/4] Makefile change and merge-trees script itself.

The patches are against 516f2a088903a7b5f5a542de96b6a70c17856314


^ permalink raw reply

* [PATCH 1/4] Add --cacheinfo option to update-cache
From: Junio C Hamano @ 2005-04-15  6:03 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Linus Torvalds, git
In-Reply-To: <7vr7hco9z7.fsf@assigned-by-dhcp.cox.net>

This adds "--cacheinfo" option to update-cache.  It is needed to
manipulate dircache without actually having such a blob in the working
directory.  To pretend you have a file with mode-sha1 at path, say:

 $ update-cache --cacheinfo mode sha1 path


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

---

 update-cache.c |   25 ++++++++++++++++++++++++-
 1 files changed, 24 insertions(+), 1 deletion(-)

Index: update-cache.c
===================================================================
--- 6767883b330882bc0e9a7c1e4fd999c0ee97ba3a/update-cache.c  (mode:100644 sha1:6c1d608cff03d2126191c0891cf1d262d6ae7823)
+++ 21e5e9f7d7dfa81c6519f0204d5a467236c7fdd5/update-cache.c  (mode:100644 sha1:8e82862ee66dc339967de558e7a5a9c52ba37259)
@@ -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

* [PATCH 2/4] Add -z option to show-files
From: Junio C Hamano @ 2005-04-15  6:04 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Linus Torvalds, git
In-Reply-To: <7vr7hco9z7.fsf@assigned-by-dhcp.cox.net>

This adds NUL-terminated output (-z) to show-files.  This is necessary
for merge-trees script to deal with filenames with embedded newlines.

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

 show-files.c |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)

Index: show-files.c
===================================================================
--- 6767883b330882bc0e9a7c1e4fd999c0ee97ba3a/show-files.c  (mode:100664 sha1:a9fa6767a418f870a34b39379f417bf37b17ee18)
+++ 21e5e9f7d7dfa81c6519f0204d5a467236c7fdd5/show-files.c  (mode:100664 sha1:c392db8b4edb16675528f86e106e841f42bc74e4)
@@ -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;


^ permalink raw reply

* [PATCH 3/4] Add -r and -z options to ls-tree
From: Junio C Hamano @ 2005-04-15  6:05 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Linus Torvalds, git
In-Reply-To: <7vr7hco9z7.fsf@assigned-by-dhcp.cox.net>

Recursive behaviour (-r) and NUL-terminated output (-z) are added to
ls-tree with this patch.  They are necessary for merge-trees script to
deal with filenames with embedded newlines.

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

 ls-tree.c |  108 +++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 90 insertions(+), 18 deletions(-)

Index: ls-tree.c
===================================================================
--- 6767883b330882bc0e9a7c1e4fd999c0ee97ba3a/ls-tree.c  (mode:100644 sha1:3e2a6c7d183a42e41f1073dfec6794e8f8a5e75c)
+++ 21e5e9f7d7dfa81c6519f0204d5a467236c7fdd5/ls-tree.c  (mode:100664 sha1:cf1279b2c032aeffa72013ddee9dcb8742a7b069)
@@ -5,45 +5,117 @@
  */
 #include "cache.h"
 
-static int list(unsigned char *sha1)
+int line_terminator = '\n';
+int recursive = 0;
+
+struct path_prefix {
+	struct path_prefix *prev;
+	const char *name;
+};
+
+static void print_path_prefix(struct path_prefix *prefix)
 {
-	void *buffer;
-	unsigned long size;
-	char type[20];
+	if (prefix) {
+		if (prefix->prev)
+			print_path_prefix(prefix->prev);
+		fputs(prefix->name, stdout);
+		putchar('/');
+	}
+}
+
+static void list_recursive(void *buffer,
+			  unsigned char *type,
+			  unsigned long size,
+			  struct path_prefix *prefix)
+{
+	struct path_prefix this_prefix;
+	this_prefix.prev = prefix;
 
-	buffer = read_sha1_file(sha1, type, &size);
-	if (!buffer)
-		die("unable to read sha1 file");
 	if (strcmp(type, "tree"))
 		die("expected a 'tree' node");
+
 	while (size) {
-		int len = strlen(buffer)+1;
-		unsigned char *sha1 = buffer + len;
-		char *path = strchr(buffer, ' ')+1;
+		int namelen = strlen(buffer)+1;
+		void *eltbuf;
+		char elttype[20];
+		unsigned long eltsize;
+		unsigned char *sha1 = buffer + namelen;
+		char *path = strchr(buffer, ' ') + 1;
 		unsigned int mode;
-		unsigned char *type;
 
-		if (size < len + 20 || sscanf(buffer, "%o", &mode) != 1)
+		if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
 			die("corrupt 'tree' file");
 		buffer = sha1 + 20;
-		size -= len + 20;
+		size -= namelen + 20;
+
 		/* XXX: We do some ugly mode heuristics here.
 		 * 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);
+		 * and the file size. -- pasky@ucw.cz
+		 * ... that is, when we are not recursive -- junkio@cox.net
+		 */
+		eltbuf = (recursive ? read_sha1_file(sha1, elttype, &eltsize) :
+			  NULL);
+		if (! eltbuf) {
+			if (recursive)
+				error("cannot read %s", sha1_to_hex(sha1));
+			type = S_ISDIR(mode) ? "tree" : "blob";
+		}
+		else
+			type = elttype;
+
+		printf("%03o\t%s\t%s\t", mode, type, sha1_to_hex(sha1));
+		print_path_prefix(prefix);
+		fputs(path, stdout);
+		putchar(line_terminator);
+
+		if (eltbuf && !strcmp(type, "tree")) {
+			this_prefix.name = path;
+			list_recursive(eltbuf, elttype, eltsize, &this_prefix);
+		}
+		free(eltbuf);
 	}
+}
+
+static int list(unsigned char *sha1)
+{
+	void *buffer;
+	unsigned long size;
+	char type[20];
+
+	buffer = read_sha1_file(sha1, type, &size);
+	if (!buffer)
+		die("unable to read sha1 file");
+	list_recursive(buffer, type, size, NULL);
 	return 0;
 }
 
+static void _usage(void)
+{
+	usage("ls-tree [-r] [-z] <key>");
+}
+
 int main(int argc, char **argv)
 {
 	unsigned char sha1[20];
 
+	while (1 < argc && argv[1][0] == '-') {
+		switch (argv[1][1]) {
+		case 'z':
+			line_terminator = 0;
+			break;
+		case 'r':
+			recursive = 1;
+			break;
+		default:
+			_usage();
+		}
+		argc--; argv++;
+	}
+
 	if (argc != 2)
-		usage("ls-tree <key>");
+		_usage();
 	if (get_sha1_hex(argv[1], sha1) < 0)
-		usage("ls-tree <key>");
+		_usage();
 	sha1_file_directory = getenv(DB_ENVIRONMENT);
 	if (!sha1_file_directory)
 		sha1_file_directory = DEFAULT_DB_ENVIRONMENT;







^ permalink raw reply

* [PATCH 4/4] Makefile change and merge-trees script itself.
From: Junio C Hamano @ 2005-04-15  6:06 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Linus Torvalds, git
In-Reply-To: <7vr7hco9z7.fsf@assigned-by-dhcp.cox.net>

This adds merge-trees to the list of scripts to be installed in
the Makefile, and also adds merge-trees script itself.

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

 21e5e9f7d7dfa81c6519f0204d5a467236c7fdd5/merge-trees |  302 ++++++++++++++++++
 Makefile                                             |    2 
 2 files changed, 303 insertions(+), 1 deletion(-)

Index: Makefile
===================================================================
--- 6767883b330882bc0e9a7c1e4fd999c0ee97ba3a/Makefile  (mode:100644 sha1:3a4683454df4ac8f16eca01fe93a787a2ce8f0f4)
+++ 21e5e9f7d7dfa81c6519f0204d5a467236c7fdd5/Makefile  (mode:100644 sha1:0384a229456145a763a5316ce301e74d07454f66)
@@ -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 commit-id
+	gitapply.sh gitcancel.sh gitlntree.sh commit-id merge-trees
 
 COMMON=	read-cache.o
 


Index: merge-trees
===================================================================
--- /dev/null  (tree:6767883b330882bc0e9a7c1e4fd999c0ee97ba3a)
+++ 21e5e9f7d7dfa81c6519f0204d5a467236c7fdd5/merge-trees  (mode:100775 sha1:5da2f8a949aeffa60a400d013222fce34eb7262e)
@@ -0,0 +1,302 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Cwd;
+use Getopt::Long;
+
+my $full_checkout = 0;
+my $partial_checkout = 0;
+my $output_directory = ',,merge~tree';
+
+GetOptions("full-checkout" => \$full_checkout,
+	   "partial-checkout" => \$partial_checkout,
+	   "output-directory=s" => \$output_directory)
+    or die;
+
+
+if (@ARGV != 3) {
+    die "Usage: $0 -o [output-directory] [-f] [-p] ancestor A B\n";
+}
+
+if ($full_checkout) {
+    $partial_checkout = 1;
+}
+
+################################################################
+# UI helper -- although it is encouraged to give tree ID, 
+# it is OK to give commit ID.
+sub possibly_commit_to_tree {
+    my ($commit_or_tree_id) = @_;
+    my $type = read_cat_file_t($commit_or_tree_id);
+    if ($type eq 'tree') { return $commit_or_tree_id }
+    if ($type ne 'commit') {
+	die "Tree ID (or commit ID) required, given $type.";
+    }
+
+    my ($fhi);
+    open $fhi, '-|', 'cat-file', 'commit', $commit_or_tree_id
+	or die "$!: cat-file commit $commit_or_tree_id";
+    my ($tree) = <$fhi>;
+    close $fhi;
+    ($tree =~ s/^tree (.*)$/$1/)
+	or die "$tree: Linus says the first line is guaranteed to be tree.";
+    return $tree;
+}
+
+sub read_cat_file_t {
+    my ($id) = @_;
+    my ($fhi);
+    open $fhi, '-|', 'cat-file', '-t', $id
+	or die "$!: cat-file -t $id";
+    my ($t) = <$fhi>;
+    close $fhi;
+    chomp($t);
+    return $t;
+}
+
+################################################################
+# Reads diff-tree -r output and gives a hash that maps a path
+# to 4-tuple (old-mode new-mode old-oid new-oid).
+# When creating, old-* are undef.  When removing, new-* are undef.
+
+sub OLD_MODE () { 0 }
+sub NEW_MODE () { 1 }
+sub OLD_OID ()  { 2 }
+sub NEW_OID ()  { 3 }
+
+sub read_diff_tree {
+    my (@tree) = @_;
+    my ($fhi);
+
+    # Regular expression piece for mode
+    my $reM  = '[0-7]+';
+
+    # Regular expression piece for object ID.
+    # There is a talk about base-64 so better make it easier to modify...
+    my $reID = '[0-9a-f]{40}';
+
+    local ($_, $/);
+    $/ = "\0"; 
+    my %path;
+    open $fhi, '-|', 'diff-tree', '-r', @tree
+	or die "$!: diff-tree -r @tree";
+    while (<$fhi>) {
+	chomp;
+	if (/^\*($reM)->($reM)\tblob\t($reID)->($reID)\t(.*)$/so) {
+	    $path{$5} = [$1, $2, $3, $4]; # modified
+	}
+	elsif (/^\+($reM)\tblob\t($reID)\t(.*)$/so) {
+	    $path{$3} = [undef, $1, undef, $2]; # added
+	}
+	elsif (/^\-($reM)\tblob\t($reID)\t(.*)$/so) {
+	    $path{$3} = [$1, undef, $2, undef]; # deleted
+	}
+	else {
+	    die "cannot parse diff-tree output: $_";
+	}
+    }
+    close $fhi;
+    return %path;
+}
+
+################################################################
+# Read show-files output to figure out the set of files contained
+# in the tree.  This is used to figure out what ancestor had.
+sub read_show_files {
+    my ($fhi);
+    local ($_, $/);
+    $/ = "\0"; 
+    open $fhi, '-|', 'show-files', '-z', '--cached'
+	or die "$!: show-files -z --cached";
+    my (@path) = map { chomp; $_ } <$fhi>;
+    close $fhi;
+    return @path;
+}
+
+################################################################
+# Given path and info (typically returned from read_diff_tree),
+# create the file in the working directory to match the NEW tree.
+# This does not touch dircache.
+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->[NEW_OID]
+	or die "$!: cat-file blob $info->[NEW_OID]";
+    while (<$fhi>) {
+	print $fho $_;
+    }
+    close $fhi;
+    close $fho;
+    chmod oct("0$info->[NEW_MODE]"), "$path";
+}
+
+################################################################
+# Given path and info record the file in the dircache without
+# affecting working directory.
+sub record_file {
+    my ($path, $info) = @_;
+    system ('update-cache', '--add', '--cacheinfo',
+	    $info->[NEW_MODE], $info->[NEW_OID], $path);
+}
+
+################################################################
+# Merge info from two trees and leave it in path, without
+# affecting dircache.
+sub merge_tree {
+    my ($path, $infoA, $infoB) = @_;
+    checkout_file("$path~A~", $infoA);
+    checkout_file("$path~B~", $infoB);
+    system 'checkout-cache', $path;
+    rename $path, "$path~O~";
+    my ($fhi, $fho);
+    open $fhi, '-|', 'merge', '-p', "$path~A~", "$path~O~", "$path~B~";
+    open $fho, '>', $path;
+    local ($/);
+    while (<$fhi>) { print $fho $_; }
+    close $fhi;
+    close $fho;
+    # There is no reason to prefer infoA over infoB but
+    # we need to pick one.
+    chmod oct("0$infoA->[NEW_MODE]"), $path;
+}
+
+################################################################
+
+# O stands for "the original".  A and B are being merged.
+my ($treeO, $treeA, $treeB) = map { possibly_commit_to_tree $_ } @ARGV;
+
+# Create a temporary directory and go there.
+system('rm', '-rf', $output_directory) == 0 &&
+system('mkdir', '-p', "$output_directory/.git") == 0 &&
+symlink(Cwd::getcwd . "/.git/objects", "$output_directory/.git/objects") &&
+chdir $output_directory &&
+system('read-tree', $treeO) == 0
+    or die "$!: Failed to set up merge working area $output_directory";
+
+# Find out edits done in each branch.
+my %treeA = read_diff_tree($treeO, $treeA);
+my %treeB = read_diff_tree($treeO, $treeB);
+
+# The list of files that was in the ancestor.
+my @ancestor_file = read_show_files();
+my %ancestor_file = map { $_ => 1 } @ancestor_file;
+
+# Report output is formated as follows:
+#
+# The first letter shows the origin of the result.
+#   O - original
+#   A - treeA
+#   B - treeB
+#   M - both treeA and treeB
+#   * - treeA and treeB conflicts; needs human action.
+#
+# The second and third letter shows what each tree did.
+#   . - no change
+#   A - created
+#   M - modified
+#   D - deleted
+
+for (@ancestor_file) {
+    if (! exists $treeA{$_} && ! exists $treeB{$_}) {
+	if ($full_checkout) {
+	    system 'checkout-cache', $_;
+	}
+	print STDERR "O.. $_\n"; # keep original
+    }
+}
+
+for my $set ([\%treeA, \%treeB, 'A'], [\%treeB, \%treeA, 'B']) {
+    my ($this, $other, $side) = @$set;
+    my $delete_sign = ($side eq 'A') ? 'D.' : '.D';
+    my $create_sign = ($side eq 'A') ? 'A.' : '.A';
+    my $modify_sign = ($side eq 'A') ? 'M.' : '.M';
+    while (my ($path, $info) = each %$this) {
+	# In this loop we do not deal with overlaps.
+	next if (exists $other->{$path});
+
+	if (! defined $info->[NEW_OID]) {
+	    # deleted in this tree only.
+	    unlink $path;
+	    system 'update-cache', '--remove', $path;
+	    print STDERR "${side}${delete_sign} $path\n";
+	}
+	else {
+	    # modified or created in this tree only.
+	    my $create_or_modify =
+		(! defined $info->[OLD_OID]) ? $create_sign : $modify_sign;
+	    print STDERR "${side}${create_or_modify} $path\n";
+	    if ($partial_checkout) {
+		checkout_file($path, $info);
+		system 'update-cache', '--add', $path;
+	    } else {
+		record_file($path, $info);
+	    }
+	}
+    }
+}
+
+my @warning = ();
+
+while (my ($path, $infoA) = each %treeA) {
+    # We need to deal only with overlaps.
+    next if (!exists $treeB{$path});
+
+    my $infoB = $treeB{$path};
+    if (! defined $infoA->[NEW_OID]) {
+	# Deleted in tree A.
+	if (! defined $infoB->[NEW_OID]) {
+	    # Deleted in both trees (obvious).
+	    print STDERR "MDD $path\n";
+	    unlink $path;
+	    system 'update-cache', '--remove', $path;
+	}
+	else {
+	    # TreeA wants to remove but TreeB wants to modify it.
+	    print STDERR "*DM $path\n";
+	    checkout_file("$path~B~", $infoB);
+	    push @warning, $path;
+	}
+    }
+    else {
+	# Modified or created in tree A
+	if (! defined $infoB->[NEW_OID]) {
+	    # TreeA wants to modify but treeB wants to remove it.
+	    print STDERR "*MD $path\n";
+	    checkout_file("$path~A~", $infoA);
+	    push @warning, $path;
+	}
+	else {
+	    # Modified both in treeA and treeB.
+	    # Are they modifying to the same contents?
+	    if ($infoA->[NEW_OID] eq $infoB->[NEW_OID]) {
+		# No changes or just the mode.
+		# we prefer TreeA over TreeB for no particular reason.
+		print STDERR "MMM $path\n";
+		record_file($path, $infoA);
+	    }
+	    else {
+		# Modified in both.  Needs merge.
+		print STDERR "*MM $path\n";
+		merge_tree($path, $infoA, $infoB);
+	    }
+	}
+    }
+}
+
+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', '-q';


^ permalink raw reply

* Re: Remove need to untrack before tracking new branch
From: Paul Jackson @ 2005-04-15  6:42 UTC (permalink / raw)
  To: azarah; +Cc: raa.lkml, pasky, git
In-Reply-To: <1113543914.23299.151.camel@nosferatu.lan>

> No, '&' have a higher priority (weight?) than '&&'.

& has a higher precedence than &&

  C Operator Precedence and Associativity
  http://www.difranco.net/cop2220/op-prec.htm

and many others -- google for 'c operator precedence'

Where the bitops &, | and ^ bite you is that they are
lower precedence than many other ops, including '=='.

-- 
                  I won't rest till it's the best ...
                  Programmer, Linux Scalability
                  Paul Jackson <pj@engr.sgi.com> 1.650.933.1373, 1.925.600.0401

^ permalink raw reply

* Re: Merge with git-pasky II.
From: Junio C Hamano @ 2005-04-15  7:43 UTC (permalink / raw)
  To: Christopher Li; +Cc: Petr Baudis, Linus Torvalds, git
In-Reply-To: <20050414223039.GB28082@64m.dyndns.org>

>>>>> "CL" == Christopher Li <git@chrisli.org> writes:

>> - Result is this object $SHA1 with mode $mode at $path (takes
>> one of the trees); you can do update-cache --cacheinfo (if
>> you want to muck with dircache) or cat-file blob (if you want
>> to get the file) or both.

CL> Is that SHA1 for tree or the file object?

I am talking about a single file here.



^ permalink raw reply

* Re: another perspective on renames.
From: Ingo Molnar @ 2005-04-15  8:27 UTC (permalink / raw)
  To: Paul Jackson; +Cc: C. Scott Ananian, git
In-Reply-To: <20050414221626.10c6c0e7.pj@engr.sgi.com>


* Paul Jackson <pj@engr.sgi.com> wrote:

> Scott wrote:
> > Anyway, maybe it's worth thinking a little about an SCM in which this is a 
> > feature, instead of (or in addition to) automatically assuming this is a 
> > bug we need to add infrastructure to work around.
> 
> Agreed.
> 
> To me, the main purpose in tracking renames is to obtain a deeper
> history of the line-by-line changes in a file.
> 
>   ==> But that doesn't seem relevant here.
> 
> Last I looked, git has no such history.  A given file contents is the 
> indivisable atom of the git world, with no fine structure.
> 
> This is quite unlike classic SCM's, built on file formats that track 
> source lines, not files, as the atomic unit.

i believe the fundamental thing to think about is not file or line or 
namespace, but 'tracking developer intent'. While keeping in mind that 
GIT is not an SCM, all SCMs boil down to this single thing: being able 
to track what the developer did and why he did it - to be a useful tool 
later on. (SCMs are for humans with bad limitations, who have this 
fundamental design bug and keep forgetting things.)

the basic question is, how much to track. The most extreme form of 
tracking (just for the sake of visualizing it) would be to have an 
eye-position recognizing software attached to a webcam looking at the 
developer, and then exactly mapping what he did, how long did he look at 
one particular line of code and exactly what did he type when doing 
that. [ Perhaps also a thought-reader module in addition, once one is 
available. (combined with another module that removes all the swearing)]

but i think Linus is on the right track to suggest that "the file names 
dont matter all that much, it's all about the content". Global diffs 
might track most types of plain renames, and if it gets it wrong - do we 
care? Misdetection of renames can happen, but realistically only with 
small files and trivial code, which wont have alot of history.

The only serious type of misdetection would be if two large modules in 
two different places in the namespace happen to have exactly the same 
content but have a different history (because e.g. they were merged in 
via two separate trees, one came from one tree, the other from the other 
tree), and the developer renamed both of them in the same commit: in 
such a case the global diff would have no way to figure out what the 
proper thread of history is. But is this a realistic scenario?  If the 
two files are nontrivial and have the same content, why werent they 
merged in the namespace in the first place?

the moment we allow 'namespace' into the picture, things get complex and 
ugly. Directory recursion is already a complexity that would have been 
nice to avoid.

	Ingo

^ permalink raw reply

* Re: Merge with git-pasky II.
From: David Woodhouse @ 2005-04-15  9:14 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Junio C Hamano, Petr Baudis, git
In-Reply-To: <Pine.LNX.4.58.0504141133260.7211@ppc970.osdl.org>

On Thu, 2005-04-14 at 11:36 -0700, Linus Torvalds wrote:
> And "merge these two trees" (which works on a _tree_ level)
> or "find the common commit" (which works on a _commit_ level)

I suspect that finding the common commit is actually a per-file thing;
it's not just something you do for the _commit_ graph, then use for
merging each file in the two branches you're trying to merge.

Consider a simple repository which contains two files A and B. We start
off with the first version of each ('A1B1'), and the owner of each file
takes a branch and modifies their own file. There is cross-pulling
between the two, and then each modifies the _other's_ file as well as
their own...

   (A1B2)--(A2B2)--(A2'B3)
    /  \   /            \
   /    \ /              \
 (A1B1)  X               (...)
   \    / \              /
    \  /   \            /
   (A2B1)--(A2B2)--(A3B2')

Now, we're trying to merge the two branches. It appears that the most
useful common ancestor to use for a three-way merge of file A is the
version from tree 'A2B1', while the most useful common ancestor for
merging file B is that in 'A1B2'.

(I think it's a coincidence that in my example the useful files 'A2' and
'B2' actually do end up in a single tree together at some point.)

-- 
dwmw2



^ permalink raw reply

* [patch pasky 1/2] fix various issues in gitapply.sh (basically did not handle add/del/cm at all)
From: Martin Schlemmer @ 2005-04-15  9:28 UTC (permalink / raw)
  To: GIT Mailing Lists; +Cc: Petr Baudis


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

Hi,

The egrep regex should not escape the '{' and '}', and also add a check
for ' \t' so that we do not pickup stuff like '+----', etc.  Fix typo in
assignment.  Check if file exists in new tree before adding/removing
(might add support for this lowlevel to increase speed?).  Fix typo in
line removing temp files.

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

gitapply.sh:  47b9346d2679b1bf34220fe4502f15c7d0737b0c
--- 47b9346d2679b1bf34220fe4502f15c7d0737b0c/gitapply.sh
+++ uncommitted/gitapply.sh
@@ -19,15 +19,22 @@
 # just handle it all ourselves.
 patch -p1 -N <$patchfifo &

-tee $patchfifo | egrep '^[+-]\{3\}' | {
+exits_in_cache() {
+       for x in $(ls-tree "$1"); do
+               [ "$x" = "$2" ] && return 0
+       done
+       return 1
+}
+
+tee $patchfifo | egrep '^[+-]{3}[ \t]' | {
        victim=
        origmode=

        while read sign file attrs; do
-               echo $sign $file $attrs ... >&2
+#              echo $sign $file $attrs ... >&2
                case $sign in
                "---")
-                       victim=file
+                       victim=$file
                        mode=$(echo $attrs | sed 's/.*mode:[0-7]*\([0-7]\{3\}\).*/\1/')
                        origmode=
                        [ "$mode" != "$attrs" ] && origmode=$mode
@@ -35,14 +42,19 @@
                "+++")
                        if [ "$file" = "/dev/null" ]; then
                                torm=$(echo "$victim" | sed 's/[^\/]*\///') #-p1
-                               echo -ne "rm\0$torm\0"
+                               tree=$(echo $attrs | sed 's/.*tree:\([0-9a-f]\{40\}\).*/\1/')
+                               exits_in_cache "$tree" "$torm" && echo -ne "rm\0$torm\0"
                                continue
                        elif [ "$victim" = "/dev/null" ]; then
-                               echo -ne "add\0$file\0"
+                               toadd=$(echo "$file" | sed 's/[^\/]*\///') #-p1
+                               tree=$(echo "$file" | sed -e 's/\([^\/]*\)\/.*/\1/')
+                               exits_in_cache "$tree" "$toadd" || echo -ne "add\0$toadd\0"
                        fi
                        mode=$(echo $attrs | sed 's/.*mode:[0-7]*\([0-7]\{3\}\).*/\1/')
                        if [ "$mode" ] && [ "$mode" != "$attrs" ] && [ "$origmode" != "$mode" ]; then
-                               echo -ne "cm\0$mode\0$file\0"
+                               tochmod=$(echo "$file" | sed 's/[^\/]*\///') #-p1
+                               # need a space else numbers gets converted
+                               echo -ne "cm\0 $mode\0$tochmod\0"
                        fi
                        ;;
                *)
@@ -74,4 +86,4 @@
 done
 ' padding

-rm $pathfifo $todo $gonefile
+rm $patchfifo $todo $gonefile


-- 
Martin Schlemmer


[-- Attachment #1.2: git-gitapply-fixes.patch --]
[-- Type: text/x-patch, Size: 1781 bytes --]

gitapply.sh:  47b9346d2679b1bf34220fe4502f15c7d0737b0c
--- 47b9346d2679b1bf34220fe4502f15c7d0737b0c/gitapply.sh
+++ uncommitted/gitapply.sh
@@ -19,15 +19,22 @@
 # just handle it all ourselves.
 patch -p1 -N <$patchfifo &
 
-tee $patchfifo | egrep '^[+-]\{3\}' | {
+exits_in_cache() {
+	for x in $(ls-tree "$1"); do
+		[ "$x" = "$2" ] && return 0
+	done
+	return 1
+}
+
+tee $patchfifo | egrep '^[+-]{3}[ \t]' | {
 	victim=
 	origmode=
 
 	while read sign file attrs; do
-		echo $sign $file $attrs ... >&2
+#		echo $sign $file $attrs ... >&2
 		case $sign in
 		"---")
-			victim=file
+			victim=$file
 			mode=$(echo $attrs | sed 's/.*mode:[0-7]*\([0-7]\{3\}\).*/\1/')
 			origmode=
 			[ "$mode" != "$attrs" ] && origmode=$mode
@@ -35,14 +42,19 @@
 		"+++")
 			if [ "$file" = "/dev/null" ]; then
 				torm=$(echo "$victim" | sed 's/[^\/]*\///') #-p1
-				echo -ne "rm\0$torm\0"
+				tree=$(echo $attrs | sed 's/.*tree:\([0-9a-f]\{40\}\).*/\1/')
+				exits_in_cache "$tree" "$torm" && echo -ne "rm\0$torm\0"
 				continue
 			elif [ "$victim" = "/dev/null" ]; then
-				echo -ne "add\0$file\0"
+				toadd=$(echo "$file" | sed 's/[^\/]*\///') #-p1
+				tree=$(echo "$file" | sed -e 's/\([^\/]*\)\/.*/\1/')
+				exits_in_cache "$tree" "$toadd" || echo -ne "add\0$toadd\0"
 			fi
 			mode=$(echo $attrs | sed 's/.*mode:[0-7]*\([0-7]\{3\}\).*/\1/')
 			if [ "$mode" ] && [ "$mode" != "$attrs" ] && [ "$origmode" != "$mode" ]; then
-				echo -ne "cm\0$mode\0$file\0"
+				tochmod=$(echo "$file" | sed 's/[^\/]*\///') #-p1
+				# need a space else numbers gets converted
+				echo -ne "cm\0 $mode\0$tochmod\0"
 			fi
 			;;
 		*)
@@ -74,4 +86,4 @@
 done
 ' padding
 
-rm $pathfifo $todo $gonefile
+rm $patchfifo $todo $gonefile

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

^ permalink raw reply

* Re: [patch pasky 1/2] fix various issues in gitapply.sh (basically did not handle add/del/cm at all)
From: Martin Schlemmer @ 2005-04-15  9:31 UTC (permalink / raw)
  To: GIT Mailing Lists; +Cc: Petr Baudis
In-Reply-To: <1113557318.23299.165.camel@nosferatu.lan>

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

PS: forget the '1/2' in the topic, i did it slightly different which
required changes to gettrack.sh, etc, but to got getmerge.sh, and saw my
short sightedness.

On Fri, 2005-04-15 at 11:28 +0200, Martin Schlemmer wrote:
> Hi,
> 
> The egrep regex should not escape the '{' and '}', and also add a check
> for ' \t' so that we do not pickup stuff like '+----', etc.  Fix typo in
> assignment.  Check if file exists in new tree before adding/removing
> (might add support for this lowlevel to increase speed?).  Fix typo in
> line removing temp files.
> 
> Signed-off-by: Martin Schlemmer <azarah@gentoo.org>
> 
> gitapply.sh:  47b9346d2679b1bf34220fe4502f15c7d0737b0c
> --- 47b9346d2679b1bf34220fe4502f15c7d0737b0c/gitapply.sh
> +++ uncommitted/gitapply.sh
> @@ -19,15 +19,22 @@
>  # just handle it all ourselves.
>  patch -p1 -N <$patchfifo &
> 
> -tee $patchfifo | egrep '^[+-]\{3\}' | {
> +exits_in_cache() {
> +       for x in $(ls-tree "$1"); do
> +               [ "$x" = "$2" ] && return 0
> +       done
> +       return 1
> +}
> +
> +tee $patchfifo | egrep '^[+-]{3}[ \t]' | {
>         victim=
>         origmode=
> 
>         while read sign file attrs; do
> -               echo $sign $file $attrs ... >&2
> +#              echo $sign $file $attrs ... >&2
>                 case $sign in
>                 "---")
> -                       victim=file
> +                       victim=$file
>                         mode=$(echo $attrs | sed 's/.*mode:[0-7]*\([0-7]\{3\}\).*/\1/')
>                         origmode=
>                         [ "$mode" != "$attrs" ] && origmode=$mode
> @@ -35,14 +42,19 @@
>                 "+++")
>                         if [ "$file" = "/dev/null" ]; then
>                                 torm=$(echo "$victim" | sed 's/[^\/]*\///') #-p1
> -                               echo -ne "rm\0$torm\0"
> +                               tree=$(echo $attrs | sed 's/.*tree:\([0-9a-f]\{40\}\).*/\1/')
> +                               exits_in_cache "$tree" "$torm" && echo -ne "rm\0$torm\0"
>                                 continue
>                         elif [ "$victim" = "/dev/null" ]; then
> -                               echo -ne "add\0$file\0"
> +                               toadd=$(echo "$file" | sed 's/[^\/]*\///') #-p1
> +                               tree=$(echo "$file" | sed -e 's/\([^\/]*\)\/.*/\1/')
> +                               exits_in_cache "$tree" "$toadd" || echo -ne "add\0$toadd\0"
>                         fi
>                         mode=$(echo $attrs | sed 's/.*mode:[0-7]*\([0-7]\{3\}\).*/\1/')
>                         if [ "$mode" ] && [ "$mode" != "$attrs" ] && [ "$origmode" != "$mode" ]; then
> -                               echo -ne "cm\0$mode\0$file\0"
> +                               tochmod=$(echo "$file" | sed 's/[^\/]*\///') #-p1
> +                               # need a space else numbers gets converted
> +                               echo -ne "cm\0 $mode\0$tochmod\0"
>                         fi
>                         ;;
>                 *)
> @@ -74,4 +86,4 @@
>  done
>  ' padding
> 
> -rm $pathfifo $todo $gonefile
> +rm $patchfifo $todo $gonefile
> 
> 
-- 
Martin Schlemmer


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

^ permalink raw reply

* Re: Merge with git-pasky II.
From: Ingo Molnar @ 2005-04-15  9:36 UTC (permalink / raw)
  To: David Woodhouse; +Cc: Linus Torvalds, Junio C Hamano, Petr Baudis, git
In-Reply-To: <1113556448.12012.269.camel@baythorne.infradead.org>


* David Woodhouse <dwmw2@infradead.org> wrote:

> Consider a simple repository which contains two files A and B. We 
> start off with the first version of each ('A1B1'), and the owner of 
> each file takes a branch and modifies their own file. There is 
> cross-pulling between the two, and then each modifies the _other's_ 
> file as well as their own...
> 
>    (A1B2)--(A2B2)--(A2'B3)
>     /  \   /            \
>    /    \ /              \
>  (A1B1)  X               (...)
>    \    / \              /
>     \  /   \            /
>    (A2B1)--(A2B2)--(A3B2')
> 
> Now, we're trying to merge the two branches. It appears that the most 
> useful common ancestor to use for a three-way merge of file A is the 
> version from tree 'A2B1', while the most useful common ancestor for 
> merging file B is that in 'A1B2'.

do such cases occur frequently? In the kernel at least it's not too 
typical. Would it be a problem to go for the simple solution of using 
(A1B1) as the common ancestor (based on the tree graph), and then to do 
a 3-way merge of all changes from that point on?

	Ingo

^ permalink raw reply

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

On Fri, Apr 15, 2005 at 12:43:47AM -0700, Junio C Hamano wrote:
> >>>>> "CL" == Christopher Li <git@chrisli.org> writes:
> 
> CL> Is that SHA1 for tree or the file object?
> 
> I am talking about a single file here.
>
Then do you emit the entry for it's parents directory?

e.g. /foo/bar get created. foo doesn't exists. You have
to create foo first. You don't have mode information for
foo yet. If it give the top level tree, the SCM can check it
out by tree. hopefully have the mode on directory correctly.
Well, if they care about those little details.

Chris
 

^ permalink raw reply

* Re: Merge with git-pasky II.
From: David Woodhouse @ 2005-04-15 10:02 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Junio C Hamano, Petr Baudis, git
In-Reply-To: <Pine.LNX.4.58.0504141728590.7211@ppc970.osdl.org>

On Thu, 2005-04-14 at 17:42 -0700, Linus Torvalds wrote:
> I've not even been convinved that renames are worth it. Nobody has
> really given a good reason why.
> 
> There are two reasons for renames I can think of:
> 
>  - space efficiency in delta-based trees.
>  - "annotate".

Neither of those were my motivation for looking at renames. The reasons
I wanted to track renames were:
   - Per-file revision history which doesn't stop dead at a rename.
   - Merging where files have been renamed in one branch and modified in
     another. Which is basically a special case of the above; we need to
     see the per-file revision history.

>    So I'd seriously suggest that instead of worryign about renames, people 
>    think about global diffs that aren't per-file. Git is good at limiting 
>    the changes to a set of objects, and it should be entirely possible to 
>    think of diffs as ways of moving lines _between_ objects and not just
>    within objects. It's quite common to move a function from one file to 
>    another - certainly more so than renaming the whole file.
>
>    In other words, I really believe renames are just a meaningless special 
>    case of a much more interesting problem. Which is just one reason why 
>    I'm not at all interested in bothering with them other than as a "data 
>    moved" thing, which git already handles very well indeed.

Git doesn't handle 'data moved' except at a whole-tree level. For each
commit, it says "these are the old trees; this is the new tree".

Git doesn't actually look hard into the contents of tree; certainly it
has no business looking at the contents of individual files; that is
something that the SCM or possibly only the user should do. The storage
of 'rename' information in the commit object is another kind of 'xattr'
storage which git would provides but not directly interpret.

And you're right; it shouldn't have to be for renames only. There's no
need for us to limit it to one "source" and one "destination"; the SCM
can use it to track content as it sees fit.

As I said, the main aim of this is to track revision history of given
content, for displaying to the user and for performing merges. So when a
file is split up, or a function is moved from it to another file, a
'rename' xattr can be included to mark that files 'foo' and 'bar' in the
new tree are both associated with file 'wibble' in the parent.

That's as much as we need to provide for content tracking, and it _does_
handle the general case as well as we should be attempting to. We don't
want to get into dealing with file contents ourselves; we just want to
store the hint for the SCM or the user that "your data went thataway".

-- 
dwmw2



^ permalink raw reply

* Re: Merge with git-pasky II.
From: David Woodhouse @ 2005-04-15 10:05 UTC (permalink / raw)
  To: Ingo Molnar; +Cc: Linus Torvalds, Junio C Hamano, Petr Baudis, git
In-Reply-To: <20050415093649.GA28077@elte.hu>

On Fri, 2005-04-15 at 11:36 +0200, Ingo Molnar wrote:
> do such cases occur frequently? In the kernel at least it's not too 
> typical. 

Isn't it? I thought it was a fairly accurate representation of the
process "I make a whole bunch of changes to files I maintain, pulling
from Linus while occasionally asking him to pull from my tree. Sometimes
my files are changed by someone else in Linus' tree, and sometimes I
change files that I don't actually own.".

-- 
dwmw2



^ permalink raw reply

* Re: Merge with git-pasky II.
From: Junio C Hamano @ 2005-04-15 10:22 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Linus Torvalds, git
In-Reply-To: <20050414233159.GX22699@pasky.ji.cz>

After I re-read [*R1*], in which Linus talks about dircache,
especially this section:

 - The "current directory cache" describes some baseline. In particular,
   note the "some" part. It's not tied to any special baseline, and you
   can change your baseline any way you please.

   So it does NOT have to track any particular state in either the object 
   database _or_ in your actual current working tree. In fact, all real 
   interactions with "git" are really about updating this staging area one 
   way or the other: you might check out the state from it into your 
   working area (partially or fully), you can push your working area into 
   the staging area (again, partially or fully).

   And if you want to, you can write the thing that the staging area 
   represents as a "tree" into the object database, or you can merge a 
   tree from the object database into the staging area.

   In other words: the staging area aka "current directory cache" is 
   really how all interaction takes place. The object database never 
   interacts directly with your working directory contents. ALL 
   interactions go through the current directory cache.

I started to have more doubts on the approach of *not*
performing the merge in the dircache I set up specifically for
merging, which is the direction in which you are pushing if I
understand you correctly.  Maybe I completely misunderstand what
you want.  This message is long but I need a clear understanding
of what is expected to be useful to you, so please bear with me.

PB> 	merge-tree.pl -b $base $(tree-id) $merged | parse-your-output

Please help me understand this example you have given earlier.
Here is my understanding of your assumption when the above
pipeline takes place.  Correct me if I am mistaken.

 * The user is in a working directory $W.  It is controlled by
   git-tools and there are $W/.git/. directory and $W/.git/index
   dircache.

 * The dircache $W/.git/index started its life as a read-tree
   from some commit.  The git-tools is keeping track of which
   commit it is somewhere, presumably in $W/.git/ directory.
   Let's call it $C (commit).

 ? Question.  Is the $(tree-id) in your example the same as $C
   above?

 * The user have run [*1*] (see Footnote below) checkout-cache
   on $W/.git/index some time in the past and $W is full of
   working files.  Some of them may or may not have modified.
   There may be some additions or deletions.  So the contents of
   the working directory may not match the tree associated with
   $C.

 * The user may or may not have run [*1*] update-cache in $W.
   The contents of the dircache $W/.git/index may not match the
   tree associated with $C.

 ? Question.  Are you forbidding the user to run update-cache by
   hand, and keeping track of the changes yourself, to be
   applied all at once at "git commit" time, thereby
   guaranteeing the $W/.git/index to match the tree associated
   with $C all times?  From the description of The "GIT toolkit"
   section in README, it is not clear to me which part of his
   repository an end user is not supposed to muck with himself.

 * Now the user has some changes in his working directory and
   notices upstream or a side branch has notable changes
   desireble to be picked up.  So he runs some git-tools command
   to cause the above quoted pipeline to run.

 ? Question.  Does $merged in your example mean such an upstream
   or side branch?  Is $base in your example the common ancestor
   between $C and $merged?

Assuming that my above understanding of your model is correct,
here are my "thinking aloud".

 - "merge-trees $base $C $merged" looks only at the git object
   database for those three trees named.  The data structure of
   git object database is optimized to distinguish differences
   in those recorded trees (and hence recorded blobs they point
   at) without unpacking most of the files if the changes are
   small, because all the blobs involved are already hashed.  It
   is not very good at comparing things in git object store and
   working files in random states, which would involve unpacking
   blobs and comparing, so "merge-trees" does not bother.

 - What can come out from merge-trees is therefore one of the
   following for each path from the union of paths contained in
   $base, $C, and $merged:

   (a) Neither $C nor $merged changed it --- merge result is what
       is in $C.

   (b) $C changed it but $merged did not --- merge result is what
       is in $C.

   (c) Both $C and $merged changed it in the same way --- merge
       result is what is in $C.

   (d) $C did not change it but $merged did --- merge result is
       what is in $merged.

   (e) Both $C and $merged changed it differently --- merge is
       needed and automatically succeeds between $C and $merge.

   (f) Both $C and $merged changed it differently --- merge is
       needed but have conflicts.

 - Assuming we are dealing with the case where working files are
   dirty and do not match what is in $C, among the above,
   (a)-(c) can be ignored by SCM.  What the user has in his
   working files is exactly what he would have got if he started
   working from the merge result, although in reality the work
   was started from $C.

   Handling (d), (e) and (f) from SCM's point of view would be
   the same.  They all involve 3-way merges between the file in
   the working directory, and the file from $merged, pivoting on
   the file from $base.  In order to help SCM, merge-trees
   therefore should output SHA1 of blobs for such a file from
   $base and $merged and expect SCM to run "cat-file blob" on
   them and then merge or diff3.  Up to the point of giving
   those two SHA1 out is the business of merge-trees and after
   that it is up to SCM.

   That would work.  So I should base the design of output from
   merge-trees on the above analysis, which probably needs to be
   extended to cover differences between creation, modification,
   and deletion.

 - However, the above is quite different from the way Linus
   envisioned initially, on which my current implementation is
   based [*3*].

   My current implementation is to record the merge outcome in
   the temporary dircache $W/,,merge/.git/index for cases
   (a)-(e).  The last case (f) is problematic and needs human
   validation [*2*], so it is not recorded in that temporary
   dircache, but the files to be merged are left in that
   temporary directory and merge-trees stops there.  It is
   expected that the end-user or SCM would merge the resulting
   file and run update-cache to update $W/,,merge/.git/index.
   After that happens, $W/,,merge/.git/index has the tree
   representing the desired result of the merge.  It is expected
   that the end-user or SCM would write-tree, commit-tree there
   in the temporary directory, creating a new commit $C1.

   Then, it is expected that the SCM would make a patch file
   between $C and the user working directory, checks out $C1
   (either in the user's working directory or another temporary
   directory; at this point merge-trees does not care because it
   has already done its job and exited), applies that patch to
   bring the user edits over to $C1.  Then that directory would
   contain the desired merge of user edits.

   That is my understanding of how Linus originally wanted the
   tool to do his kernel work with to work.  My hesitation to
   suggestions from you to change it not to keep its own merge
   dircache is coming from here.  Not doing what I am currently
   doing to $W/,,merge/.git/index dircache would mean that SCM
   would have to do more, not less, to arrive at $C1 (the result
   of the clean $merge and $C merge pivoted at $base), where the
   real SCM merge begins.

Although I suspect I am misunderstanding what you want, your
messages so far suggest that what you want might be quite
different from what Linus wants.  Please do not misunderstand
what I mean by saying this.  I am not saying that Linus is
always right [*4*] and therefore you are wrong for wanting
something else.  It is just that, if what I started writing
needs to support both of those quite different needs, I need to
know what they are.  I think I understand what Linus wants well
enough [*5*], but I am not certain about yours.


[Footnotes]

*1* By "The user have run" I mean either the user directly used
the low-level plumbing command himself, or used git-tools to
cause such command to run.

*2* Strictly speaking, case (e) needs human validation as
well, because successful textual merge does not guarantee
sensible semantic merge.

*3* See [*R2*] for descriptions on the way Linus wanted merge
in git to happen.  Especially around "5) At this point you need
to MERGE" onwards.  The current implementation handles (or
attempts to handle) the `your working directory was fully
committed' case described there.

*4* According to Linus himself, he is always right ;-). [*R3*]

*5* I consider [*R1*] and [*R2*] essential read for anybody
wanting to understand merging operation in git object model (I
am saying this for others; not for Pasky --- it would be like
preaching to the choir ;-)).


[References]

*R1* <Pine.LNX.4.58.0504110928360.1267@ppc970.osdl.org>
http://marc.theaimsgroup.com/?i=%3CPine.LNX.4.58.0504110928360.1267%20()%20ppc970%20!%20osdl%20!%20org%3E

*R2* <Pine.LNX.4.58.0504121606580.4501@ppc970.osdl.org> 
http://marc.theaimsgroup.com/?i=%3CPine.LNX.4.58.0504121606580.4501%20()%20ppc970%20!%20osdl%20!%20org%3E

*R3*
http://www.uwsg.indiana.edu/hypermail/linux/kernel/0008.3/0555.html


^ permalink raw reply

* Re: Merge with git-pasky II.
From: Junio C Hamano @ 2005-04-15 11:11 UTC (permalink / raw)
  To: Christopher Li, Linus Torvalds; +Cc: Petr Baudis, git
In-Reply-To: <20050415062807.GA29841@64m.dyndns.org>

>>>>> "CL" == Christopher Li <git@chrisli.org> writes:

CL> Then do you emit the entry for it's parents directory?

In GIT object model, directory modes do not matter.  It is not
designed to record directories, and running "update-cache --add
foo" when foo is a directory fails.

The data model of GIT is that it associates file datablob to a
string called "pathname" that happen to contain slashes in them.
It is kinda wierd.  When you externalize it with checkout-cache,
these slashes are mapped to hierarchical UNIX filesystem paths,
relative to whereever you happened to run checkout-cache.  The
hierarchical "tree" representation in the GIT database was
started as just a space optimization thing.

CL> e.g. /foo/bar get created. foo doesn't exists. You have
CL> to create foo first. You don't have mode information for
CL> foo yet.

And you will never have that information, since it is not
recorded anywhere.  If I say you should have foo/bar (by the
way, no leading slashes are placed in the dircache either), and
if it so happens that you do not have foo yet, you'd better
create one without waiting to be told, because I will never tell
you to just create a directory.

By the way, Linus, while I was studying how the new hierarchical
trees are written out, I think I have found one small funny (I
would not call this a *bug*) there.  Here is an excerpt from
write-tree (around ll. 56; I am basing on pasky-0.4 so your line
numbers may have some offsets):

        sha1 = ce->sha1;
        mode = ntohl(ce->st_mode);

        /* Do we have _further_ subdirectories? */
        filename = pathname + baselen;
        dirname = strchr(filename, '/');
        if (dirname) {
                int subdir_written;

                subdir_written = write_tree(cachep + nr, maxentries - nr, pathname, dirname-pathname+1, subdir_sha1);
                nr += subdir_written;

                /* Now we need to write out the directory entry into this tree.. */
                mode |= S_IFDIR;
                pathlen = dirname - pathname;

                /* ..but the directory entry doesn't count towards the total count */
                nr--;
                sha1 = subdir_sha1;
        }

This code is going through a flat list of cache entries sorted
by pathnames.  The list is flat in the sense that the pathnames
are like "foo/bar" i.e. with slashes inside.  The if() statement
there, upon seeing "foo/bar", slurps all the entries in foo/
subhierarchy and writes into a separate tree, recursively, to
"represent" foo/.

Notice what mode the "tree" object gets in this case?  File mode
for foo/bar (or whatever happens to be sorted the first among
the stuff in dircache from foo/ directory) ORed with S_IFDIR.  I
think this is nonsense, and we should just store constant
S_IFDIR.

Another option, probably better from the SCM purist's POV, would
be to start recording directories in dircaches, so that people
can actually keep track of directory modes.  Does it matter? ---
I would say not.  GIT does not have to be tar or cpio.  


^ permalink raw reply

* Re: Git archive now available
From: Kenneth Johansson @ 2005-04-15 11:11 UTC (permalink / raw)
  To: linux-kernel; +Cc: git
In-Reply-To: <20050415000147.GA1480@cse.unsw.EDU.AU>

Darren Williams wrote:
> Hi All
> 
> Thanks to the team at Gelato@UNSW we now have a
> no so complete Git archive at
> http://www.gelato.unsw.edu.au/archives/git/
> 
> If somebody could send me a complete Git mbox I will
> update the archive with it.
> 
>  - dsw 
gmane.org is already archiving this list.

^ permalink raw reply

* [PATCH] trivial argument parsing patches
From: Paul Mackerras @ 2005-04-15 11:28 UTC (permalink / raw)
  To: git

In perusing the git code, I noticed some errors in argument parsing,
which the patch below fixes.  The show-diff error (checking argv[1]
each time around the loop) probably doesn't actually cause any real
problem, but it could be confusing for a novice if "show-diff x"
produces an error but "show-diff -s x" doesn't (and ignores the extra
argument).

Signed-off-by: Paul Mackerras <paulus@samba.org>

rev-tree.c:  7bf9e9a92f528485360f374239809714ce7a19f5
--- rev-tree.c
+++ rev-tree.c	2005-04-15 21:17:16.000000000 +1000
@@ -189,8 +189,8 @@
 		char *arg = argv[i];
 
 		if (!strcmp(arg, "--cache")) {
-			read_cache_file(argv[2]);
 			i++;
+			read_cache_file(argv[i]);
 			continue;
 		}
 
show-diff.c:  a531ca4078525d1c8dcf84aae0bfa89fed6e5d96
--- show-diff.c
+++ show-diff.c	2005-04-15 21:22:28.000000000 +1000
@@ -61,12 +61,10 @@
 	int entries = read_cache();
 	int i;
 
-	while (argc-- > 1) {
-		if (!strcmp(argv[1], "-s")) {
-			silent = 1;
-			continue;
-		}
-		usage("show-diff [-s]");
+	if (argc > 1) {
+		if (argc > 2 || strcmp(argv[1], "-s"))
+			usage("show-diff [-s]");
+		silent = 1;
 	}
 
 	if (entries < 0) {

^ 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