Git development
 help / color / mirror / Atom feed
* Re: More gitweb queries..
From: Thomas Glanzmann @ 2005-05-29 23:56 UTC (permalink / raw)
  To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <20050529234606.GF12290@cip.informatik.uni-erlangen.de>

Hello,
here is the actual version of my merge function. However I have a
question left over: When I do an automatic merge or a threeway merge
should I set the head for the next round to the remote one or just let
it be the current one (this is the current behaviour) or maybe make it
configurable? Note: At the moment the *local* head for the merge-base is only
modified during the set of the first head *or* on a head forward condition. I
have no clue. I am going to write the 'more on merging chapter' after I have
figured this out. ;-)

sub
merge
{

	my $message      = undef;
	my $head         = undef;
	my $last_tree    = undef;
	my $fh;
	my @heads        = ();

	foreach my $r (@_) {
		my $current_head = @{$r}[0];
		my $current_url  = @{$r}[1];

		print "current_head => $current_head\ncurrent_url => $current_url\n";

		push(@heads, '-p', ${current_head});

		if (! defined($last_tree)) {
			$message    = "=> ${current_url}\n";
			$head       = $current_head;
			$last_tree  = $current_head;
			
			if (@_ == 1) {
				head($head);
				return;
			}

			next;
		}

		my $merge_base = gitcmdout('git-merge-base', $head, $current_head)
				 || die ("no merge-base");
		chomp($merge_base);

		print "head => $head\nremote => $current_head\nbase => $merge_base\n";
	
		if ($merge_base eq $current_head) {
			$message .= "<= ${current_url} (nothing to merge)\n";

			$#heads -= 2;

			next;
		}

		if ($merge_base eq $head) {
			$message   .= "<= ${current_url} (bringing head ahead)\n";
			$head       = ${current_head};
			$last_tree  = ${current_head};

			$#heads -= 4;
			push(@heads, '-p', $current_head);

			next;
		}

		gitcmd('git-read-tree', '-m', $merge_base, $last_tree, $current_head);
		if (! defined($last_tree = write_tree())) {
			system('git-merge-cache', '-o', 'git-merge-one-file-script', '-a');
			if (! defined($last_tree = write_tree())) {
				# FIXME: Make manual intervention possible
				# --tg 23:11 05-05-29
				die("Couldn't merge automatically: Call 'git resolve'");
			}
			$message .= "<= ${current_url} (threeway merge)\n";

		} else {
			$message .= "<= ${current_url} (automatic merge)\n";
		}
	}

	if (@heads == 1) {
		head(@head[0]);
		return;
	}

	open($fh, "+>", undef);
	print $fh $message;
	seek($fh, 0, 0);
	$head = gitcmdinout($fh, 'git-commit-tree', $last_tree, @heads);
	chomp($head);
	close $fh;

	head($head);
	return;
}

	Thomas

^ permalink raw reply

* [PATCH 2/3] diff: consolidate various calls into diffcore.
From: Junio C Hamano @ 2005-05-29 23:56 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <7vmzqdiore.fsf_-_@assigned-by-dhcp.cox.net>

The three diff-* brothers had a sequence of calls into diffcore
that were almost identical.  Introduce a new diffcore_std()
function that takes all the necessary arguments to consolidate
it.  This will make later enhancements and changing the order of
diffcore application simpler.

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

diff-cache.c |    9 +++------
diff-files.c |    9 +++------
diff-tree.c  |    7 +++----
diff.c       |   12 ++++++++++++
diff.h       |    4 ++++
5 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/diff-cache.c b/diff-cache.c
--- a/diff-cache.c
+++ b/diff-cache.c
@@ -240,12 +240,9 @@ int main(int argc, const char **argv)
 		die("unable to read tree object %s", tree_name);
 
 	ret = diff_cache(active_cache, active_nr);
-	if (pathspec)
-		diffcore_pathspec(pathspec);
-	if (detect_rename)
-		diffcore_rename(detect_rename, diff_score_opt);
-	if (pickaxe)
-		diffcore_pickaxe(pickaxe, pickaxe_opts);
+	diffcore_std(pathspec,
+		     detect_rename, diff_score_opt,
+		     pickaxe, pickaxe_opts);
 	diff_flush(diff_output_format, 1);
 	return ret;
 }
diff --git a/diff-files.c b/diff-files.c
--- a/diff-files.c
+++ b/diff-files.c
@@ -116,12 +116,9 @@ int main(int argc, const char **argv)
 		show_modified(oldmode, mode, ce->sha1, null_sha1,
 			      ce->name);
 	}
-	if (1 < argc)
-		diffcore_pathspec(argv + 1);
-	if (detect_rename)
-		diffcore_rename(detect_rename, diff_score_opt);
-	if (pickaxe)
-		diffcore_pickaxe(pickaxe, pickaxe_opts);
+	diffcore_std(argv + 1,
+		     detect_rename, diff_score_opt,
+		     pickaxe, pickaxe_opts);
 	diff_flush(diff_output_format, 1);
 	return 0;
 }
diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -261,10 +261,9 @@ static void call_diff_setup(void)
 
 static int call_diff_flush(void)
 {
-	if (detect_rename)
-		diffcore_rename(detect_rename, diff_score_opt);
-	if (pickaxe)
-		diffcore_pickaxe(pickaxe, pickaxe_opts);
+	diffcore_std(0,
+		     detect_rename, diff_score_opt,
+		     pickaxe, pickaxe_opts);
 	if (diff_queue_is_empty()) {
 		diff_flush(DIFF_FORMAT_NO_OUTPUT, 0);
 		return 0;
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -887,6 +887,18 @@ void diff_flush(int diff_output_style, i
 	q->nr = q->alloc = 0;
 }
 
+void diffcore_std(const char **paths,
+		  int detect_rename, int rename_score,
+		  const char *pickaxe, int pickaxe_opts)
+{
+	if (paths && paths[0])
+		diffcore_pathspec(paths);
+	if (detect_rename)
+		diffcore_rename(detect_rename, rename_score);
+	if (pickaxe)
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
+}
+
 void diff_addremove(int addremove, unsigned mode,
 		    const unsigned char *sha1,
 		    const char *base, const char *path)
diff --git a/diff.h b/diff.h
--- a/diff.h
+++ b/diff.h
@@ -43,6 +43,10 @@ extern void diffcore_pickaxe(const char 
 
 extern void diffcore_pathspec(const char **pathspec);
 
+extern void diffcore_std(const char **paths,
+			 int detect_rename, int rename_score,
+			 const char *pickaxe, int pickaxe_opts);
+
 extern int diff_queue_is_empty(void);
 
 #define DIFF_FORMAT_HUMAN	0


^ permalink raw reply

* [PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag.
From: Junio C Hamano @ 2005-05-29 23:54 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <7vmzqdiore.fsf_-_@assigned-by-dhcp.cox.net>

The score number that follow R/C status were parsed but the
parse pointer was not updated, causing the entire line to become
unrecognized.  This patch fixes this problem.

There was a test missing to catch this breakage, which this
commit adds as t4009-diff-rename-4.sh.  The diff-raw tests used
in related t4005-diff-rename-2.sh (the same test without -z) and
t4007-rename-3.sh were stricter than necessarily, despite that
the comment for the tests said otherwise.  This patch also
corrects them.

The documentation is updated to say that the status can
optionally be followed by a number called "score"; it does not
have to stay similarity index forever and there is no reason to
limit it only to C and R.

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

Documentation/diff-format.txt |    2 
diff-helper.c                 |   17 +--
t/t4005-diff-rename-2.sh      |   20 ++--
t/t4007-rename-3.sh           |    2 
t/t4009-diff-rename-4.sh      |  196 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 219 insertions(+), 18 deletions(-)
new file (100755): t/t4009-diff-rename-4.sh

diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -36,7 +36,7 @@ That is, from the left to the right:
   (6) sha1 for "src"; 0{40} if creation or unmerged.
   (7) a space.
   (8) sha1 for "dst"; 0{40} if creation, unmerged or "look at work tree".
-  (9) status, followed by similarlity index number only for C and R.
+  (9) status, followed by optional "score" number.
  (10) a tab or a NUL when '-z' option is used.
  (11) path for "src"
  (12) a tab or a NUL when '-z' option is used; only exists for C or R.
diff --git a/diff-helper.c b/diff-helper.c
--- a/diff-helper.c
+++ b/diff-helper.c
@@ -80,17 +80,16 @@ int main(int ac, const char **av) {
 			if (!strchr("MCRNDU", status))
 				break;
 			two_paths = score = 0;
-			if (status == 'R' || status == 'C') {
+			if (status == 'R' || status == 'C')
 				two_paths = 1;
-				sscanf(cp, "%d", &score);
-				if (line_termination) {
-					cp = strchr(cp,
-						    inter_name_termination);
-					if (!cp)
-						break;
-				}
-			}
 
+			/* pick up score if exists */
+			if (sscanf(cp, "%d", &score) != 1)
+				score = 0;
+			cp = strchr(cp,
+				    inter_name_termination);
+			if (!cp)
+				break;
 			if (*cp++ != inter_name_termination)
 				break;
 
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -8,11 +8,17 @@ test_description='Same rename detection 
 '
 . ./test-lib.sh
 
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+sanitize_diff_raw='s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*	/ X X \1#	/'
 compare_diff_raw () {
     # When heuristics are improved, the score numbers would change.
     # Ignore them while comparing.
-    sed -e 's/ \([CR]\)[0-9]*	/\1#/' <"$1" >.tmp-1
-    sed -e 's/ \([CR]\)[0-9]*	/\1#/' <"$2" >.tmp-2
+    # Also we do not check SHA1 hash generation in this test, which
+    # is a job for t0000-basic.sh
+
+    sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
+    sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
     diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
 }
 
@@ -109,11 +115,6 @@ test_expect_success \
     'validate output from rename/copy detection (#2)' \
     'compare_diff_raw current expected'
 
-test_expect_success \
-    'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
-     git-update-cache --add --remove COPYING COPYING.1'
-
 # make sure diff-helper can grok it.
 mv expected diff-raw
 GIT_DIFF_OPTS=--unified=0 git-diff-helper <diff-raw >current
@@ -151,6 +152,11 @@ test_expect_success \
 # anything about rezrov nor COPYING, since the revised again diff-raw
 # nows how to say Copy.
 
+test_expect_success \
+    'prepare work tree once again' \
+    'cat ../../COPYING >COPYING &&
+     git-update-cache --add --remove COPYING COPYING.1'
+
 git-diff-cache -C $tree >current
 cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234	COPYING	COPYING.1
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -10,7 +10,7 @@ test_description='Rename interaction wit
 
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-sanitize_diff_raw='s/ \('"$_x40"'\) \1 \([CR]\)[0-9]*	/ \1 \1 \2#	/'
+sanitize_diff_raw='s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*	/ X X \1#	/'
 compare_diff_raw () {
     # When heuristics are improved, the score numbers would change.
     # Ignore them while comparing.
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
new file mode 100755
--- /dev/null
+++ b/t/t4009-diff-rename-4.sh
@@ -0,0 +1,196 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Same rename detection as t4003 but testing diff-raw -z.
+
+'
+. ./test-lib.sh
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+sanitize_diff_raw='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*$/ X X \1#/'
+compare_diff_raw () {
+    # When heuristics are improved, the score numbers would change.
+    # Ignore them while comparing.
+    # Also we do not check SHA1 hash generation in this test, which
+    # is a job for t0000-basic.sh
+
+    tr '\0' '\012' <"$1" | sed -e "$sanitize_diff_raw" >.tmp-1
+    tr '\0' '\012' <"$2" | sed -e "$sanitize_diff_raw" >.tmp-2
+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+compare_diff_patch () {
+    # When heuristics are improved, the score numbers would change.
+    # Ignore them while comparing.
+    sed -e '/^similarity index [0-9]*%$/d' <"$1" >.tmp-1
+    sed -e '/^similarity index [0-9]*%$/d' <"$2" >.tmp-2
+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+test_expect_success \
+    'prepare reference tree' \
+    'cat ../../COPYING >COPYING &&
+     echo frotz >rezrov &&
+    git-update-cache --add COPYING rezrov &&
+    tree=$(git-write-tree) &&
+    echo $tree'
+
+test_expect_success \
+    'prepare work tree' \
+    'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+    sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+    rm -f COPYING &&
+    git-update-cache --add --remove COPYING COPYING.?'
+
+# tree has COPYING and rezrov.  work tree has COPYING.1 and COPYING.2,
+# both are slightly edited, and unchanged rezrov.  We say COPYING.1
+# and COPYING.2 are based on COPYING, and do not say anything about
+# rezrov.
+
+git-diff-cache -z -M $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234
+COPYING
+COPYING.2
+EOF
+
+test_expect_success \
+    'validate output from rename/copy detection (#1)' \
+    'compare_diff_raw current expected'
+
+# make sure diff-helper can grok it.
+mv current diff-raw
+GIT_DIFF_OPTS=--unified=0 git-diff-helper -z <diff-raw >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+diff --git a/COPYING b/COPYING.2
+rename old COPYING
+rename new COPYING.2
+--- a/COPYING
++++ b/COPYING.2
+@@ -2 +2 @@
+- Note that the only valid version of the GPL as far as this project
++ Note that the only valid version of the G.P.L as far as this project
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ HOWEVER, in order to allow a migration to G.P.Lv3 if that seems like
+@@ -12 +12 @@
+-	This file is licensed under the GPL v2, or a later version
++	This file is licensed under the G.P.L v2, or a later version
+EOF
+
+test_expect_success \
+    'validate output from diff-helper (#1)' \
+    'compare_diff_patch current expected'
+
+################################################################
+
+test_expect_success \
+    'prepare work tree again' \
+    'mv COPYING.2 COPYING &&
+     git-update-cache --add --remove COPYING COPYING.1 COPYING.2'
+
+# tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
+# both are slightly edited, and unchanged rezrov.  We say COPYING.1
+# is based on COPYING and COPYING is still there, and do not say anything
+# about rezrov.
+
+git-diff-cache -z -C $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M
+COPYING
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+EOF
+
+test_expect_success \
+    'validate output from rename/copy detection (#2)' \
+    'compare_diff_raw current expected'
+
+# make sure diff-helper can grok it.
+mv current diff-raw
+GIT_DIFF_OPTS=--unified=0 git-diff-helper -z <diff-raw >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING
+--- a/COPYING
++++ b/COPYING
+@@ -2 +2 @@
+- Note that the only valid version of the GPL as far as this project
++ Note that the only valid version of the G.P.L as far as this project
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ HOWEVER, in order to allow a migration to G.P.Lv3 if that seems like
+@@ -12 +12 @@
+-	This file is licensed under the GPL v2, or a later version
++	This file is licensed under the G.P.L v2, or a later version
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+EOF
+
+test_expect_success \
+    'validate output from diff-helper (#2)' \
+    'compare_diff_patch current expected'
+
+################################################################
+
+# tree has COPYING and rezrov.  work tree has the same COPYING and
+# copy-edited COPYING.1, and unchanged rezrov.  We should not say
+# anything about rezrov nor COPYING, since the revised again diff-raw
+# nows how to say Copy.
+
+test_expect_success \
+    'prepare work tree once again' \
+    'cat ../../COPYING >COPYING &&
+     git-update-cache --add --remove COPYING COPYING.1'
+
+git-diff-cache -z -C $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+EOF
+
+test_expect_success \
+    'validate output from rename/copy detection (#3)' \
+    'compare_diff_raw current expected'
+
+# make sure diff-helper can grok it.
+mv current diff-raw
+GIT_DIFF_OPTS=--unified=0 git-diff-helper -z <diff-raw >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+EOF
+
+test_expect_success \
+    'validate output from diff-helper (#3)' \
+    'compare_diff_patch current expected'
+
+test_done
------------------------------------------------


^ permalink raw reply

* Re: [PATCH] Add -B flag to diff-* brothers.
From: Junio C Hamano @ 2005-05-29 23:51 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Linus Torvalds, Git Mailing List
In-Reply-To: <20050529215617.GT1036@pasky.ji.cz>

>>>>> "PB" == Petr Baudis <pasky@ucw.cz> writes:

PB> Actually, I like this one - contrary to -O I can see how this could be
PB> quite useful - I have wished for this many times when people would send
PB> me some "complete rewrite" patches which I actually wanted to review.

PB> Please don't give up on it. :-)

Well, although I do not do Porcelain ;-), I do want to have -O
to help my use pattern.  I envision that Porcelain noticing the
existence of ${GIT-.git}/patch-order file and adding -O to its
diff-* argument would make the world a better place.

And I am not giving up on -B yet, but as you can imagine, it has
interesting interaction with -M/-C, since rename detection code
must be prepared to deal with broken pairs (earlier it did not
have to worry about the same path that is a regular file
appearing twice in its input).  Since I have tested -B only in
the context of full set of patches I sent out, and have not
tested it with only the subset Linus decided to swallow, I would
recommend against applying it on top of the Linus tip as is.
I'll be rebasing it to his tip, test it again and then resubmit
later.  I'll do the same for -O changes.


^ permalink raw reply

* Re: [COGITO PATCH] Fix cg-log and cg-status for non-GNU sed/sort
From: Petr Baudis @ 2005-05-29 23:49 UTC (permalink / raw)
  To: Mark Allen; +Cc: git
In-Reply-To: <20050529234326.12407.qmail@web41203.mail.yahoo.com>

Dear diary, on Mon, May 30, 2005 at 01:43:26AM CEST, I got a letter
where Mark Allen <mrallen1@yahoo.com> told me that...
> --- Petr Baudis <pasky@ucw.cz> wrote:
> > Dear diary, on Mon, May 30, 2005 at 01:00:10AM CEST, I got a letter
> > where Mark Allen <mrallen1@yahoo.com> told me that...
> > > -                               pdate="$(showdate $date)"
> > > +                               pdate="$(date -u -r $date)"
> > 
> > Hmm, coudlnt' showdate be fixed instead then? And $date is not a file so
> > -r $date makes no sense to me - what am I missing?
> 
> There is no showdate(1) command on Darwin.  On Darwin, the date(1) command flags are

There's probably no showdate command at all. This is internal function
defined in cg-Xlib. ;-)

> -u (show UTC)
> -r seconds 
> 
> So `date -u -r $date` gives output like "Sun May 29 12:07:55 GMT 2005" instead of
> 1117336545

GNU date:

  -r, --reference=FILE      display the last modification time of FILE

> > > -                               echo; sed -re '
> > > -                                       /
> > *Signed-off-by:.*/Is//'$colsignoff'&'$coldefault'/
> > > -                                       /
> > *Acked-by:.*/Is//'$colsignoff'&'$coldefault'/
> > > -                                       s/./    &/
> > > -                               '
> > > +                               echo; sed -e "/ *Signed-off-by:.*/s/
> > *Signed-off-by:.*/$colsignoff&$coldefault/" -e"/
> > > +*Acked-by:.*/s/ *Acked-by:.*/$colsignoff&$coldefault/" -e "s/./    &/"
> > > +
> > 
> > Is it necessary to take away the newlines? What is the real problem,
> > actually? Just the I flag?
> 
> Is it strictly necessary to take away the newlines? I don't know. To be honest, I didn't 
> experiment too much with alternate implementations. But the I flag in the sed script, 
> yes that is not understood by non-GNU sed. And the "-r" to sed itself is also
> problematic.

I think we don't really need -r anyway. Could you please try with the
'i' flag instead of 'I'? (The manpage is somewhat unclear on the
difference and portability.) Also, could you try if it works with the
newlines?

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

^ permalink raw reply

* Re: More gitweb queries..
From: Thomas Glanzmann @ 2005-05-29 23:46 UTC (permalink / raw)
  To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <20050529231621.GE12290@cip.informatik.uni-erlangen.de>

Hello,
yet another bug. If there is only one head left after the for loop this
head is *set* not *commitet* as new HEAD because it comes from a 'pull
into empty tree' or from a 'just head ahead'-condition.

	Thomas

^ permalink raw reply

* Re: [COGITO PATCH] Fix cg-log and cg-status for non-GNU sed/sort
From: Mark Allen @ 2005-05-29 23:43 UTC (permalink / raw)
  To: Petr Baudis; +Cc: git
In-Reply-To: <20050529231652.GX1036@pasky.ji.cz>

--- Petr Baudis <pasky@ucw.cz> wrote:
> Dear diary, on Mon, May 30, 2005 at 01:00:10AM CEST, I got a letter
> where Mark Allen <mrallen1@yahoo.com> told me that...
> > -                               pdate="$(showdate $date)"
> > +                               pdate="$(date -u -r $date)"
> 
> Hmm, coudlnt' showdate be fixed instead then? And $date is not a file so
> -r $date makes no sense to me - what am I missing?

There is no showdate(1) command on Darwin.  On Darwin, the date(1) command flags are

-u (show UTC)
-r seconds 

So `date -u -r $date` gives output like "Sun May 29 12:07:55 GMT 2005" instead of
1117336545

> I'm lost on this one too. Why do you introduce the -n?

Since I used date(1) above, I get an extra newline now when the date string is printed
out. Using echo -n on the second echo stops that behavior. (This is perhaps a matter of 
taste; some people like lots of whitespace in headers, and some don't.)

> > -                               echo; sed -re '
> > -                                       /
> *Signed-off-by:.*/Is//'$colsignoff'&'$coldefault'/
> > -                                       /
> *Acked-by:.*/Is//'$colsignoff'&'$coldefault'/
> > -                                       s/./    &/
> > -                               '
> > +                               echo; sed -e "/ *Signed-off-by:.*/s/
> *Signed-off-by:.*/$colsignoff&$coldefault/" -e"/
> > +*Acked-by:.*/s/ *Acked-by:.*/$colsignoff&$coldefault/" -e "s/./    &/"
> > +
> 
> Is it necessary to take away the newlines? What is the real problem,
> actually? Just the I flag?

Is it strictly necessary to take away the newlines? I don't know. To be honest, I didn't 
experiment too much with alternate implementations. But the I flag in the sed script, 
yes that is not understood by non-GNU sed. And the "-r" to sed itself is also
problematic.

> Could you please sign the patch off, and send it as text/plain or inline the
> message body?


Modify cg-log and cg-status to work with non-GNU sed, sort and date

Signed-off-by: Mark Allen <mrallen1@yahoo.com>
---
commit 7e8fbf87acead0ee83afc4647228efdad0bbae2e
tree 50acf8ce23fee4ca6c9493b5f5959093e7579c74
parent d36ed1fd011d349ea2adc00456750b5b3da3a9cd
author Mark Allen <mallen@aeris.local> 1117409841 -0500
committer Mark Allen <mallen@aeris.local> 1117409841 -0500

 cg-log    |   15 +++++++--------
 cg-status |    2 +-
 2 files changed, 8 insertions(+), 9 deletions(-)

Index: cg-log
===================================================================
--- 3a65f5fb53367c75f4f76aebed27a83d8679f068/cg-log  (mode:100755)
+++ 50acf8ce23fee4ca6c9493b5f5959093e7579c74/cg-log  (mode:100755)
@@ -148,10 +148,10 @@
                                fi
 
                                date=(${rest#*> })
-                               pdate="$(showdate $date)"
+                               pdate="$(date -u -r $date)"
                                if [ "$pdate" ]; then
-                                       echo -n $color$key $rest | sed "s/>.*/> $pdate/"
-                                       echo $coldefault
+                                       echo -n $color$key $rest | sed "s/>.*/> $pdate/" 
+                                       echo -n $coldefault
                                else
                                        echo $color$key $rest $coldefault
                                fi
@@ -168,11 +168,10 @@
                                if [ -n "$list_files" ]; then
                                        list_commit_files "$tree1" "$tree2"
                                fi
-                               echo; sed -re '
-                                       /
*Signed-off-by:.*/Is//'$colsignoff'&'$coldefault'/
-                                       / *Acked-by:.*/Is//'$colsignoff'&'$coldefault'/
-                                       s/./    &/
-                               '
+                               echo; sed -e "/ *Signed-off-by:.*/s/
*Signed-off-by:.*/$colsignoff&$coldefault/" 
+                               -e"/ *Acked-by:.*/s/
*Acked-by:.*/$colsignoff&$coldefault/" 
+                               -e "s/./    &/"
+
                                ;;
                        *)
                                echo $colheader$key $rest $coldefault
Index: cg-status
===================================================================
--- 3a65f5fb53367c75f4f76aebed27a83d8679f068/cg-status  (mode:100755)
+++ 50acf8ce23fee4ca6c9493b5f5959093e7579c74/cg-status  (mode:100755)
@@ -15,7 +15,7 @@
 
 {
        git-ls-files -z -t --others --deleted --unmerged $EXCLUDE
-} | sort -z -k 2 | xargs -0 sh -c '
+} | sort -t '\0' -k 2 | xargs -0 sh -c '
 while [ "$1" ]; do
        tag=${1% *};
        filename=${1#* };

^ permalink raw reply

* [PATCH 0/3] Leftover bits after 12-series
From: Junio C Hamano @ 2005-05-29 23:41 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <7v3bs5k8d1.fsf@assigned-by-dhcp.cox.net>

>>>>> "JCH" == Junio C Hamano <junkio@cox.net> writes:

JCH> I'm taking a look at the resulting tree to see if there are
JCH> fixes and cleanups that I still think should be merged.  I'll
JCH> feed them afresh to you later, if there are any, after rebasing
JCH> the patch to the tip of your tree.

I'll be submitting a set of cleanup and bugfix patches.  The
first one is a real bugfix.  The latter two are cleanups.

    [PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag.
    [PATCH 2/3] diff: consolidate various calls into diffcore.
    [PATCH 3/3] diff: code clean-up.



^ permalink raw reply

* Re: Problem with cg-diff <file>
From: Petr Baudis @ 2005-05-29 23:38 UTC (permalink / raw)
  To: Marcel Holtmann, Junio C Hamano; +Cc: GIT Mailing List
In-Reply-To: <1117408555.7072.109.camel@pegasus>

Dear diary, on Mon, May 30, 2005 at 01:15:55AM CEST, I got a letter
where Marcel Holtmann <marcel@holtmann.org> told me that...
> Hi Petr,

Hi,

> your latest changes are breaking the cg-diff <file> functionality. Now
> cg-diff creates a diff against all local not committed changes.

oops, thanks for the report. Hopefully fixed and pushed out.

Eek, apparently not entirely fixed yet. git-diff-tree (in contrast to
git-diff-cache) won't take the pathspec as its trailing arguments,
causing cg-diff -r a:b still not to work 100% correct.

Junio, is there any specific reason for that, or is the end of
git-diff-tree argument list the right spot for the pathspec stuff?

Thanks,

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

^ permalink raw reply

* Re: [COGITO PATCH] Fix cg-log and cg-status for non-GNU sed/sort
From: Petr Baudis @ 2005-05-29 23:16 UTC (permalink / raw)
  To: Mark Allen; +Cc: git
In-Reply-To: <20050529230011.30885.qmail@web41201.mail.yahoo.com>

Dear diary, on Mon, May 30, 2005 at 01:00:10AM CEST, I got a letter
where Mark Allen <mrallen1@yahoo.com> told me that...
> Here're a couple of pretty simple patches against cg-log and cg-status (two cogito
> commands I use a lot) for the non-GNU (or at least older, forked GNU) tools on Darwin.

> @@ -148,10 +148,10 @@
>                                 fi
> 
>                                 date=(${rest#*> })
> -                               pdate="$(showdate $date)"
> +                               pdate="$(date -u -r $date)"

Hmm, coudlnt' showdate be fixed instead then? And $date is not a file so
-r $date makes no sense to me - what am I missing?

>                                 if [ "$pdate" ]; then
> -                                       echo -n $color$key $rest | sed "s/>.*/> $pdate/"
> -                                       echo $coldefault
> +                                       echo -n $color$key $rest | sed "s/>.*/> $pdate/"
> +                                       echo -n $coldefault

I'm lost on this one too. Why do you introduce the -n?

>                                 else
>                                         echo $color$key $rest $coldefault
>                                 fi
> @@ -168,11 +168,8 @@
>                                 if [ -n "$list_files" ]; then
>                                         list_commit_files "$tree1" "$tree2"
>                                 fi
> -                               echo; sed -re '
> -                                       / *Signed-off-by:.*/Is//'$colsignoff'&'$coldefault'/
> -                                       / *Acked-by:.*/Is//'$colsignoff'&'$coldefault'/
> -                                       s/./    &/
> -                               '
> +                               echo; sed -e "/ *Signed-off-by:.*/s/ *Signed-off-by:.*/$colsignoff&$coldefault/" -e"/
> +*Acked-by:.*/s/ *Acked-by:.*/$colsignoff&$coldefault/" -e "s/./    &/"
> +

Is it necessary to take away the newlines? What is the real problem,
actually? Just the I flag?

Could you please sign the patch off, and send it as text/plain or inline the
message body?

Thanks,

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

^ permalink raw reply

* Re: More gitweb queries..
From: Thomas Glanzmann @ 2005-05-29 23:16 UTC (permalink / raw)
  To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <20050529231053.GD12290@cip.informatik.uni-erlangen.de>

Hello,

> > 			$message .= "<= ${current_url} (nothing to merge)\n";
> > 			$message   .= "<= ${current_url} (bringing head ahead)\n";

> the parents of such 'merges' should *not* be referenced in the commit
> object E.g. this is wrong: 'git-commit-tree -p nothing_to_merge -p
> just_bringing_head_ahead'. Leaving the message in the log section of the
> commit object could be useful, maybe. Thoughts?

Wrong, wrong, wrong. If 'head ahead' condition shows up, I have to kick
the previous head out (not the current), don't I?

	Thomas

^ permalink raw reply

* Problem with cg-diff <file>
From: Marcel Holtmann @ 2005-05-29 23:15 UTC (permalink / raw)
  To: Petr Baudis; +Cc: GIT Mailing List

Hi Petr,

your latest changes are breaking the cg-diff <file> functionality. Now
cg-diff creates a diff against all local not committed changes.

Regards

Marcel



^ permalink raw reply

* Re: More gitweb queries..
From: Thomas Glanzmann @ 2005-05-29 23:10 UTC (permalink / raw)
  To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <20050529230240.GB12290@cip.informatik.uni-erlangen.de>

Hello,
one obvious fix:

> 			$message .= "<= ${current_url} (nothing to merge)\n";
> 			$message   .= "<= ${current_url} (bringing head ahead)\n";

the parents of such 'merges' should *not* be referenced in the commit
object E.g. this is wrong: 'git-commit-tree -p nothing_to_merge -p
just_bringing_head_ahead'. Leaving the message in the log section of the
commit object could be useful, maybe. Thoughts?

And I should doublecheck if more than 16 parents show up in
git-commit-tree when doing the commits and throw an error.

	Thomas

^ permalink raw reply

* Re: More gitweb queries..
From: Thomas Glanzmann @ 2005-05-29 23:02 UTC (permalink / raw)
  To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <20050527203227.GA11139@cip.informatik.uni-erlangen.de>

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

Hello,
here we go!  gitk screenshot:
http://wwwcip.informatik.uni-erlangen.de/~sithglan/shot.png (64k)

(faui00u) [~/work/mutt/git/mutt-test] ../../../git/yagf/git pull ../mutt-attach-file/ ../mutt-collapse-flags/ ../mutt-cstatus/ ../mutt-cvs/ ../mutt-edit-threads/ ../mutt-hcache/ ../mutt-headers/ ../mutt-imap/ ../mutt-maildir-mtime/ ../mutt-move-hook/ ../mutt-setenv-hack/ ../mutt-thread-pattern/ ../mutt-menu-move/
<chatty output>
(faui00u) [~/work/mutt/git/mutt-test] git treediff ../mutt-tg-solaris
(faui00u) [~/work/mutt/git/mutt-test] git changes -m | perl -pe '$a += /^diff-tree/; exit if $a==2'
diff-tree 5b22da9792b6f6a968dc0a916275d6c301575f75 (from e8f4a291a81f0a8fb24555f0e36e4b75e2d3f4c8)
Author: Thomas Glanzmann <sithglan@stud.uni-erlangen.de>
Date:   Mon May 30 00:28:53 2005 +0200

    => faui00u:/home/cip/adm/sithglan/work/mutt/git/mutt-test
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-attach-file/.git (bringing head ahead)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-collapse-flags/.git (automatic merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-cstatus/.git (threeway merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-cvs/.git (nothing to merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-edit-threads/.git (threeway merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-hcache/.git (threeway merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-headers/.git (threeway merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-imap/.git (automatic merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-maildir-mtime/.git (threeway merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-move-hook/.git (nothing to merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-setenv-hack/.git (automatic merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-thread-pattern/.git (threeway merge)
    <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-menu-move/.git (threeway merge)

Could please someone who has a clue how this merge should work (which it
hopefully already does) look at my code to check for obvious mistakes in
the merge *logic*.  I call 'merge' with all involved trees (with current
head first, if there is a current head). If everything is fine. I would
like to write a seperate git-resolve script in sh or adopt the current
to multi head merge - whatever you please.

sub
merge
{

	my $message      = undef;
	my $head         = undef;
	my $last_tree    = undef;
	my $fh;
	my @heads        = ();

	foreach my $r (@_) {
		my $current_head = @{$r}[0];
		my $current_url  = @{$r}[1];

		print "current_head => $current_head\ncurrent_url => $current_url\n";

		push(@heads, '-p', ${current_head});

		if (! defined($last_tree)) {
			$message    = "=> ${current_url}\n";
			$head       = $current_head;
			$last_tree  = $current_head;
			
			if (@_ == 1) {
				head($head);
				return;
			}

			next;
		}

		my $merge_base = gitcmdout('git-merge-base', $head, $current_head)
				 || die ("no merge-base");
		chomp($merge_base);

		print "head => $head\nremote => $current_head\nbase => $merge_base\n";
	
		if ($merge_base eq $current_head) {
			$message .= "<= ${current_url} (nothing to merge)\n";

			if (@_ == 2) {
				return;
			}

			next;
		}

		if ($merge_base eq $head) {
			$message   .= "<= ${current_url} (bringing head ahead)\n";
			$head       = ${current_head};
			$last_tree  = ${current_head};

			if (@_ == 2) {
				head($head);
				return;
			}

			next;
		}

		gitcmd('git-read-tree', '-m', $merge_base, $last_tree, $current_head);
		if (! defined($last_tree = write_tree())) {
			system('git-merge-cache', '-o', 'git-merge-one-file-script', '-a');
			if (! defined($last_tree = write_tree())) {
				# FIXME: Make manual intervention possible
				# --tg 23:11 05-05-29
				die("Couldn't merge automatically: Call 'git resolve'");
			}
			$message .= "<= ${current_url} (threeway merge)\n";

		} else {
			$message .= "<= ${current_url} (automatic merge)\n";
		}
	}

	open($fh, "+>", undef);
	print $fh $message;
	seek($fh, 0, 0);
	$head = gitcmdinout($fh, 'git-commit-tree', $last_tree, @heads);
	chomp($head);
	close $fh;

	head($head);
	return;
}

	Thomas

[-- Attachment #2: git --]
[-- Type: text/plain, Size: 25336 bytes --]

#!/usr/bin/env perl

use strict;
use warnings;
use IO::Handle;
use File::Temp qw/ tempfile tempdir /;
use File::Copy;
use Cwd;
use Getopt::Long;

STDOUT->autoflush(1);

my $hostname = gitcmdout('hostname');
chomp($hostname);

my $DIFF  = undef;
my $PATCH = undef;

if (-x '/opt/csw/bin/gdiff') {
	$DIFF='/opt/csw/bin/gdiff';

} else {
	$DIFF='diff';
}

if (-x '/usr/bin/gpatch') {
	$PATCH='/usr/bin/gpatch';

} else {
	$PATCH='patch';
}

my %commands = (
	"add"        => \&add,
	"checkout"   => \&checkout,
	"ci"         => \&ci,
	"clone"      => \&clone,
	"commit"     => \&commit,
	"diff"       => \&diff,
	"treediff"   => \&treediff,
	"dirty"      => \&dirty,
	"help"       => \&usage,
	"init-db"    => \&init_db,
	"log"        => \&log,
	"orphan"     => \&orphan,
	"parent"     => \&parent,
	"patch"      => \&patch,
	"pull"       => \&pull,
	"push"       => \&_push,
	"refresh"    => \&refresh,
	"revert"     => \&revert,
	"rm"         => \&rm,
	"setup"      => \&setup,
	"status"     => \&status,
	"undo"       => \&undo,
	"changed"    => \&changed,
	"resolve"    => \&resolve,
	"changes"    => \&changes,
);

my %touched = ();

sub
usage
{
	print STDERR <<"__EOF__";
Usage: $0 COMMAND [ARG]...

Available commands:

__EOF__

	print "\t" . join("\n\t", sort(keys(%commands))) . "\n\n";

	return 1;
}

sub
refresh
{
	`git-update-cache --refresh`;
}

sub
process_git_diff_output
{
	my $str = shift || return (());

	my @in  = split("\0", $str);
	my @out = ();

	while (@in) {
		my @tmp = split(' ', shift(@in));
		$tmp[0] =~ s/^://g;
		push(@tmp, shift(@in));
		push(@out, [@tmp]);
	}

	return(@out);
}

sub
dirty_files
{
	refresh();

	my @dirty = ();
	my $str = gitcmdout('git-diff-files', '-z', '-r');

	foreach (process_git_diff_output($str)) {
		if ((@{$_})[1] ne '000000') {
			push(@dirty, @{$_}[5]);
		}
		#print "<" . join("> <", @{$_}) . ">\n";
	}

	return @dirty;
}

sub
orphan_files
{
	my @orphan = gitcmdout('git-ls-files', '--others');
	chomp(@orphan);

	my $regexp = '^\.';
	if (-f '.git/ignore') {
		my @ignore = ();
		chomp (@ignore = _read_file('.git/ignore'));
		$regexp = join('|', '^\.', @ignore);
	}

	@orphan = grep(! /$regexp/, @orphan);

	return @orphan;
}

sub
orphan
{
	foreach (orphan_files()) {
		print $_ . "\n";
	}
}

sub
dirty
{
	foreach (dirty_files()) {
		print $_ . "\n";
	}
}

sub
changed
{
	foreach my $rev (gitcmdout('git-rev-list', 'HEAD')) {
		chomp($rev);
		my %hash = commit_hash($rev);
		
		my $str = gitcmdout('git-diff-tree', '--root', '-r', '-z', $rev);
		foreach (process_git_diff_output($str)) {
			my ($time, $rest) = split(/\s/, $hash{committer_date});
			push(@{$touched{@{$_}[5]}}, $time);
		}
	}
}

sub
status
{
	my %hash = ();
	
	foreach (orphan_files()) {
		$hash{$_} = '?';
	}

	foreach (dirty_files()) {
		$hash{$_} = 'D';
	}

	my $str = gitcmdout('git-diff-cache', '-r', '--cached', '-z', 'HEAD');
	foreach (process_git_diff_output($str)) {
		if      (@{$_}[0] eq '000000') {
			$hash{@{$_}[5]} .= '+';

		} elsif (@{$_}[1] eq '000000') {
			$hash{@{$_}[5]} .= '-';

		} else {
			$hash{@{$_}[5]} .= '*';
		}
	}

	foreach (sort(keys(%hash))) {
		if ($hash{$_} eq "D") {
			$hash{$_} = "D ";
		}
		printf("% 2s %s\n", $hash{$_}, $_);
	}
}

sub
write_tree
{
	chomp (my $tree = `git-write-tree`);
	if ($?) {
		return undef;
	} else {
		return $tree;
	}
}

sub
retrieve_unmerged
{
	my %hash = ();
	
	foreach my $line (gitcmdout('git-ls-files', '--unmerged')) {
		chomp($line);
# 100644 cde27275fad8103084d7ed2d08d246ba4ce6eb9c 1 Makefile
# 100644 d311a35e5e8a09629ea9e6051a43710c76fa8f6d 2 Makefile
# 100644 ec2b76bf90fb105b2aaf00a66f44b135046d3002 3 Makefile
		if ($line =~ /^(\d{6})\s([a-z0-9]{40})\s(\d)\s(.+)$/) {
			push(@{$hash{$4}}, $line);
		} else {
			die("Can't match: <$line>\n");
		}
	}

	return %hash;
}

sub
process_unmerged_file
{
	my $line;
	my $file;
	my @files = ();
	while($line = shift) {
		if ($line =~ /^(\d{6})\s([a-z0-9]{40})\s(\d)\s(.+)$/) {
			if ($3 eq '1') { $file = "$4.GCA"; }
			if ($3 eq '2') { $file = "$4.LOCAL"; }
			if ($3 eq '3') { $file = "$4.REMOTE"; }

			my $mode = substr($1, 2);

			if (-f $file) {
				die("Please get rid of $file\n");
			}

			print STDERR "Checking out: $file with permissions $mode\n";

			system("git-cat-file blob $2 > $file");
			chmod oct($mode), $file;
			push(@files, $file);
		}
	}

	if (@files == 3) {
		my $filename = (@files)[0];
		$filename =~ s#\.(GCA|LOCAL|REMOTE)$##;

		print STDERR <<"EOF";

You got GCA, LOCAL and REMOTE, so I run merge for you and leaving the merges in
${filename} .\n

EOF

		unlink($filename);
		system('cp', "${filename}.LOCAL", "${filename}");

		system('merge', "${filename}", "${filename}.GCA", "${filename}.REMOTE");
	}

	print STDERR <<"EOF";

Please resolve the conflict and add the file using the command 'git ci file'.
Droping you to a login shell now. Please exit the shell as soon as you resolved
the conflict.

EOF

	system($ENV{'SHELL'}, '--login');

	foreach (@files) {
		unlink($_);
	}
}

sub
resolve
{
	if (! -f '.git/RESOLVE') {
		die("Nothing to resolve");
	}
	if ( ! -f '.git/HEAD' && ! -l '.git/HEAD') {
		die("How the hell I am supposed to resolve without a head?");
	}

	my $fh;
	my $head = head();
	my $merge_tree = undef;
	my $pwd        = getcwd;
	chomp (my ($remote_head, $url) = _read_file('.git/RESOLVE'));

	print "remote_head => $remote_head\nurl => $url\n";

	my %unmerged = retrieve_unmerged();

	foreach my $file (keys(%unmerged)) {
		process_unmerged_file(@{$unmerged{$file}});
	}

	if (! defined($merge_tree = write_tree())) {
		die("Still unresolved conflicts. Run git resolve again.");
	}

	open($fh, "+>", undef);
	print $fh "Manual Merge $url => ${hostname}:${pwd}\n";
	seek($fh, 0, 0);
	chomp($head = gitcmdinout($fh, 'git-commit-tree', $merge_tree, '-p', $head, '-p', $remote_head));
	close $fh;

	head($head);
	unlink('.git/RESOLVE');
	checkout('-f');

	print STDERR "All issues resolved manual: Commited as ${head}.\n";
        print STDERR "Have a pleasant day.\n";
	return;
}

sub
merge
{

	my $message      = undef;
	my $head         = undef;
	my $last_tree    = undef;
	my $fh;
	my @heads        = ();

	foreach my $r (@_) {
		my $current_head = @{$r}[0];
		my $current_url  = @{$r}[1];

		print "current_head => $current_head\ncurrent_url => $current_url\n";

		push(@heads, '-p', ${current_head});

		if (! defined($last_tree)) {
			$message    = "=> ${current_url}\n";
			$head       = $current_head;
			$last_tree  = $current_head;
			

			if (@_ == 1) {
				print "Setting HEAD\n";
				head($head);
				return;
			}

			print "Next round\n";

			next;
		}

		my $merge_base = gitcmdout('git-merge-base', $head, $current_head)
				 || die ("no merge-base");
		chomp($merge_base);

		print "head => $head\nremote => $current_head\nbase => $merge_base\n";
	
		if ($merge_base eq $current_head) {
			$message .= "<= ${current_url} (nothing to merge)\n";

			if (@_ == 2) {
				return;
			}

			next;
		}

		if ($merge_base eq $head) {
			$message   .= "<= ${current_url} (bringing head ahead)\n";
			$head       = ${current_head};
			$last_tree  = ${current_head};

			if (@_ == 2) {
				head($head);
				return;
			}

			next;
		}

		gitcmd('git-read-tree', '-m', $merge_base, $last_tree, $current_head);
		if (! defined($last_tree = write_tree())) {
			system('git-merge-cache', '-o', 'git-merge-one-file-script', '-a');
			if (! defined($last_tree = write_tree())) {
				# FIXME: Make manual intervention possible
				# --tg 23:11 05-05-29
				die("Couldn't merge automatically: Call 'git resolve'");
			}
			$message .= "<= ${current_url} (threeway merge)\n";

		} else {
			$message .= "<= ${current_url} (automatic merge)\n";
		}
	}

	open($fh, "+>", undef);
	print $fh $message;
	seek($fh, 0, 0);
	$head = gitcmdinout($fh, 'git-commit-tree', $last_tree, @heads);
	chomp($head);
	close $fh;

	head($head);
	return;
}

sub
patch
{
	my @files = ();
	my ($fh, $patch);
	my $file = shift || die("Need at least on argument.\n");
	my $head  = undef;
	my $dir   = undef;

	if (head()
	&& (    dirty_files()
            || `git-diff-cache -r --cached HEAD`)) {
		print STDERR "Get rid of dirty files / uncommited deltas first.\n";
		exit 1;
	}

	($fh, $patch) = tempfile(CLEANUP => 1);

	`filterdiff -x '*/.*' $file > $patch`;

	@files = `lsdiff --strip 1 $patch`;

	chomp(@files);

	$dir = tempdir(CLEANUP => 1);
	$ENV{GIT_INDEX_FILE} = "$dir/.index";

	if ($head = head()) {
		gitcmd('git-read-tree', $head);
		foreach my $file (@files) {
			gitcmd('git-checkout-cache', '-q', "--prefix=$dir/", $file);
		}
	}

	my $pwd = getcwd;

	symlink("$pwd/.git", "$dir/.git");

	chdir($dir);

	# FIXME call in batch modus and check return value --tg 08:30 05-05-21
	system("${PATCH} -p1 < $patch");

	foreach my $file (@files) {
		gitcmd('git-update-cache', '--add', '--remove', $file);
	}

	commit();

	chdir($pwd);
	delete($ENV{GIT_INDEX_FILE});
	checkout('-f');
}

sub
checkout
{
	my $head = head();
	gitcmd('git-read-tree', '-m', $head);

	# FIXME if this fails call it without -m --tg 20:25 05-05-09

	if (defined($_[0]) && $_[0] eq '-f') {
		gitcmd('git-checkout-cache', '-u', '-f', '-a');

	} else {
		gitcmd('git-checkout-cache', '-u', '-q', '-a');
	}

#	changed();
#
#	foreach my $file (gitcmdout('git-ls-files')) {
#		chomp($file);
#		my $time = (sort {$b <=> $a} @{$touched{$file}})[0];
#		utime $time, $time, $file;
#	}
}

sub
generate_url
{
	my $url = shift;

	if (! defined($url)) {
		if ( -f '.git/PARENT' || -l '.git/PARENT') {
			chomp ($url = _read_file('.git/PARENT'));
		} else {
			die("$0: No URL specified and no parent found: Where to pull from?\n");
		}
	} else {
		$url =~ s#\/$##;

		if (-d $url &&
		    ! ($url =~ /\.git$/)) {
				$url .= "/.git";
		}

		if (-d $url &&
		    ! ($url =~ /^\//)) {
			# FIXME: use Cwd; my $pwd = getcwd; --tg 21:32 05-05-03
			chomp(my $pwd = `pwd`);
			$url = "${pwd}/${url}";
		}
	}

	return $url;
}

sub
pull
{
	my @references = ();
	my %options;
	local @ARGV = @_;
	GetOptions(\%options, 'o', 'l');
	@_ = @ARGV;

	if (head()
	&& (! defined($options{'o'}))
	&& (    dirty_files()
	    || `git-diff-cache -r --cached HEAD`)) {
		print STDERR "Get rid of dirty files / uncommited deltas first.\n";
		exit 1;
	}

	# FIXME SANITY CHECK. It is only possible to pull in 15 trees
	# --tg 17:58 05-05-28

	if (! defined($_[0])) {
		unshift(@_, generate_url(shift));
	}

	if ( -f '.git/HEAD' || -l '.git/HEAD') {
		push(@references, [head(), "${hostname}:" . getcwd()]);
	}

	while(defined (my $url = shift)) {
		$url = generate_url($url);
		my $remote_head = undef;

		if (defined($options{'l'}) && -d $url) {
			$ENV{'GIT_ALTERNATE_OBJECT_DIRECTORIES'} = "${url}/objects";

		} else {
			gitcmd('rsync', '-qa', '--ignore-existing', "$url/objects/.", ".git/objects/.");
		}
		gitcmd('rsync', '-qL', "$url/HEAD", '.git/REMOTE_HEAD');
		chomp($remote_head = _read_file('.git/REMOTE_HEAD'));
		push(@references, [$remote_head, $url]);
	}

	if (! defined($options{'o'})) {
		merge(@references);
		checkout('-f');
	}
}

sub
changes
{
	my %options;
	local @ARGV = @_;
	GetOptions(\%options, "L", "R", "d", "m", "n", "t=s", "S=s", 'root');
	@_ = @ARGV;

	my $git_diff_tree_options = '-s';
	if (defined ($options{'d'})) {
		$git_diff_tree_options = '-p';
	}
	if (defined ($options{'m'})) {
		$git_diff_tree_options .= ' -m';
	}
	if (defined ($options{'S'})) {
		$git_diff_tree_options .= " -S'$options{'S'}'";
	}
	if (defined ($options{'root'})) {
		$git_diff_tree_options .= " --root";
	}

	if (defined ($options{'L'})) {
		if (! defined ($options{'n'})) {
			pull('-o', '-l', shift);
		}
		system("git-rev-tree HEAD '^REMOTE_HEAD' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV));

	} elsif (defined ($options{'R'})) {
		if (! defined ($options{'n'})) {
			pull('-o', '-l', shift);
		}
		system("git-rev-tree REMOTE_HEAD '^HEAD' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV));

	} elsif (defined ($options{'t'})) {
		system("git-rev-tree HEAD '^$options{'t'}' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join (' ', @ARGV));
	
	} else {
		system("git-rev-list HEAD | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV));
	}
}

sub
_push
{
	my $url = generate_url(shift);

	gitcmd('rsync', '-qL', "$url/HEAD", '.git/REMOTE_HEAD');
	chomp (my $remote_head = _read_file('.git/REMOTE_HEAD'));
	my $head        = head() || die("No local HEAD\n");
	if ($head eq $remote_head) {
		print STDERR "Nothing to push.\n";
		return;
	}
	print "head => $head\nremote => $remote_head\n";
	my @revlist     = gitcmdout('git-rev-list', $head);
	if (! grep(/^${remote_head}$/, @revlist)) {
		print STDERR "Remote is ahead or unrelated: Need to pull first?\n";
		exit 1;
	}
	gitcmd('rsync', '-qa', '--ignore-existing', ".git/objects/.", "$url/objects/.");
	gitcmd('rsync', '-q', '.git/HEAD', "$url/HEAD");
}

sub
clone
{
	my %options;

	GetOptions(\%options, "l");

	my $url     = shift(@ARGV);
	my $project = shift(@ARGV);

	if (! defined($url) || ! defined($project)) {
		die("Usage: $0 clone <url> <project>\n");
	}

	$url = generate_url($url);

	if (defined($options{l})) {
		if (-d $url) {
			-d ".git"              && die("$0 clone $project: Don\'t create repository in a repository.");
			mkdir($project, 0755)  || die("$0 clone $project: mkdir: $!");
			chdir($project)        || die("$0 clone $project: chdir: $!");
			mkdir('.git', 0755)    || die("$0 clone $project: mkdir: $!");
			symlink("$url/objects", '.git/objects') || die("Can\'t symlink object database");

		} else {
			die("Can't symlink from network repositories\n");
		}
	} else {
		setup($project);
	}
	parent($url);
	pull();
}

sub
parent
{
	my $parent = $_[0];
	if (defined($parent)) {
		if ($parent eq "-c") {
			if ( -f ".git/PARENT" || -l ".git/PARENT") {
				unlink(".git/PARENT") || die("can't delete .git/PARENT: $!");
			}
			print STDERR "$0 parent: Parent removed\n";

		} else {
			$parent = generate_url($parent);

			_write_file('.git/PARENT', "$parent\n");
		}

	} else {
		if ( -f ".git/PARENT" || -l ".git/PARENT") {
			
			chomp($parent = _read_file('.git/PARENT'));
			print "${parent}\n";

		} else {
			print STDERR "$0 parent: No parent specified\n";
		}
	}
}

sub
diff
{
	my %options;
	local @ARGV = @_;
	GetOptions(\%options, 'r=s', 'f', 'c', 'v', "S=s", 'root');
	@_ = @ARGV;

	my $dir = tempdir(CLEANUP => 1);

	refresh();

	my $git_diff_tree_options = '';

	if (defined ($options{'S'})) {
		$git_diff_tree_options .= " -S'$options{'S'}'";
	}

	if (defined ($options{'root'})) {
		$git_diff_tree_options .= " --root";
	}

	if (defined($options{'r'})) {
		if($options{'r'} =~ /:/) {
			my ($first, $second) = split(/:/, $options{'r'});
			system("git-diff-tree -p -r $first $second $git_diff_tree_options " . join(' ', @_));

		} else {
			if (defined ($options{'v'})) {
				$git_diff_tree_options .= ' -v';
			}
			system("git-diff-tree -p -r $options{'r'} $git_diff_tree_options " . join(' ', @_));
		}

	} elsif (defined($options{"f"})) {
		system("git-diff-files -r -z $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z");

	} elsif (defined($options{"c"})) {
		system("git-diff-cache --cached -r -z HEAD $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z");

	} else {
		system("git-diff-cache -r -z HEAD $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z");
	}
}

sub
treediff
{
	my %options;
	local @ARGV = @_;
	Getopt::Long::Configure("pass_through");
	GetOptions(\%options, 'p=s');
	Getopt::Long::Configure("no_pass_through");
	@_ = @ARGV;
	pull('-o', '-l', $options{'p'});
	diff('-r', 'REMOTE_HEAD:HEAD', @_);
}

sub
init_db
{
	gitcmd( 'init-db', @_ );
}

sub
setup
{
	my $project = shift    || die ("usage: $0 setup project");
	-d ".git"              && die ("$0 setup $project: Don\'t create repository in a repository.");
	mkdir($project, 0755)  || die ("$0 setup $project: mkdir: $!");
	chdir($project)        || die ("$0 setup $project: chdir: $!");
	gitcmd('git-init-db');
}

sub
revert
{
	# TODO handle revert of add/remove --tg 02:32 05-05-06
	my @in    = ();
	my $fh;

	if (! defined($_[0])) {
		return;
	}

	if ($_[0] eq "-") {
		@in = <STDIN>;

	} else {
		@in = @_;
	}

	my $head = head();
	($fh, $ENV{GIT_INDEX_FILE}) = tempfile(CLEANUP => 1);
	gitcmd('git-read-tree', $head);

	foreach (@in) {
		chomp($_);
		$_ =~ s#^\.\/##;

		if (/^\./) {
			print STDERR "Warning: Skipping $_\n";
			next;
		}

		if (-d $_) {
			next;
		}

		gitcmd('git-checkout-cache', '-f', $_);
	}

	delete($ENV{GIT_INDEX_FILE});

	foreach (@in) {
		chomp($_);
		$_ =~ s#^\.\/##;

		if (/^\./) {
			next;
		}

		if (! -f $_) {
			next;
		}

		gitcmd('git-update-cache',  $_);
	}
}

sub
add
{
	my @in    = ();

	if (defined($_[0]) && $_[0] eq "-") {
		@in = <STDIN>;

	} else {
		@in = @_;
	}

	foreach (@in) {
		chomp($_);
		$_ =~ s#^\.\/##;

		if (/^\./) {
			print STDERR "Warning: Skipping $_\n";
			next;
		}

		if (-d $_) {
			next;
		}

		if (! -f $_) {
			print STDERR "Warning: Skipping nonexisting file: $_\n";
			next;
		}

		gitcmd( 'git-update-cache', '--add', '--', $_ );
	}

}

sub
ci
{
	my @in    = ();

	if (defined($_[0]) && $_[0] eq "-") {
		@in = <STDIN>;

	} else {
		@in = @_;
	}

	foreach (@in) {
		chomp($_);
		$_ =~ s#^\.\/##;

		if (/^\./) {
			print STDERR "Warning: Skipping $_\n";
			next;
		}

		if (-d $_) {
			next;
		}

		if (! -f $_) {
			print STDERR "Warning: Skipping nonexisting file: $_\n";
			next;
		}

		gitcmd( 'git-update-cache', '--', $_ );
	}

}

sub
rm
{
	my @in    = ();

	if (defined($_[0]) && $_[0] eq "-") {
		@in = <STDIN>;

	} else {
		@in = @_;
	}

	foreach (@in) {
		chomp($_);
		$_ =~ s#^\.\/##;

		if (/^\./) {
			print STDERR "Warning: Skipping $_\n";
			next;
		}

		if (-d $_) {
			next;
		}

		if (-f $_) {
			unlink($_) || die ("$0 rm $_: $!");
		}

		gitcmd( 'git-update-cache', '--remove', '--' , $_ );
	}

}

sub
head
{
	my $head = $_[0];
	if (defined($head)) {
		if ($head eq "") {
			if ( -f ".git/HEAD" || -l ".git/HEAD") {
				unlink(".git/HEAD") || die "failed to delete .git/HEAD: $!\n";
			}
		} else {
			chomp($head);
			_write_file( '.git/HEAD', "$head\n" );
		}

	} else {
		if (-f '.git/HEAD') {
			chomp( $head = _read_file( '.git/HEAD' ) );
		} else {
			$head = undef;
		}
	}

	return $head;
}

sub
print_commit
{
	my $commit = shift || die("need commit");

	print "commit $commit\n";
	foreach (gitcmdout('git-cat-file', 'commit', $commit)) {
		if (/^(author|committer)(.+>\s)(\d+)\s([+-]?\d{4})$/) {
			# local $ENV{TZ} = $4;
			print "$1$2" . localtime($3) . "\n";
		} else {
			print "$_\n";
		}
	}
	print "\n";
}

sub
commit
{
	my $head = head();
	chomp (my $tree = gitcmdout('git-write-tree'));

	if (dirty_files()) {
		die "$0 commit: Get rid of dirty files first.\n";
	}

	if (defined($head)) {
		my %hash = commit_hash($head);

		if ($hash{tree} eq $tree) {
			die "$0 commit: The commit wouldn't commit anything different.\n";
		}

		$head = gitcmdout('git-commit-tree', $tree, '-p', $head);
	} else {
		$head = gitcmdout('git-commit-tree', $tree);
	}

	head($head);
}

sub
log
{
	my $head = head() || return ;

	my $pid;
	if ( ! -p STDIN && ! -p STDOUT ) {
		my ( $r, $w );
		pipe( $r, $w ) || die "Failed to pipe: $!";
		defined( $pid = fork ) || die "Failed to fork: $!";
		if ( $pid ) { # Parent
			$SIG{INT} = $SIG{TERM} = $SIG{HUP} = sub {
				kill 15, $pid;
				exit 1;
			};
			close $r;
			close STDOUT;
			open STDOUT, '>&', $w || die "Failed to redirect STDOUT: $!";
		} else { # pager child
			close $w;
			close STDIN;
			open STDIN, '<&', $r || die "Failed to redirect STDIN: $!";
			if ( $ENV{PAGER} ) {
				exec( $ENV{PAGER} );
			} else {
				exec( 'less', '-r', '-' );
			}
		}
	}

	foreach (gitcmdout('git-rev-list', $head)) {
		print_commit($_);
	}

	if ( $pid ) {
		close STDOUT;
		waitpid($pid, 0);
	}
}

sub
commit_hash
{
	my %hash    = ();
	my $id      = shift || die ("$0: commit_hash: expect one argument");
	my $comment = 0;

 	my @lines = gitcmdout('git-cat-file', 'commit', $id);

	foreach (@lines) {
		chomp;

		if ($comment) {
			push(@{$hash{comment}}, $_);
			next;
		}

		if (/^tree\s(\w{40})$/) {
			$hash{tree} = $1;
			next;
		}

		if (/^parent\s(\w{40})$/) {
			push(@{$hash{parent}}, $1);
			next;
		}

		if (/^author\s(.*)\s<(.*)>\s(.*)$/) {
			$hash{author_name}  = $1;
			$hash{author_eMail} = $2;
			$hash{author_date}  = $3;
			next;
		}

		if (/^committer\s(.*)\s<(.*)>\s(.*)$/) {
			$hash{committer_name}  = $1;
			$hash{committer_eMail} = $2;
			$hash{committer_date}  = $3;
			next;
		}

		if (/^$/) {
			$comment = 1;
		}
	}

	return(%hash);
}

sub
first_parent
{
	my $this = shift || die ("$0: parent called without object");
	my %hash = commit_hash($this);

	if (! defined(@{$hash{parent}}[0])) {
		return "";
	}

	return(@{$hash{parent}}[0]);
}

sub
undo
{
	my $head = head();

	if (! defined($head)) {
		return;
	}

	if (    dirty_files()
            || `git-diff-cache -r --cached HEAD`) {
		print STDERR "Get rid of dirty files / uncommited deltas first.\n";
		exit 1;
	}

	my $newhead = first_parent($head);

	print "oldhead $head\nnewhead $newhead\n";

	head($newhead);
	checkout('-f');
}

sub
object_type
{
	my $id = shift;
	defined $id && $id =~ /^[A-Za-z0-9]{40}$/ ||
		die "Invalid sha1 id '$id'";
	my $type = gitcmdout('git-cat-file', '-t', $id );
	chomp $type;
	return $type;
}

sub
tree_id
{
	my $id = shift;
	( $id ) = grep { defined }
		map { /^tree ([A-Za-z0-9]{40})$/ ? $1 : undef }
			gitcmdout( 'git-cat-file', 'commit', $id ) or
		die "Unable to find tree id for commit id $id";
	object_type( $id ) eq 'tree' ||
		die "tree id from commit is not a tree object!";
	return $id;
}

sub
parent_id
{
	my $id = shift;
	my ( $pid ) = grep { defined }
		map { /^parent ([A-Za-z0-9]{40})$/ ? $1 : undef }
			gitcmdout( 'git-cat-file', 'commit', $id ) or
		die "Unable to determine parent commit of commit $id";
	return $pid;
}

# int
# main(int argc, char **argv)
# {

if (defined($ARGV[0]) && defined($commands{$ARGV[0]})) {
	my $string = shift;
	if ( ! -d ".git"
	&&   ! ($string eq "setup"
             || $string eq "init-db"
             || $string eq "clone")) {
		die("$0 $string: Not in a git BASE directory");
	}
	$commands{$string}->(@ARGV);

} else {
	if (defined($ARGV[0])) {
		print STDERR "No such command: $ARGV[0]\n\n";
	}	
	usage();
}

# }

{
	my %gitcmd;

	sub
	gitcmdpath
	{
		my $cmd = shift;
		unless ( defined $gitcmd{$cmd} ) {
			local $/ = "\n";
			chomp( $gitcmd{$cmd} = `which $cmd` );
			return undef if $gitcmd{$cmd} eq '';
		}
		return $gitcmd{$cmd};
	}

	sub
	gitcmd
	{
		my $cmd = shift;
		gitcmdpath( $cmd ) || die "command '$cmd' not found";
		my $r = system( $gitcmd{$cmd}, @_ );
		die "$cmd failed: " . _gitcmderrmsg( $cmd )
			if $r != 0;
		return 1;
	}

	sub
	gitcmdinout
	{
		my $infh = shift;
		my $cmd  = shift;
		gitcmdpath( $cmd ) || die "command '$cmd' not found";
		my ( $r, $w );
		pipe( $r, $w ) || die "Failed to pipe: $!";
		my $pid = fork();
		die "Failed to fork: $!" unless defined $pid;
		if ( $pid ) {
			close $w;
			local $/;
			local $_ = <$r>;
			close $r;
			my $kid = waitpid( $pid, 0 );
			die "Hmm, auto reaping in place?" if $kid == -1;
			die "$cmd failed: " . _gitcmderrmsg( $cmd )
				if $? & 127 || $? >> 8 != 0;
			if ( wantarray ) {
				return split( "\n", $_ );
			} else {
				return $_;
			}
		} else {
			close $r;
			close STDOUT;
			close STDIN;
			open STDIN, '<&', $infh || die "Failed to rediret STDIN";
			open STDOUT, '>&', $w || die "Failed to redirect STDOUT";
			exec( $gitcmd{$cmd}, @_ );
		}
	}

	sub
	gitcmdout
	{
		my $cmd = shift;
		gitcmdpath( $cmd ) || die "command '$cmd' not found";
		my ( $r, $w );
		pipe( $r, $w ) || die "Failed to pipe: $!";
		my $pid = fork();
		die "Failed to fork: $!" unless defined $pid;
		if ( $pid ) {
			close $w;
			local $/;
			my $ret = <$r>;
			close $r;

			my $kid = waitpid( $pid, 0 );
			die "Hmm, auto reaping in place?" if $kid == -1;
			die "$cmd failed: " . _gitcmderrmsg( $cmd )
				if $? & 127 || $? >> 8 != 0;

			if (wantarray) {
				return split("\n", $ret);
			} else {
				return $ret;
			}
		} else {
			close $r;
			close STDOUT;
			open STDOUT, '>&', $w || die "Failed to redirect STDOUT";
			exec( $gitcmd{$cmd}, @_ );
		}
	}

	sub
	_gitcmderrmsg
	{
		my $cmd = shift;
		my $e;
		if ( $? == -1 ) {
			$e = "failed to execute $gitcmd{$cmd}: $!";
		} elsif ( $? & 127 ) {
			$e = sprintf( 'child die from signal %d', ( $? & 127 ) );
			$e .= ' (with coredump)' if $? & 128;
		} else {
			$e = sprintf( 'child exit value: %d', $? >> 8 );
		}
		return $e;
	}
}

sub
_recur_mkdir
{
	my $dir = shift;
	my @dir = split( /\//, $dir );
	my $path = '';
	while ( @dir ) {
		$path .= '/' . shift @dir;
		( -d $path ) || mkdir( $path ) ||
			die "Failed to mkdir $path: $!";
	}
}

sub
_read_file
{
	my $file = shift;
	my $fh;
	open $fh, '<', $file || die "failed to read $file: $!\n";
	if ( wantarray ) {
		my @r = <$fh>;
		close $fh || die "failed to close $file: $!\n";
		return @r;
	} else {
		local $/;
		my $r = <$fh>;
		close $fh || die "failed to close $file: $!\n";
		return $r;
	}
}

sub
_write_file
{
	my $file = shift;
	my $fh;
	open $fh, '>', $file || die "failed to write $file: $!\n";
	if ( @_ ) {
		print $fh join( $/, @_ );
	}
	close $fh            || die "failed to close $file: $!\n";
	return 1;
}

# vim:set noexpandtab:

^ permalink raw reply

* [COGITO PATCH] Fix cg-log and cg-status for non-GNU sed/sort
From: Mark Allen @ 2005-05-29 23:00 UTC (permalink / raw)
  To: git

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

Here're a couple of pretty simple patches against cg-log and cg-status (two cogito
commands I use a lot) for the non-GNU (or at least older, forked GNU) tools on Darwin.

There's also a non-GNU sed problem in cg-commit, but I haven't quite figured a good way
to tackle this issue yet. (The problem is in the regex stripping leading and trailing
newlines from $LOGMSG at line 170. Darwin's sed does not like the embedded curly braces,
but there's not a good simple way to rewrite this and retain the meaning of the "sedlet"
that tracks whether its seen any text yet.)

Cheers,

--Mark

[-- Attachment #2: 14121161-cogito-non-gnu.patch --]
[-- Type: application/octet-stream, Size: 1880 bytes --]


---
commit e43fc64b1f779b1ecf1bcc36e4f89a9f43ab2fb6
tree 95770616edbd49c71dff9ca6e51e7abf449d35e7
parent d36ed1fd011d349ea2adc00456750b5b3da3a9cd
author Mark Allen <mrallen1@yahoo.com> 1117406652 -0500
committer Mark Allen <mallen@aeris.local> 1117406652 -0500

 cg-log    |   13 +++++--------
 cg-status |    2 +-
 2 files changed, 6 insertions(+), 9 deletions(-)

Index: cg-log
===================================================================
--- 3a65f5fb53367c75f4f76aebed27a83d8679f068/cg-log  (mode:100755)
+++ 95770616edbd49c71dff9ca6e51e7abf449d35e7/cg-log  (mode:100755)
@@ -148,10 +148,10 @@
 				fi
 
 				date=(${rest#*> })
-				pdate="$(showdate $date)"
+				pdate="$(date -u -r $date)"
 				if [ "$pdate" ]; then
-					echo -n $color$key $rest | sed "s/>.*/> $pdate/"
-					echo $coldefault
+					echo -n $color$key $rest | sed "s/>.*/> $pdate/" 
+					echo -n $coldefault
 				else
 					echo $color$key $rest $coldefault
 				fi
@@ -168,11 +168,8 @@
 				if [ -n "$list_files" ]; then
 					list_commit_files "$tree1" "$tree2"
 				fi
-				echo; sed -re '
-					/ *Signed-off-by:.*/Is//'$colsignoff'&'$coldefault'/
-					/ *Acked-by:.*/Is//'$colsignoff'&'$coldefault'/
-					s/./    &/
-				'
+				echo; sed -e "/ *Signed-off-by:.*/s/ *Signed-off-by:.*/$colsignoff&$coldefault/" -e"/ *Acked-by:.*/s/ *Acked-by:.*/$colsignoff&$coldefault/" -e "s/./    &/"
+				
 				;;
 			*)
 				echo $colheader$key $rest $coldefault
Index: cg-status
===================================================================
--- 3a65f5fb53367c75f4f76aebed27a83d8679f068/cg-status  (mode:100755)
+++ 95770616edbd49c71dff9ca6e51e7abf449d35e7/cg-status  (mode:100755)
@@ -15,7 +15,7 @@
 
 {
 	git-ls-files -z -t --others --deleted --unmerged $EXCLUDE
-} | sort -z -k 2 | xargs -0 sh -c '
+} | sort -t '\0' -k 2 | xargs -0 sh -c '
 while [ "$1" ]; do
 	tag=${1% *};
 	filename=${1#* };

^ permalink raw reply

* Re: Problem with cg-merge
From: Jonas Fonseca @ 2005-05-29 22:58 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Marcel Holtmann, GIT Mailing List
In-Reply-To: <20050529224042.GV1036@pasky.ji.cz>

Petr Baudis <pasky@ucw.cz> wrote Mon, May 30, 2005:
> Dear diary, on Sun, May 29, 2005 at 11:15:24PM CEST, I got a letter
> where Jonas Fonseca <fonseca@diku.dk> told me that...
> > There is another problem I've noticed in the work on the cogito
> > manpages. To make it easier to update the ``external'' patch I maintain
> > I have added the files Documentation/make-cg-asciidoc and
> > Documentation/make-cogito-asciidoc but not actually committed them.
> > That way cg-diff will include them (diffed against /dev/null) which I
> > like very much. However, when I cg-update from my local cogito origin
> > cg-merge will fast-forward using cg-diff which results in rejects when
> > patching the local changes due to the files already existing.
> > 
> > One possibility would be to revert all local changes with the patch
> > generated by cg-diff since most of them will be overwritten anyway.
> > This would remove uncommitted added files but it would probably require
> > use of the GIT metadata to get the fast-forwarding right with modes
> > intact.
> 
> I'm confused. Are you suggesting that we just throw away any local
> changes when updating? That sounds horrible - it's something the user
> has full right not to expect and I'd be very careful about it.

Forget what I suggested. The current way to cg-diff then update the tree
and apply the local changes on top of that does handled locally added
but uncommited files.

> In the longer term we shouldn't muck with diff | patch but instead do a
> three-way merge on the local changes too.

Yes, that sounds nice.

-- 
Jonas Fonseca

^ permalink raw reply

* Re: Problem with cg-merge
From: Petr Baudis @ 2005-05-29 22:40 UTC (permalink / raw)
  To: Jonas Fonseca; +Cc: Marcel Holtmann, GIT Mailing List
In-Reply-To: <20050529211524.GB32141@diku.dk>

Dear diary, on Sun, May 29, 2005 at 11:15:24PM CEST, I got a letter
where Jonas Fonseca <fonseca@diku.dk> told me that...
> There is another problem I've noticed in the work on the cogito
> manpages. To make it easier to update the ``external'' patch I maintain
> I have added the files Documentation/make-cg-asciidoc and
> Documentation/make-cogito-asciidoc but not actually committed them.
> That way cg-diff will include them (diffed against /dev/null) which I
> like very much. However, when I cg-update from my local cogito origin
> cg-merge will fast-forward using cg-diff which results in rejects when
> patching the local changes due to the files already existing.
> 
> One possibility would be to revert all local changes with the patch
> generated by cg-diff since most of them will be overwritten anyway.
> This would remove uncommitted added files but it would probably require
> use of the GIT metadata to get the fast-forwarding right with modes
> intact.

I'm confused. Are you suggesting that we just throw away any local
changes when updating? That sounds horrible - it's something the user
has full right not to expect and I'd be very careful about it.

In the longer term we shouldn't muck with diff | patch but instead do a
three-way merge on the local changes too.

-- 
				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] Make cg-status report added/removed files correctly
From: Dan Holmsand @ 2005-05-29 22:11 UTC (permalink / raw)
  To: git

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

cg-status presently only cares about the cache (by parsing the output of 
git-update-cache and git-ls-files), when it should look at the tree in HEAD.

This patch fixes this, by using git-diff-cache HEAD instead (using 
git-diff-files only to detect the difference between missing files and 
cg-rm'd ones).

It also uses the exclusion mechanism in git-ls-files, instead of parsing 
git-ls-files output, and adds '*~' and '#*' to the list of patterns 
excluded by default.

Signed-off-by: Dan Holmsand <holmsand@gmail.com>

[-- Attachment #2: cg-status.patch.txt --]
[-- Type: text/plain, Size: 1092 bytes --]

diff --git a/cg-status b/cg-status
--- a/cg-status
+++ b/cg-status
@@ -7,33 +7,24 @@
 
 . ${COGITO_LIB}cg-Xlib
 
+[ 0 -eq $# ] || die "no arguments allowed"
+
 EXCLUDEFILE=$_git/exclude
 EXCLUDE=
 if [ -f $EXCLUDEFILE ]; then
 	EXCLUDE="--exclude-from=$EXCLUDEFILE"
 fi
 
-{
-	git-ls-files -z -t --others --deleted --unmerged $EXCLUDE
-} | sort -z -k 2 | xargs -0 sh -c '
-while [ "$1" ]; do
-	tag=${1% *};
-	filename=${1#* };
-	case "$filename" in
-	*.[ao] | tags | ,,merge*) ;;
-	*)   echo "$tag $filename";;
-	esac
-	shift
-done
-' padding
+git-update-cache --refresh > /dev/null
+
+git-ls-files --others --exclude='*.[ao]' --exclude='.*' --exclude=tags \
+	--exclude='*~' --exclude='#*' \
+	--exclude=',,merge*' $EXCLUDE | sed 's,^,? ,'
 
-{
-	git-update-cache --refresh
-} | cut -f 1 -d ":" | xargs sh -c '
-while [ "$1" ]; do
-	tag="M";
-	filename=${1%: *};
-	echo "$tag $filename";
-	shift
+git-diff-cache HEAD | cut -f5- -d' ' | 
+while IFS=$'\t' read -r mode file; do
+	if [ "$mode" = D ]; then
+		[ "$(git-diff-files "$file")" ] && mode=!
+	fi
+	echo "$mode $file"
 done
-' padding

^ permalink raw reply

* Re: [PATCH] Add -B flag to diff-* brothers.
From: Petr Baudis @ 2005-05-29 21:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List
In-Reply-To: <7vbr6vrvqf.fsf@assigned-by-dhcp.cox.net>

Dear diary, on Sat, May 28, 2005 at 09:33:44PM CEST, I got a letter
where Junio C Hamano <junkio@cox.net> told me that...
> This introduces diffcore-break.c, a new diffcore
> transformation.
> 
> When the -B flag is given, a patch that represents a complete
> rewrite is broken into a deletion followed by a creation.  This
> makes it easier to review such a complete rewrite patch.
> 
> The -B flag takes the same syntax as the -M and -C flags to
> specify the maximum amount of source material the resulting file
> can still have to be considered a complete rewrite, and defaults
> to 15% if not specified.  For example, this detects the complete
> rewrite of ls-tree.c I sent earlier.
> 
> Signed-off-by: Junio C Hamano <junkio@cox.net>

Actually, I like this one - contrary to -O I can see how this could be
quite useful - I have wished for this many times when people would send
me some "complete rewrite" patches which I actually wanted to review.

Please don't give up on it. :-)

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

^ permalink raw reply

* Re: [PATCH] Do not show empty diff in diff-cache uncached.
From: Junio C Hamano @ 2005-05-29 21:52 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <7vis11n69l.fsf@assigned-by-dhcp.cox.net>

>>>>> "JCH" == Junio C Hamano <junkio@cox.net> writes:

JCH> Let me make sure that we are on the same page.

Linus,

    now I've examined what are and what aren't merged in your
tree, I think I know the answer to that question.  The set you
picked look sensible to me.

I'm taking a look at the resulting tree to see if there are
fixes and cleanups that I still think should be merged.  I'll
feed them afresh to you later, if there are any, after rebasing
the patch to the tip of your tree.

Please disregard the patches you have already discarded so far;
this request-to-discard includes -O and -B enhancements.


^ permalink raw reply

* [COGITO PATCH] mirroring repositories
From: Michael Frank @ 2005-05-29 21:41 UTC (permalink / raw)
  To: Petr Baudis; +Cc: git

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

The attached patch adds the two programs cg-mirror-add and
cg-mirror-sync.  Say you do all of your work on your laptop and you want
to make a mirror of your repository available to the public.  You
specify the location of the mirror with cg-mirror-add:

$ cg-mirror-add scp://my.server:/var/www/repos/project.git

which locally creates the file .git/mirrors.  Whenever you want to
upload your changes, you run cg-mirror-sync.

Michael

Signed-off-by: Michael Frank <msfrank@syntaxjockey.com>

[-- Attachment #2: mirror.patch --]
[-- Type: text/x-patch, Size: 2426 bytes --]

diff -pruN cogito.old/cg-mirror-add cogito.new/cg-mirror-add
--- cogito.old/cg-mirror-add	1969-12-31 16:00:00.000000000 -0800
+++ cogito.new/cg-mirror-add	2005-05-26 18:46:16.357489808 -0700
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+#
+# Add new mirror to the GIT repository.
+# Copyright (c) Michael Frank, 2005
+#
+# Takes the mirror location as parameter.  Location can be
+# an rsync URI, as in:
+#     $ cg-mirror add rsync://your.server:/path/to/project.git
+#
+# or an scp URI (rsync is still used to copy the files):
+#     $ cg-mirror add scp://user@your.server:/project/path.git
+#
+
+. ${COGITO_LIB}cg-Xlib
+
+location=$1
+
+([ "$location" ]) || die "usage: cg-mirror-add MIRROR_LOC"
+if [ -e $_git/mirrors ]; then
+    grep -xq $location $_git/mirrors && die "mirror already exists"
+fi
+echo "$location" >> $_git/mirrors
diff -pruN cogito.old/cg-mirror-sync cogito.new/cg-mirror-sync
--- cogito.old/cg-mirror-sync	1969-12-31 16:00:00.000000000 -0800
+++ cogito.new/cg-mirror-sync	2005-05-26 18:37:35.408686064 -0700
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+#
+# Pushes changes from the local GIT repository to mirrors.
+# Copyright (c) Michael Frank, 2005
+#
+
+. ${COGITO_LIB}cg-Xlib
+
+[ -r $_git/mirrors ] || die "No mirrors to sync!"
+
+uri=
+for mirror in `cat $_git/mirrors`; do
+    if echo $mirror | grep -q "^scp://"; then
+        uri=`echo $mirror | sed -e "s/^scp:\/\///"`
+        echo "syncing $mirror ..."
+        rsync -a -v -z --exclude=mirrors $_git/* $uri
+        echo ""
+    elif echo $mirror | grep -q "^rsync://"; then
+        echo "syncing $mirror ..."
+        rsync -a -v -z --exclude=mirrors $_git/* $mirror
+        echo ""
+    else
+        echo "skipping $mirror; mirror uses unknown transport"
+    fi
+done
diff -pruN cogito.old/Makefile cogito.new/Makefile
--- cogito.old/Makefile	2005-05-26 19:15:25.412593016 -0700
+++ cogito.new/Makefile	2005-05-26 17:44:43.348912448 -0700
@@ -55,7 +55,8 @@ PROG=   git-update-cache git-diff-files 
 SCRIPT=	commit-id tree-id parent-id cg-add cg-admin-lsobj cg-admin-uncommit \
 	cg-branch-add cg-branch-ls cg-cancel cg-clone cg-commit cg-diff \
 	cg-export cg-help cg-init cg-log cg-ls cg-merge cg-mkpatch cg-patch \
-	cg-pull cg-restore cg-rm cg-seek cg-status cg-tag cg-tag-ls cg-update
+	cg-pull cg-restore cg-rm cg-seek cg-status cg-tag cg-tag-ls cg-update \
+	cg-mirror-add cg-mirror-sync
 
 LIB_SCRIPT=cg-Xlib cg-Xdiffdo cg-Xmergefile
 

^ permalink raw reply

* Re: change of git-diff-tree and symlinks
From: Petr Baudis @ 2005-05-29 21:32 UTC (permalink / raw)
  To: Kay Sievers; +Cc: Jochen Roemling, Git Mailing List
In-Reply-To: <20050529150656.GA27127@vrfy.org>

Dear diary, on Sun, May 29, 2005 at 05:06:56PM CEST, I got a letter
where Kay Sievers <kay.sievers@vrfy.org> told me that...
> On Sun, May 29, 2005 at 04:16:53PM +0200, Jochen Roemling wrote:
> > I'm planning to use cogito/git for tracking development of my (PHP 
> > based) website. Although this is the first time in my life I'm using 
> > something that smells like a SCM, it seems to work great. The only thing 
> > lacking is a working gitweb installation.
> > I downloaded the gitweb.cgi script from 
> > kernel.org/pub/software/scm/gitweb, but it dates already May 23rd.
> > 
> > In your mail below from May 25 you state that there have been quite some 
> > changes to git-diff-tree:
> > 
> > Kay Sievers wrote:
> > >
> > >The new one shows simply nothing.
> > >Shouldn't it print the mode changes like the old one?
> > >
> > and that might be the reason why I'm getting "nothing" when I'm clicking 
> > on a "commitdiff" link.
> 
> The kernel.org servers use Cogito which needs a new release. So the
> version on kernel.org is always a working version with the current
> release of Cogito as long as we don't get a git-core to provide the
> binaries.

Tomorrow evening. I'd even do it now but I feel the changes need a bit
of testing yet.

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

^ permalink raw reply

* Re: Problem with cg-merge
From: Jonas Fonseca @ 2005-05-29 21:15 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Marcel Holtmann, GIT Mailing List
In-Reply-To: <20050529184830.GM1036@pasky.ji.cz>

Petr Baudis <pasky@ucw.cz> wrote Sun, May 29, 2005:
> Dear diary, on Sun, May 29, 2005 at 08:17:30PM CEST, I got a letter
> where Marcel Holtmann <marcel@holtmann.org> told me that...
> > > --- 0ca4ae56fa7bbd8d10e2c2791e389bc19977e460/cg-Xlib  (mode:100755)
> > > +++ uncommitted/cg-Xlib  (mode:100755)
> > > @@ -66,7 +66,7 @@
> > >  		# XXX: This may be suboptimal, but it is also non-trivial to keep
> > >  		# the adds/removes properly.  So this is just a quick hack to get it
> > >  		# working without much fuss.
> > > -		cg-diff -r $branch >$patchfile
> > > +		cg-diff >$patchfile
> > >  	fi
> > >  
> > >  	git-read-tree -m "$branch" || die "$branch: bad commit"
> > 
> > this looks better now. Petr, please apply this patch.
> 
> Bah. Well if your imaginary friend in your head told you this is
> obviously just a no-go workaround, (s)he was right for once. You just
> broke cg-admin-uncommit with this one, the real fix is to tell
> tree_timewarp to rollback the tree (or rather rollforth here ;-). This
> way the right branch of this if will be invoked.

Ok, I should have mentioned it was an ugly workaround.

There is another problem I've noticed in the work on the cogito
manpages. To make it easier to update the ``external'' patch I maintain
I have added the files Documentation/make-cg-asciidoc and
Documentation/make-cogito-asciidoc but not actually committed them.
That way cg-diff will include them (diffed against /dev/null) which I
like very much. However, when I cg-update from my local cogito origin
cg-merge will fast-forward using cg-diff which results in rejects when
patching the local changes due to the files already existing.

One possibility would be to revert all local changes with the patch
generated by cg-diff since most of them will be overwritten anyway.
This would remove uncommitted added files but it would probably require
use of the GIT metadata to get the fast-forwarding right with modes
intact.

-- 
Jonas Fonseca

^ permalink raw reply

* [PATCH]  Don't remove missing files in cg-commit
From: Dan Holmsand @ 2005-05-29 20:01 UTC (permalink / raw)
  To: git

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

cg-commit presently wants to remove missing files, even if they haven't 
been cg-rm'd. This is due to a missing "-m" in the call to git-diff-cache.

Signed-off-by: Dan Holmsand <holmsand@gmail.com>


[-- Attachment #2: cg-commit.patch.txt --]
[-- Type: text/plain, Size: 396 bytes --]

diff --git a/cg-commit b/cg-commit
--- a/cg-commit
+++ b/cg-commit
@@ -73,7 +73,7 @@ else
 	# be committed along automagically as well.
 
 	if [ ! "$ignorecache" ]; then
-		eval changedfiles=($(git-diff-cache -r HEAD | cut -f 2 | \
+		eval changedfiles=($(git-diff-cache -r -m HEAD | cut -f 2 | \
 			sed -e 's/^/"/' -e 's/$/"/'))
 		commitfiles=($addedfiles $remfiles "${changedfiles[@]}")
 	fi

^ permalink raw reply

* Re: [PATCH] Add -O<orderfile> option to diff-* brothers.
From: Junio C Hamano @ 2005-05-29 20:32 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.58.0505291154030.10545@ppc970.osdl.org>

>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:

LT> This smells to me like a "cool feature, because we can"
LT> rather than a "this is useful because of xxx".

You could even do the rename/copy detection outside with a
standalone diff-raw filter, just like your "sort-filenames" pipe
example.  Rename/copy is not done that way primarily for the
performance reasons.  Pickaxe is not done that way because we
wanted to conditionally suppress diff-tree headers, and it is
easier to implement the suppression inside rather than as a
"sort-filename" like filter.

This diffcore-order is more like "cool feature, because we can,
_and_ it is simpler to do it inside rather than outside, now we
already have the infrastructure inside to do this kind of thing
for other purposes anyway".





^ 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