Git development
 help / color / mirror / Atom feed
* [PATCH] t5540: clarify that http-push does not handle packed-refs on the remote
From: Johannes Schindelin @ 2009-01-17 15:41 UTC (permalink / raw)
  To: git, gitster
In-Reply-To: <alpine.DEB.1.00.0901171632330.3586@pacific.mpi-cbg.de>


Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	On Sat, 17 Jan 2009, Johannes Schindelin wrote:

	> 	Note that the push in t5540 is still broken, as http-push does
	> 	not handle packed-refs (when looking what branches are on the 
	> 	remote side).

	... and this clarifies the issue.

 t/t5540-http-push.sh |   18 +++++++++++++++---
 1 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index 22cfbb6..c236b5e 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -51,17 +51,29 @@ test_expect_success 'clone remote repository' '
 	git clone $HTTPD_URL/test_repo.git test_repo_clone
 '
 
-test_expect_failure 'push to remote repository' '
+test_expect_failure 'push to remote repository with packed refs' '
 	cd "$ROOT_PATH"/test_repo_clone &&
 	: >path2 &&
 	git add path2 &&
 	test_tick &&
 	git commit -m path2 &&
+	HEAD=$(git rev-parse --verify HEAD) &&
 	git push &&
-	[ -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/refs/heads/master" ]
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 test $HEAD = $(git rev-parse --verify HEAD))
 '
 
-test_expect_failure 'create and delete remote branch' '
+test_expect_success ' push to remote repository with unpacked refs' '
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 rm packed-refs &&
+	 git update-ref refs/heads/master \
+		0c973ae9bd51902a28466f3850b543fa66a6aaf4) &&
+	git push &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+	 test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'create and delete remote branch' '
 	cd "$ROOT_PATH"/test_repo_clone &&
 	git checkout -b dev &&
 	: >path3 &&
-- 
1.6.1.325.g062d4

^ permalink raw reply related

* Re: Cyellow, was Re: [a way-too-long line]
From: Johannes Schindelin @ 2009-01-17 15:46 UTC (permalink / raw)
  To: Jeff King
  Cc: Markus Heidelberg, Baz, Adeodato Simó,
	Boyd Stephen Smith Jr., Shawn O. Pearce, Ted Pavlic, git,
	Junio C Hamano
In-Reply-To: <20090117154052.GC27071@coredump.intra.peff.net>

Hi,

On Sat, 17 Jan 2009, Jeff King wrote:

> On Sat, Jan 17, 2009 at 04:39:10PM +0100, Johannes Schindelin wrote:
> 
> > > True. I was going to suggest %Cyellow, but for some reason only red, 
> > > green, and blue are implemented. How silly. Patch series to follow.
> > 
> > http://repo.or.cz/w/git/dscho.git?a=commitdiff;h=af6ee188b3533e9c8eb665066ed0be32d58be875
> 
> :) Yes, that does work, but please see the series I just posted:
> 
>   http://article.gmane.org/gmane.comp.version-control.git/106062
> 
> which I think is a better solution.

Definitely.

Mail from the list seems to be lagging these days... Sigh.

Ciao,
Dscho

^ permalink raw reply

* Re: [RFC] Making format-patch output better when blank line is missing?
From: Jeff King @ 2009-01-17 15:47 UTC (permalink / raw)
  To: Adeodato Simó; +Cc: git
In-Reply-To: <20090117123545.GA22842@chistera.yi.org>

On Sat, Jan 17, 2009 at 01:35:45PM +0100, Adeodato Simó wrote:

> I'm using git-format-patch to send commit diff emails, and it was
> recently brought to my atention that if a commit message consists
> entirely in a paragraph with no blank lines (particluarly, no blank line
> after the first line, which is not a summary but just the first line of
> the paragraph), git-format-patch will place all the commit message in
> the Subject, which is inconvenient to read.

Yeah, such messages don't really interact well with the idea of a
subject/body commit format.

> Do you think it would be good to have format-patch do something like
> this in this case?:
> 
>     Subject: [PATCH] This commit introduces a new function bla() that [...]
> 
>     [...] makes it possible to apply the frobniz operation to objects
>     that don't bla bla bla bla.
> 
> (I realize that, with this, the applying logic would have to learn about
> it. Hm.)

I think it would be more readable to simply limit the subject line, add
"...", and then repeat the whole paragraph in the body. Then worst case,
the person applying just gets the text repeated, like:

  This commit introduces a new function bla() that...

  This commit introduces a new function bla() that makes it possible to
  aply the forbniz operation to objects that don't bla bla bla bla.

which is actually pretty reasonable. And best case, the person applying
fixes it up appropriately. But I don't know if it is worth trying to
come up with a reversible format. Patches sent through email are meant
to preserve what's on the other end, but it doesn't _have_ to be exact,
especially in the face of somebody ignoring the workflow conventions
that the patch format is meant to support.

-Peff

^ permalink raw reply

* [PATCH 1/4] Add ctype test
From: René Scharfe @ 2009-01-17 15:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alex Riesen, Git Mailing List
In-Reply-To: <7vy6xfd3oh.fsf@gitster.siamese.dyndns.org>

Manipulating the character class table in ctype.c by hand is error prone.
To ensure that typos are found quickly, add a test program and script.

test-ctype checks the output of the character class macros isspace() et.
al. by applying them on all possible char values and consulting a list of
all characters in the particular class.  It doesn't check tolower() and
toupper(); this could be added later.

The test script t0070-fundamental.sh is created because there is no good
place for the ctype test, yet -- except for t0000-basic.sh perhaps, but
it doesn't run well on Windows, yet.

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 Makefile               |    3 ++
 t/t0070-fundamental.sh |   15 +++++++++++
 test-ctype.c           |   66 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 0 deletions(-)
 create mode 100755 t/t0070-fundamental.sh
 create mode 100644 test-ctype.c

diff --git a/Makefile b/Makefile
index 4b1d488..dca61f5 100644
--- a/Makefile
+++ b/Makefile
@@ -1360,6 +1360,7 @@ endif
 ### Testing rules
 
 TEST_PROGRAMS += test-chmtime$X
+TEST_PROGRAMS += test-ctype$X
 TEST_PROGRAMS += test-date$X
 TEST_PROGRAMS += test-delta$X
 TEST_PROGRAMS += test-genrandom$X
@@ -1379,6 +1380,8 @@ export NO_SVN_TESTS
 test: all
 	$(MAKE) -C t/ all
 
+test-ctype$X: ctype.o
+
 test-date$X: date.o ctype.o
 
 test-delta$X: diff-delta.o patch-delta.o
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
new file mode 100755
index 0000000..680d7d6
--- /dev/null
+++ b/t/t0070-fundamental.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+test_description='check that the most basic functions work
+
+
+Verify wrappers and compatibility functions.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'character classes (isspace, isalpha etc.)' '
+	test-ctype
+'
+
+test_done
diff --git a/test-ctype.c b/test-ctype.c
new file mode 100644
index 0000000..723eff4
--- /dev/null
+++ b/test-ctype.c
@@ -0,0 +1,66 @@
+#include "cache.h"
+
+
+static int test_isdigit(int c)
+{
+	return isdigit(c);
+}
+
+static int test_isspace(int c)
+{
+	return isspace(c);
+}
+
+static int test_isalpha(int c)
+{
+	return isalpha(c);
+}
+
+static int test_isalnum(int c)
+{
+	return isalnum(c);
+}
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+static const struct ctype_class {
+	const char *name;
+	int (*test_fn)(int);
+	const char *members;
+} classes[] = {
+	{ "isdigit", test_isdigit, DIGIT },
+	{ "isspace", test_isspace, " \n\r\t" },
+	{ "isalpha", test_isalpha, LOWER UPPER },
+	{ "isalnum", test_isalnum, LOWER UPPER DIGIT },
+	{ NULL }
+};
+
+static int test_class(const struct ctype_class *test)
+{
+	int i, rc = 0;
+
+	for (i = 0; i < 256; i++) {
+		int expected = i ? !!strchr(test->members, i) : 0;
+		int actual = test->test_fn(i);
+
+		if (actual != expected) {
+			rc = 1;
+			printf("%s classifies char %d (0x%02x) wrongly\n",
+			       test->name, i, i);
+		}
+	}
+	return rc;
+}
+
+int main(int argc, char **argv)
+{
+	const struct ctype_class *test;
+	int rc = 0;
+
+	for (test = classes; test->name; test++)
+		rc |= test_class(test);
+
+	return rc;
+}
-- 
1.6.1

^ permalink raw reply related

* [PATCH 2/4] Reformat ctype.c
From: René Scharfe @ 2009-01-17 15:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alex Riesen, Git Mailing List
In-Reply-To: <7vy6xfd3oh.fsf@gitster.siamese.dyndns.org>

Enhance the readability of ctype.c by using an enum instead of macros
to initialize the character class table.  This allows the use of a single
letter to mark a char, making the table fit within 80 columns.

Also list the index of the last entry in each row in the following comment.

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 ctype.c |   32 ++++++++++++++------------------
 1 files changed, 14 insertions(+), 18 deletions(-)

diff --git a/ctype.c b/ctype.c
index 9208d67..6528687 100644
--- a/ctype.c
+++ b/ctype.c
@@ -5,25 +5,21 @@
  */
 #include "cache.h"
 
-/* Just so that no insane platform contaminate namespace with these symbols */
-#undef SS
-#undef AA
-#undef DD
-#undef GS
-
-#define SS GIT_SPACE
-#define AA GIT_ALPHA
-#define DD GIT_DIGIT
-#define GS GIT_SPECIAL  /* \0, *, ?, [, \\ */
+enum {
+	S = GIT_SPACE,
+	A = GIT_ALPHA,
+	D = GIT_DIGIT,
+	G = GIT_SPECIAL,	/* \0, *, ?, [, \\ */
+};
 
 unsigned char sane_ctype[256] = {
-	GS,  0,  0,  0,  0,  0,  0,  0,  0, SS, SS,  0,  0, SS,  0,  0,		/* 0-15 */
-	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 16-15 */
-	SS,  0,  0,  0,  0,  0,  0,  0,  0,  0, GS,  0,  0,  0,  0,  0,		/* 32-15 */
-	DD, DD, DD, DD, DD, DD, DD, DD, DD, DD,  0,  0,  0,  0,  0, GS,		/* 48-15 */
-	 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,		/* 64-15 */
-	AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, GS, GS,  0,  0,  0,		/* 80-15 */
-	 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,		/* 96-15 */
-	AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,  0,  0,  0,  0,  0,		/* 112-15 */
+	G, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,		/*   0.. 15 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  16.. 31 */
+	S, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, 0, 0, 0, 0, 0,		/*  32.. 47 */
+	D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G,		/*  48.. 63 */
+	0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  64.. 79 */
+	A, A, A, A, A, A, A, A, A, A, A, G, G, 0, 0, 0,		/*  80.. 95 */
+	0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  96..111 */
+	A, A, A, A, A, A, A, A, A, A, A, 0, 0, 0, 0, 0,		/* 112..127 */
 	/* Nothing in the 128.. range */
 };
-- 
1.6.1

^ permalink raw reply related

* [PATCH 3/4] Change NUL char handling of isspecial()
From: René Scharfe @ 2009-01-17 15:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alex Riesen, Git Mailing List
In-Reply-To: <7vy6xfd3oh.fsf@gitster.siamese.dyndns.org>

Replace isspecial() by the new macro is_glob_special(), which is more,
well, specialized.  The former included the NUL char in its character
class, while the letter only included characters that are special to
file name globbing.

The new name contains underscores because they enhance readability
considerably now that it's made up of three words.  Renaming the
function is necessary to document its changed scope.

The call sites of isspecial() are updated to check explicitly for NUL.

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
---
This patch applies to next (plus the previous ones in this series).

 ctype.c           |    4 ++--
 dir.c             |    4 ++--
 git-compat-util.h |    4 ++--
 grep.c            |    5 +++--
 test-ctype.c      |    6 ++++++
 5 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/ctype.c b/ctype.c
index 6528687..9de187c 100644
--- a/ctype.c
+++ b/ctype.c
@@ -9,11 +9,11 @@ enum {
 	S = GIT_SPACE,
 	A = GIT_ALPHA,
 	D = GIT_DIGIT,
-	G = GIT_SPECIAL,	/* \0, *, ?, [, \\ */
+	G = GIT_GLOB_SPECIAL,	/* *, ?, [, \\ */
 };
 
 unsigned char sane_ctype[256] = {
-	G, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,		/*   0.. 15 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,		/*   0.. 15 */
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  16.. 31 */
 	S, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, 0, 0, 0, 0, 0,		/*  32.. 47 */
 	D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G,		/*  48.. 63 */
diff --git a/dir.c b/dir.c
index 7c59829..d55a41a 100644
--- a/dir.c
+++ b/dir.c
@@ -75,7 +75,7 @@ static int match_one(const char *match, const char *name, int namelen)
 	for (;;) {
 		unsigned char c1 = *match;
 		unsigned char c2 = *name;
-		if (isspecial(c1))
+		if (c1 == '\0' || is_glob_special(c1))
 			break;
 		if (c1 != c2)
 			return 0;
@@ -678,7 +678,7 @@ static int simple_length(const char *match)
 	for (;;) {
 		unsigned char c = *match++;
 		len++;
-		if (isspecial(c))
+		if (c == '\0' || is_glob_special(c))
 			return len;
 	}
 }
diff --git a/git-compat-util.h b/git-compat-util.h
index e20b1e8..7c92588 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -327,13 +327,13 @@ extern unsigned char sane_ctype[256];
 #define GIT_SPACE 0x01
 #define GIT_DIGIT 0x02
 #define GIT_ALPHA 0x04
-#define GIT_SPECIAL 0x08
+#define GIT_GLOB_SPECIAL 0x08
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
 #define isdigit(x) sane_istest(x,GIT_DIGIT)
 #define isalpha(x) sane_istest(x,GIT_ALPHA)
 #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
-#define isspecial(x) sane_istest(x,GIT_SPECIAL)
+#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
 
diff --git a/grep.c b/grep.c
index 6485760..f9a4525 100644
--- a/grep.c
+++ b/grep.c
@@ -30,8 +30,9 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat,
 
 static int isregexspecial(int c)
 {
-	return isspecial(c) || c == '$' || c == '(' || c == ')' || c == '+' ||
-			       c == '.' || c == '^' || c == '{' || c == '|';
+	return c == '\0' || is_glob_special(c) ||
+		c == '$' || c == '(' || c == ')' || c == '+' ||
+		c == '.' || c == '^' || c == '{' || c == '|';
 }
 
 static int is_fixed(const char *s)
diff --git a/test-ctype.c b/test-ctype.c
index 723eff4..d6425d5 100644
--- a/test-ctype.c
+++ b/test-ctype.c
@@ -21,6 +21,11 @@ static int test_isalnum(int c)
 	return isalnum(c);
 }
 
+static int test_is_glob_special(int c)
+{
+	return is_glob_special(c);
+}
+
 #define DIGIT "0123456789"
 #define LOWER "abcdefghijklmnopqrstuvwxyz"
 #define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -34,6 +39,7 @@ static const struct ctype_class {
 	{ "isspace", test_isspace, " \n\r\t" },
 	{ "isalpha", test_isalpha, LOWER UPPER },
 	{ "isalnum", test_isalnum, LOWER UPPER DIGIT },
+	{ "is_glob_special", test_is_glob_special, "*?[\\" },
 	{ NULL }
 };
 
-- 
1.6.1

^ permalink raw reply related

* [PATCH 4/4] Add is_regex_special()
From: René Scharfe @ 2009-01-17 15:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alex Riesen, Git Mailing List
In-Reply-To: <7vy6xfd3oh.fsf@gitster.siamese.dyndns.org>

Add is_regex_special(), a character class macro for chars that have a
special meaning in regular expressions.

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
---
This patch applies to next (plus the previous ones in this series).

 ctype.c           |    7 ++++---
 git-compat-util.h |    2 ++
 grep.c            |    9 +--------
 test-ctype.c      |    6 ++++++
 4 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/ctype.c b/ctype.c
index 9de187c..b90ec00 100644
--- a/ctype.c
+++ b/ctype.c
@@ -10,16 +10,17 @@ enum {
 	A = GIT_ALPHA,
 	D = GIT_DIGIT,
 	G = GIT_GLOB_SPECIAL,	/* *, ?, [, \\ */
+	R = GIT_REGEX_SPECIAL,	/* $, (, ), +, ., ^, {, | * */
 };
 
 unsigned char sane_ctype[256] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,		/*   0.. 15 */
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  16.. 31 */
-	S, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, 0, 0, 0, 0, 0,		/*  32.. 47 */
+	S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0,		/*  32.. 47 */
 	D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G,		/*  48.. 63 */
 	0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  64.. 79 */
-	A, A, A, A, A, A, A, A, A, A, A, G, G, 0, 0, 0,		/*  80.. 95 */
+	A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0,		/*  80.. 95 */
 	0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  96..111 */
-	A, A, A, A, A, A, A, A, A, A, A, 0, 0, 0, 0, 0,		/* 112..127 */
+	A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0,		/* 112..127 */
 	/* Nothing in the 128.. range */
 };
diff --git a/git-compat-util.h b/git-compat-util.h
index 7c92588..079cbe9 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -328,12 +328,14 @@ extern unsigned char sane_ctype[256];
 #define GIT_DIGIT 0x02
 #define GIT_ALPHA 0x04
 #define GIT_GLOB_SPECIAL 0x08
+#define GIT_REGEX_SPECIAL 0x10
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
 #define isdigit(x) sane_istest(x,GIT_DIGIT)
 #define isalpha(x) sane_istest(x,GIT_ALPHA)
 #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
 #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
+#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
 
diff --git a/grep.c b/grep.c
index f9a4525..062b2b6 100644
--- a/grep.c
+++ b/grep.c
@@ -28,16 +28,9 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat,
 	p->next = NULL;
 }
 
-static int isregexspecial(int c)
-{
-	return c == '\0' || is_glob_special(c) ||
-		c == '$' || c == '(' || c == ')' || c == '+' ||
-		c == '.' || c == '^' || c == '{' || c == '|';
-}
-
 static int is_fixed(const char *s)
 {
-	while (!isregexspecial(*s))
+	while (*s && !is_regex_special(*s))
 		s++;
 	return !*s;
 }
diff --git a/test-ctype.c b/test-ctype.c
index d6425d5..033c749 100644
--- a/test-ctype.c
+++ b/test-ctype.c
@@ -26,6 +26,11 @@ static int test_is_glob_special(int c)
 	return is_glob_special(c);
 }
 
+static int test_is_regex_special(int c)
+{
+	return is_regex_special(c);
+}
+
 #define DIGIT "0123456789"
 #define LOWER "abcdefghijklmnopqrstuvwxyz"
 #define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -40,6 +45,7 @@ static const struct ctype_class {
 	{ "isalpha", test_isalpha, LOWER UPPER },
 	{ "isalnum", test_isalnum, LOWER UPPER DIGIT },
 	{ "is_glob_special", test_is_glob_special, "*?[\\" },
+	{ "is_regex_special", test_is_regex_special, "$()*+.?[\\^{|" },
 	{ NULL }
 };
 
-- 
1.6.1

^ permalink raw reply related

* [PATCH/RFC v4 0/5] N-th last checked out branch
From: Thomas Rast @ 2009-01-17 16:09 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Johannes Sixt, Johan Herland
In-Reply-To: <alpine.DEB.1.00.0901171602340.3586@pacific.mpi-cbg.de>

Johannes Schindelin wrote:
> 
> Instead of traversing them twice, we just build a list of branch switches,
> pick the one we're interested in, and free the list again.
> 
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
> 
> On Sat, 17 Jan 2009, Thomas Rast wrote:
> 
> > Johannes Schindelin wrote:
> > 
> > > I really have to ask: why did you not work on top of Junio's 
> > > patch, just adding docs, tests, and checkout -?  And then -- maybe -- 
> > > the string_list...
> 
> Of course, I meant the patch as-is, with Junio as author.  But 
> hey, if he does not care...

*shrug*

For one thing there was no commit message.

I made up a message, split the changes I made into smaller patches,
and added a fixed up version of your patch on top, since it had some
context that is not in any version I have.


Johannes Schindelin (1):
  interpret_nth_last_branch(): avoid traversing the reflogs twice

Junio C Hamano (1):
  checkout: implement "@{-N}" shortcut name for N-th last branch

Thomas Rast (3):
  sha1_name: tweak @{-N} lookup
  sha1_name: support @{-N} syntax in get_sha1()
  checkout: implement "-" abbreviation, add docs and tests

 Documentation/git-checkout.txt  |    4 ++
 Documentation/git-rev-parse.txt |    3 +
 builtin-checkout.c              |   15 ++++++-
 cache.h                         |    1 +
 sha1_name.c                     |   88 +++++++++++++++++++++++++++++++++++++-
 t/t1505-rev-parse-last.sh       |   71 +++++++++++++++++++++++++++++++
 t/t2012-checkout-last.sh        |   50 ++++++++++++++++++++++
 7 files changed, 227 insertions(+), 5 deletions(-)
 create mode 100755 t/t1505-rev-parse-last.sh
 create mode 100755 t/t2012-checkout-last.sh

^ permalink raw reply

* [PATCH/RFC v4 2/5] sha1_name: tweak @{-N} lookup
From: Thomas Rast @ 2009-01-17 16:09 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Johannes Sixt, Johan Herland
In-Reply-To: <1232208597-29249-2-git-send-email-trast@student.ethz.ch>

Have the lookup only look at "interesting" checkouts, meaning those
that tell you "Already on ..." don't count even though they also cause
a reflog entry.

Let interpret_nth_last_branch() return the number of characters
parsed, so that git-checkout can verify that the branch spec was
@{-N}, not @{-1}^2 or something like that.  (The latter will be added
later.)

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 builtin-checkout.c |    4 ++-
 sha1_name.c        |   53 ++++++++++++++++++++++++++++-----------------------
 2 files changed, 32 insertions(+), 25 deletions(-)

diff --git a/builtin-checkout.c b/builtin-checkout.c
index a3b69d6..dc1de06 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -361,8 +361,10 @@ struct branch_info {
 static void setup_branch_path(struct branch_info *branch)
 {
 	struct strbuf buf = STRBUF_INIT;
+	int ret;
 
-	if (!interpret_nth_last_branch(branch->name, &buf)) {
+	if ((ret = interpret_nth_last_branch(branch->name, &buf))
+	    && ret == strlen(branch->name)) {
 		branch->name = xstrdup(buf.buf);
 		strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
 	} else {
diff --git a/sha1_name.c b/sha1_name.c
index 6377264..34e39db 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -685,29 +685,28 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 				  const char *message, void *cb_data)
 {
 	struct grab_nth_branch_switch_cbdata *cb = cb_data;
-	const char *match = NULL;
-
-	if (!prefixcmp(message, "checkout: moving to "))
-		match = message + strlen("checkout: moving to ");
-	else if (!prefixcmp(message, "checkout: moving from ")) {
-		const char *cp = message + strlen("checkout: moving from ");
-		if ((cp = strstr(cp, " to ")) != NULL) {
-			match = cp + 4;
-		}
+	const char *match = NULL, *target = NULL;
+	size_t len;
+
+	if (!prefixcmp(message, "checkout: moving from ")) {
+		match = message + strlen("checkout: moving from ");
+		if ((target = strstr(match, " to ")) != NULL)
+			target += 4;
 	}
 
 	if (!match)
 		return 0;
 
+	len = target - match - 4;
+	if (target[len] == '\n' && !strncmp(match, target, len))
+		return 0;
+
 	if (cb->counting) {
 		cb->nth++;
 		return 0;
 	}
 
-	if (--cb->nth <= 0) {
-		size_t len = strlen(match);
-		while (match[len-1] == '\n')
-			len--;
+	if (cb->nth-- <= 0) {
 		strbuf_reset(cb->buf);
 		strbuf_add(cb->buf, match, len);
 		return 1;
@@ -718,26 +717,28 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 /*
  * This reads "@{-N}" syntax, finds the name of the Nth previous
  * branch we were on, and places the name of the branch in the given
- * buf and returns 0 if successful.
+ * buf and returns the number of characters parsed if successful.
  *
  * If the input is not of the accepted format, it returns a negative
  * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
  */
 int interpret_nth_last_branch(const char *name, struct strbuf *buf)
 {
-	int nth, i;
+	int nth;
 	struct grab_nth_branch_switch_cbdata cb;
+	const char *brace;
+	char *num_end;
 
 	if (name[0] != '@' || name[1] != '{' || name[2] != '-')
 		return -1;
-	for (i = 3, nth = 0; name[i] && name[i] != '}'; i++) {
-		char ch = name[i];
-		if ('0' <= ch && ch <= '9')
-			nth = nth * 10 + ch - '0';
-		else
-			return -1;
-	}
-	if (nth < 0 || 10 <= nth)
+	brace = strchr(name, '}');
+	if (!brace)
+		return -1;
+	nth = strtol(name+3, &num_end, 10);
+	if (num_end != brace)
 		return -1;
 
 	cb.counting = 1;
@@ -745,11 +746,15 @@ int interpret_nth_last_branch(const char *name, struct strbuf *buf)
 	cb.buf = buf;
 	for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
 
+	if (cb.nth < nth)
+		return 0;
+
 	cb.counting = 0;
 	cb.nth -= nth;
 	cb.buf = buf;
 	for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
-	return 0;
+
+	return brace-name+1;
 }
 
 /*
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH/RFC v4 1/5] checkout: implement "@{-N}" shortcut name for N-th last branch
From: Thomas Rast @ 2009-01-17 16:09 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Johannes Sixt, Johan Herland,
	Junio C Hamano
In-Reply-To: <1232208597-29249-1-git-send-email-trast@student.ethz.ch>

From: Junio C Hamano <gitster@pobox.com>

Implement a shortcut @{-N} for the N-th last branch checked out, that
works by parsing the reflog for the message added by previous
git-checkout invocations.  We expand the @{-N} to the branch name, so
that you end up on an attached HEAD on that branch.
---
 builtin-checkout.c |   10 +++++-
 cache.h            |    1 +
 sha1_name.c        |   78 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/builtin-checkout.c b/builtin-checkout.c
index b5dd9c0..a3b69d6 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -361,8 +361,14 @@ struct branch_info {
 static void setup_branch_path(struct branch_info *branch)
 {
 	struct strbuf buf = STRBUF_INIT;
-	strbuf_addstr(&buf, "refs/heads/");
-	strbuf_addstr(&buf, branch->name);
+
+	if (!interpret_nth_last_branch(branch->name, &buf)) {
+		branch->name = xstrdup(buf.buf);
+		strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+	} else {
+		strbuf_addstr(&buf, "refs/heads/");
+		strbuf_addstr(&buf, branch->name);
+	}
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
diff --git a/cache.h b/cache.h
index 8e1af26..0dd9168 100644
--- a/cache.h
+++ b/cache.h
@@ -663,6 +663,7 @@ static inline unsigned int hexval(unsigned char c)
 extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
+extern int interpret_nth_last_branch(const char *str, struct strbuf *);
 
 extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
 extern const char *ref_rev_parse_rules[];
diff --git a/sha1_name.c b/sha1_name.c
index 159c2ab..6377264 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -674,6 +674,84 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
 	return retval;
 }
 
+struct grab_nth_branch_switch_cbdata {
+	int counting;
+	int nth;
+	struct strbuf *buf;
+};
+
+static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+				  const char *email, unsigned long timestamp, int tz,
+				  const char *message, void *cb_data)
+{
+	struct grab_nth_branch_switch_cbdata *cb = cb_data;
+	const char *match = NULL;
+
+	if (!prefixcmp(message, "checkout: moving to "))
+		match = message + strlen("checkout: moving to ");
+	else if (!prefixcmp(message, "checkout: moving from ")) {
+		const char *cp = message + strlen("checkout: moving from ");
+		if ((cp = strstr(cp, " to ")) != NULL) {
+			match = cp + 4;
+		}
+	}
+
+	if (!match)
+		return 0;
+
+	if (cb->counting) {
+		cb->nth++;
+		return 0;
+	}
+
+	if (--cb->nth <= 0) {
+		size_t len = strlen(match);
+		while (match[len-1] == '\n')
+			len--;
+		strbuf_reset(cb->buf);
+		strbuf_add(cb->buf, match, len);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * This reads "@{-N}" syntax, finds the name of the Nth previous
+ * branch we were on, and places the name of the branch in the given
+ * buf and returns 0 if successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ */
+int interpret_nth_last_branch(const char *name, struct strbuf *buf)
+{
+	int nth, i;
+	struct grab_nth_branch_switch_cbdata cb;
+
+	if (name[0] != '@' || name[1] != '{' || name[2] != '-')
+		return -1;
+	for (i = 3, nth = 0; name[i] && name[i] != '}'; i++) {
+		char ch = name[i];
+		if ('0' <= ch && ch <= '9')
+			nth = nth * 10 + ch - '0';
+		else
+			return -1;
+	}
+	if (nth < 0 || 10 <= nth)
+		return -1;
+
+	cb.counting = 1;
+	cb.nth = 0;
+	cb.buf = buf;
+	for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+
+	cb.counting = 0;
+	cb.nth -= nth;
+	cb.buf = buf;
+	for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+	return 0;
+}
+
 /*
  * This is like "get_sha1_basic()", except it allows "sha1 expressions",
  * notably "xyz^" for "parent of xyz"
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH/RFC v4 4/5] checkout: implement "-" abbreviation, add docs and tests
From: Thomas Rast @ 2009-01-17 16:09 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Johannes Sixt, Johan Herland
In-Reply-To: <1232208597-29249-4-git-send-email-trast@student.ethz.ch>

Have '-' mean the same as '@{-1}', i.e., the last branch we were on.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 Documentation/git-checkout.txt |    4 +++
 builtin-checkout.c             |    3 ++
 t/t2012-checkout-last.sh       |   50 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 57 insertions(+), 0 deletions(-)
 create mode 100755 t/t2012-checkout-last.sh

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 9cd5151..3bccffa 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -133,6 +133,10 @@ the conflicted merge in the specified paths.
 +
 When this parameter names a non-branch (but still a valid commit object),
 your HEAD becomes 'detached'.
++
+As a special case, the "`@\{-N\}`" syntax for the N-th last branch
+checks out the branch (instead of detaching).  You may also specify
+"`-`" which is synonymous with "`@\{-1\}`".
 
 
 Detached HEAD
diff --git a/builtin-checkout.c b/builtin-checkout.c
index dc1de06..b0a101b 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -679,6 +679,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		arg = argv[0];
 		has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
 
+		if (!strcmp(arg, "-"))
+			arg = "@{-1}";
+
 		if (get_sha1(arg, rev)) {
 			if (has_dash_dash)          /* case (1) */
 				die("invalid reference: %s", arg);
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
new file mode 100755
index 0000000..320f6eb
--- /dev/null
+++ b/t/t2012-checkout-last.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='checkout can switch to last branch'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo hello >world &&
+	git add world &&
+	git commit -m initial &&
+	git branch other &&
+	echo "hello again" >>world &&
+	git add world &&
+	git commit -m second
+'
+
+test_expect_success '"checkout -" does not work initially' '
+	test_must_fail git checkout -
+'
+
+test_expect_success 'first branch switch' '
+	git checkout other
+'
+
+test_expect_success '"checkout -" switches back' '
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" switches forth' '
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+'
+
+test_expect_success 'detach HEAD' '
+	git checkout $(git rev-parse HEAD)
+'
+
+test_expect_success '"checkout -" attaches again' '
+	git checkout - &&
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+'
+
+test_expect_success '"checkout -" detaches again' '
+	git checkout - &&
+	test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_done
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH/RFC v4 5/5] interpret_nth_last_branch(): avoid traversing the reflogs twice
From: Thomas Rast @ 2009-01-17 16:09 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Johannes Sixt, Johan Herland,
	Johannes Schindelin
In-Reply-To: <1232208597-29249-5-git-send-email-trast@student.ethz.ch>

From: Johannes Schindelin <Johannes.Schindelin@gmx.de>

Instead of traversing them twice, we just build a list of branch switches,
pick the one we're interested in, and free the list again.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 sha1_name.c |   61 ++++++++++++++++++++++++----------------------------------
 1 files changed, 25 insertions(+), 36 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index 9e1538e..b21a1f0 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -5,6 +5,8 @@
 #include "blob.h"
 #include "tree-walk.h"
 #include "refs.h"
+#include "cache-tree.h"
+#include "string-list.h"
 
 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 {
@@ -684,43 +686,31 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
 	return retval;
 }
 
-struct grab_nth_branch_switch_cbdata {
-	int counting;
-	int nth;
-	struct strbuf *buf;
-};
-
-static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+static int add_one_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 				  const char *email, unsigned long timestamp, int tz,
 				  const char *message, void *cb_data)
 {
-	struct grab_nth_branch_switch_cbdata *cb = cb_data;
+	struct string_list *list = cb_data;
 	const char *match = NULL, *target = NULL;
 	size_t len;
 
-	if (!prefixcmp(message, "checkout: moving from ")) {
-		match = message + strlen("checkout: moving from ");
-		if ((target = strstr(match, " to ")) != NULL)
-			target += 4;
-	}
-
-	if (!match)
+	if (prefixcmp(message, "checkout: moving from "))
 		return 0;
 
-	len = target - match - 4;
-	if (target[len] == '\n' && !strncmp(match, target, len))
-		return 0;
+	match = message + strlen("checkout: moving from ");
 
-	if (cb->counting) {
-		cb->nth++;
-		return 0;
+	/* Is it "moving" from a branch to itself?  Then ignore it. */
+	if ((target = strstr(match, " to ")) != NULL) {
+		target += 4;
+		len = target - match - 4;
+		if (target[len] == '\n' && !strncmp(match, target, len))
+			return 0;
 	}
+	else
+		len = strchrnul(match, ' ') - match;
+
+	string_list_append(xstrndup(match, len), list);
 
-	if (cb->nth-- <= 0) {
-		strbuf_reset(cb->buf);
-		strbuf_add(cb->buf, match, len);
-		return 1;
-	}
 	return 0;
 }
 
@@ -738,7 +728,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 int interpret_nth_last_branch(const char *name, struct strbuf *buf)
 {
 	int nth;
-	struct grab_nth_branch_switch_cbdata cb;
+	struct string_list branch_list = { NULL, 0, 0, 0 };
 	const char *brace;
 	char *num_end;
 
@@ -751,18 +741,17 @@ int interpret_nth_last_branch(const char *name, struct strbuf *buf)
 	if (num_end != brace)
 		return -1;
 
-	cb.counting = 1;
-	cb.nth = 0;
-	cb.buf = buf;
-	for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+	for_each_reflog_ent("HEAD", add_one_branch_switch, &branch_list);
 
-	if (cb.nth < nth)
+	if (branch_list.nr < nth)
 		return 0;
 
-	cb.counting = 0;
-	cb.nth -= nth;
-	cb.buf = buf;
-	for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+	strbuf_reset(buf);
+	strbuf_addstr(buf, branch_list.items[branch_list.nr - nth].string);
+
+	/* force free()ing the items */
+	branch_list.strdup_strings = 1;
+	string_list_clear(&branch_list, 0);
 
 	return brace-name+1;
 }
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH/RFC v4 3/5] sha1_name: support @{-N} syntax in get_sha1()
From: Thomas Rast @ 2009-01-17 16:09 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Johannes Sixt, Johan Herland
In-Reply-To: <1232208597-29249-3-git-send-email-trast@student.ethz.ch>

Let get_sha1() parse the @{-N} syntax, with docs and tests.

Note that while @{-1}^2, @{-2}~5 and such are supported, @{-1}@{1} is
currently not allowed.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 Documentation/git-rev-parse.txt |    3 ++
 sha1_name.c                     |   16 +++++++--
 t/t1505-rev-parse-last.sh       |   71 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 3 deletions(-)
 create mode 100755 t/t1505-rev-parse-last.sh

diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 2921da3..3ccef2f 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -212,6 +212,9 @@ when you run 'git-merge'.
   reflog of the current branch. For example, if you are on the
   branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'.
 
+* The special construct '@\{-<n>\}' means the <n>th branch checked out
+  before the current one.
+
 * A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   'rev{caret}'
diff --git a/sha1_name.c b/sha1_name.c
index 34e39db..9e1538e 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -297,6 +297,8 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
 	return logs_found;
 }
 
+static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
 	static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
@@ -307,7 +309,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 	if (len == 40 && !get_sha1_hex(str, sha1))
 		return 0;
 
-	/* basic@{time or number} format to query ref-log */
+	/* basic@{time or number or -number} format to query ref-log */
 	reflog_len = at = 0;
 	if (str[len-1] == '}') {
 		for (at = 0; at < len - 1; at++) {
@@ -324,6 +326,16 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 		return -1;
 
 	if (!len && reflog_len) {
+		struct strbuf buf = STRBUF_INIT;
+		int ret;
+		/* try the @{-N} syntax for n-th checkout */
+		ret = interpret_nth_last_branch(str+at, &buf);
+		if (ret > 0) {
+			/* substitute this branch name and restart */
+			return get_sha1_1(buf.buf, buf.len, sha1);
+		} else if (ret == 0) {
+			return -1;
+		}
 		/* allow "@{...}" to mean the current branch reflog */
 		refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
 	} else if (reflog_len)
@@ -379,8 +391,6 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 	return 0;
 }
 
-static int get_sha1_1(const char *name, int len, unsigned char *sha1);
-
 static int get_parent(const char *name, int len,
 		      unsigned char *result, int idx)
 {
diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh
new file mode 100755
index 0000000..1e49dd2
--- /dev/null
+++ b/t/t1505-rev-parse-last.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+test_description='test @{-N} syntax'
+
+. ./test-lib.sh
+
+
+make_commit () {
+	echo "$1" > "$1" &&
+	git add "$1" &&
+	git commit -m "$1"
+}
+
+
+test_expect_success 'setup' '
+
+	make_commit 1 &&
+	git branch side &&
+	make_commit 2 &&
+	make_commit 3 &&
+	git checkout side &&
+	make_commit 4 &&
+	git merge master &&
+	git checkout master
+
+'
+
+# 1 -- 2 -- 3 master
+#  \         \
+#   \         \
+#    --- 4 --- 5 side
+#
+# and 'side' should be the last branch
+
+git log --graph --all --pretty=oneline --decorate
+
+test_rev_equivalent () {
+
+	git rev-parse "$1" > expect &&
+	git rev-parse "$2" > output &&
+	test_cmp expect output
+
+}
+
+test_expect_success '@{-1} works' '
+	test_rev_equivalent side @{-1}
+'
+
+test_expect_success '@{-1}~2 works' '
+	test_rev_equivalent side~2 @{-1}~2
+'
+
+test_expect_success '@{-1}^2 works' '
+	test_rev_equivalent side^2 @{-1}^2
+'
+
+test_expect_failure '@{-1}@{1} works' '
+	test_rev_equivalent side@{1} @{-1}@{1}
+'
+
+test_expect_success '@{-2} works' '
+	test_rev_equivalent master @{-2}
+'
+
+test_expect_success '@{-3} fails' '
+	test_must_fail git rev-parse @{-3}
+'
+
+test_done
+
+
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH] contrib/workdir: create logs/refs and rr-cache in the origin repository
From: Adeodato Simó @ 2009-01-17 16:15 UTC (permalink / raw)
  To: git, gitster; +Cc: Adeodato Simó

If logs/refs or rr-cache are dangling symlinks in the workdir, and reflogs
and/or rerere are enabled, commit will die with "fatal: Could not create
directory". (In the case of rr-cache, it will die after having created the
commit.)

This commit just creates logs/refs and rr-cache in the origin repository if
they don't exist already.

Signed-off-by: Adeodato Simó <dato@net.com.org.es>
---
 contrib/workdir/git-new-workdir |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index 993cacf..a4e89d0 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -66,6 +66,12 @@ mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
 for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
 do
 	case $x in
+	logs/refs|rr-cache)
+		if [ ! -e "$git_dir/$x" ]; then
+			mkdir -p "$git_dir/$x"
+		fi
+	esac
+	case $x in
 	*/*)
 		mkdir -p "$(dirname "$new_workdir/.git/$x")"
 		;;
-- 
1.6.1.263.g35eb3c

^ permalink raw reply related

* [PATCH v4 0/7] customizable --color-words
From: Thomas Rast @ 2009-01-17 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Santi Béjar,
	Boyd Stephen Smith Jr., Teemu Likonen
In-Reply-To: <alpine.DEB.1.00.0901162208180.3586@pacific.mpi-cbg.de>

Johannes Schindelin wrote:
> Thomas, could you pick up the patches from my 'my-next' branch and 
> maintain an "official" topic branch?

I cherry-picked the three commits you had there, and rebuilt on top.
I pushed them to

  git://repo.or.cz/git/trast.git tr/word-diff-p2

again (js/word-diff-p1 again points directly at your half).

The changes on your side since my last push (hence your last sent
patches&squashes I collected) were only a pair of quotes changed from
double to single.

On my side I mainly tweaked the TeX pattern since I noticed it didn't
match many non-alnums such as (), and therefore declare them
unchanged:

-       "\\\\[a-zA-Z@]+|[][{}]|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
+       "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"),

I also added a clause to the C++ pattern to allow it to match
declarations such as

  int Foo::bar(...)

(it would give up on the :: before).


Johannes Schindelin (4):
  Add color_fwrite_lines(), a function coloring each line individually
  color-words: refactor word splitting and use ALLOC_GROW()
  color-words: change algorithm to allow for 0-character word
    boundaries
  color-words: take an optional regular expression describing words

Thomas Rast (3):
  color-words: enable REG_NEWLINE to help user
  color-words: expand docs with precise semantics
  color-words: make regex configurable via attributes

 Documentation/diff-options.txt  |   17 +++-
 Documentation/gitattributes.txt |   21 ++++
 color.c                         |   28 +++++
 color.h                         |    1 +
 diff.c                          |  222 ++++++++++++++++++++++++++-------------
 diff.h                          |    1 +
 t/t4034-diff-words.sh           |  159 ++++++++++++++++++++++++++++
 userdiff.c                      |   78 +++++++++++---
 userdiff.h                      |    1 +
 9 files changed, 440 insertions(+), 88 deletions(-)
 create mode 100755 t/t4034-diff-words.sh

^ permalink raw reply

* [PATCH v4 2/7] color-words: refactor word splitting and use ALLOC_GROW()
From: Thomas Rast @ 2009-01-17 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Santi Béjar,
	Boyd Stephen Smith Jr., Teemu Likonen
In-Reply-To: <1232209788-10408-2-git-send-email-trast@student.ethz.ch>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

Word splitting is now performed by the function diff_words_fill(),
avoiding having the same code twice.

In the same spirit, avoid duplicating the code of ALLOC_GROW().

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 diff.c |   40 +++++++++++++++++++---------------------
 1 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/diff.c b/diff.c
index d235482..c111eef 100644
--- a/diff.c
+++ b/diff.c
@@ -326,10 +326,7 @@ struct diff_words_buffer {
 static void diff_words_append(char *line, unsigned long len,
 		struct diff_words_buffer *buffer)
 {
-	if (buffer->text.size + len > buffer->alloc) {
-		buffer->alloc = (buffer->text.size + len) * 3 / 2;
-		buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
-	}
+	ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
 	line++;
 	len--;
 	memcpy(buffer->text.ptr + buffer->text.size, line, len);
@@ -398,6 +395,22 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 	}
 }
 
+/*
+ * This function splits the words in buffer->text, and stores the list with
+ * newline separator into out.
+ */
+static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out)
+{
+	int i;
+	out->size = buffer->text.size;
+	out->ptr = xmalloc(out->size);
+	memcpy(out->ptr, buffer->text.ptr, out->size);
+	for (i = 0; i < out->size; i++)
+		if (isspace(out->ptr[i]))
+			out->ptr[i] = '\n';
+	buffer->current = 0;
+}
+
 /* this executes the word diff on the accumulated buffers */
 static void diff_words_show(struct diff_words_data *diff_words)
 {
@@ -405,26 +418,11 @@ static void diff_words_show(struct diff_words_data *diff_words)
 	xdemitconf_t xecfg;
 	xdemitcb_t ecb;
 	mmfile_t minus, plus;
-	int i;
 
 	memset(&xpp, 0, sizeof(xpp));
 	memset(&xecfg, 0, sizeof(xecfg));
-	minus.size = diff_words->minus.text.size;
-	minus.ptr = xmalloc(minus.size);
-	memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
-	for (i = 0; i < minus.size; i++)
-		if (isspace(minus.ptr[i]))
-			minus.ptr[i] = '\n';
-	diff_words->minus.current = 0;
-
-	plus.size = diff_words->plus.text.size;
-	plus.ptr = xmalloc(plus.size);
-	memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
-	for (i = 0; i < plus.size; i++)
-		if (isspace(plus.ptr[i]))
-			plus.ptr[i] = '\n';
-	diff_words->plus.current = 0;
-
+	diff_words_fill(&diff_words->minus, &minus);
+	diff_words_fill(&diff_words->plus, &plus);
 	xpp.flags = XDF_NEED_MINIMAL;
 	xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
 	xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH v4 1/7] Add color_fwrite_lines(), a function coloring each line individually
From: Thomas Rast @ 2009-01-17 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Santi Béjar,
	Boyd Stephen Smith Jr., Teemu Likonen
In-Reply-To: <1232209788-10408-1-git-send-email-trast@student.ethz.ch>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

We have to set the color before every line and reset it before every
newline.  Add a function color_fwrite_lines() which does that for us.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 color.c |   28 ++++++++++++++++++++++++++++
 color.h |    1 +
 2 files changed, 29 insertions(+), 0 deletions(-)

diff --git a/color.c b/color.c
index fc0b72a..d4ae83f 100644
--- a/color.c
+++ b/color.c
@@ -191,3 +191,31 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
 	va_end(args);
 	return r;
 }
+
+/*
+ * This function splits the buffer by newlines and colors the lines individually.
+ *
+ * Returns 0 on success.
+ */
+int color_fwrite_lines(FILE *fp, const char *color,
+		size_t count, const char *buf)
+{
+	if (!*color)
+		return fwrite(buf, count, 1, fp) != 1;
+	while (count) {
+		char *p = memchr(buf, '\n', count);
+		if (p != buf && (fputs(color, fp) < 0 ||
+				fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
+				fputs(COLOR_RESET, fp) < 0))
+			return -1;
+		if (!p)
+			return 0;
+		if (fputc('\n', fp) < 0)
+			return -1;
+		count -= p + 1 - buf;
+		buf = p + 1;
+	}
+	return 0;
+}
+
+
diff --git a/color.h b/color.h
index 6cf5c88..cd5c985 100644
--- a/color.h
+++ b/color.h
@@ -19,5 +19,6 @@
 void color_parse(const char *var, const char *value, char *dst);
 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
 int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
+int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
 
 #endif /* COLOR_H */
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH v4 4/7] color-words: take an optional regular expression describing words
From: Thomas Rast @ 2009-01-17 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Santi Béjar,
	Boyd Stephen Smith Jr., Teemu Likonen
In-Reply-To: <1232209788-10408-4-git-send-email-trast@student.ethz.ch>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

In some applications, words are not delimited by white space.  To
allow for that, you can specify a regular expression describing
what makes a word with

	git diff --color-words='[A-Za-z0-9]+'

Note that words cannot contain newline characters.

As suggested by Thomas Rast, the words are the exact matches of the
regular expression.

Note that a regular expression beginning with a '^' will match only
a word at the beginning of the hunk, not a word at the beginning of
a line, and is probably not what you want.

This commit contains a quoting fix by Thomas Rast.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 Documentation/diff-options.txt |    6 +++-
 diff.c                         |   64 ++++++++++++++++++++++++++++++++++-----
 diff.h                         |    1 +
 t/t4034-diff-words.sh          |   57 +++++++++++++++++++++++++++++++++++
 4 files changed, 118 insertions(+), 10 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 43793d7..2c1fa4b 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -91,8 +91,12 @@ endif::git-format-patch[]
 	Turn off colored diff, even when the configuration file
 	gives the default to color output.
 
---color-words::
+--color-words[=regex]::
 	Show colored word diff, i.e. color words which have changed.
++
+Optionally, you can pass a regular expression that tells Git what the
+words are that you are looking for; The default is to interpret any
+stretch of non-whitespace as a word.
 
 --no-renames::
 	Turn off rename detection, even when the configuration
diff --git a/diff.c b/diff.c
index 37c886a..9fb3d0d 100644
--- a/diff.c
+++ b/diff.c
@@ -333,12 +333,14 @@ static void diff_words_append(char *line, unsigned long len,
 	len--;
 	memcpy(buffer->text.ptr + buffer->text.size, line, len);
 	buffer->text.size += len;
+	buffer->text.ptr[buffer->text.size] = '\0';
 }
 
 struct diff_words_data {
 	struct diff_words_buffer minus, plus;
 	const char *current_plus;
 	FILE *file;
+	regex_t *word_regex;
 };
 
 static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
@@ -382,17 +384,49 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 	diff_words->current_plus = plus_end;
 }
 
+/* This function starts looking at *begin, and returns 0 iff a word was found. */
+static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
+		int *begin, int *end)
+{
+	if (word_regex && *begin < buffer->size) {
+		regmatch_t match[1];
+		if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
+			char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
+					'\n', match[0].rm_eo - match[0].rm_so);
+			*end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
+			*begin += match[0].rm_so;
+			return *begin >= *end;
+		}
+		return -1;
+	}
+
+	/* find the next word */
+	while (*begin < buffer->size && isspace(buffer->ptr[*begin]))
+		(*begin)++;
+	if (*begin >= buffer->size)
+		return -1;
+
+	/* find the end of the word */
+	*end = *begin + 1;
+	while (*end < buffer->size && !isspace(buffer->ptr[*end]))
+		(*end)++;
+
+	return 0;
+}
+
 /*
  * This function splits the words in buffer->text, stores the list with
  * newline separator into out, and saves the offsets of the original words
  * in buffer->orig.
  */
-static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out)
+static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out,
+		regex_t *word_regex)
 {
 	int i, j;
+	long alloc = 0;
 
 	out->size = 0;
-	out->ptr = xmalloc(buffer->text.size);
+	out->ptr = NULL;
 
 	/* fake an empty "0th" word */
 	ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc);
@@ -400,11 +434,8 @@ static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out)
 	buffer->orig_nr = 1;
 
 	for (i = 0; i < buffer->text.size; i++) {
-		if (isspace(buffer->text.ptr[i]))
-			continue;
-		for (j = i + 1; j < buffer->text.size &&
-				!isspace(buffer->text.ptr[j]); j++)
-			; /* find the end of the word */
+		if (find_word_boundaries(&buffer->text, word_regex, &i, &j))
+			return;
 
 		/* store original boundaries */
 		ALLOC_GROW(buffer->orig, buffer->orig_nr + 1,
@@ -414,6 +445,7 @@ static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out)
 		buffer->orig_nr++;
 
 		/* store one word */
+		ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc);
 		memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i);
 		out->ptr[out->size + j - i] = '\n';
 		out->size += j - i + 1;
@@ -443,9 +475,10 @@ static void diff_words_show(struct diff_words_data *diff_words)
 
 	memset(&xpp, 0, sizeof(xpp));
 	memset(&xecfg, 0, sizeof(xecfg));
-	diff_words_fill(&diff_words->minus, &minus);
-	diff_words_fill(&diff_words->plus, &plus);
+	diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
+	diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
 	xpp.flags = XDF_NEED_MINIMAL;
+	/* as only the hunk header will be parsed, we need a 0-context */
 	xecfg.ctxlen = 0;
 	xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
 		      &xpp, &xecfg, &ecb);
@@ -484,6 +517,7 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
 		free (ecbdata->diff_words->minus.orig);
 		free (ecbdata->diff_words->plus.text.ptr);
 		free (ecbdata->diff_words->plus.orig);
+		free(ecbdata->diff_words->word_regex);
 		free(ecbdata->diff_words);
 		ecbdata->diff_words = NULL;
 	}
@@ -1506,6 +1540,14 @@ static void builtin_diff(const char *name_a,
 			ecbdata.diff_words =
 				xcalloc(1, sizeof(struct diff_words_data));
 			ecbdata.diff_words->file = o->file;
+			if (o->word_regex) {
+				ecbdata.diff_words->word_regex = (regex_t *)
+					xmalloc(sizeof(regex_t));
+				if (regcomp(ecbdata.diff_words->word_regex,
+						o->word_regex, REG_EXTENDED))
+					die ("Invalid regular expression: %s",
+							o->word_regex);
+			}
 		}
 		xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
 			      &xpp, &xecfg, &ecb);
@@ -2517,6 +2559,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 		DIFF_OPT_CLR(options, COLOR_DIFF);
 	else if (!strcmp(arg, "--color-words"))
 		options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
+	else if (!prefixcmp(arg, "--color-words=")) {
+		options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
+		options->word_regex = arg + 14;
+	}
 	else if (!strcmp(arg, "--exit-code"))
 		DIFF_OPT_SET(options, EXIT_WITH_STATUS);
 	else if (!strcmp(arg, "--quiet"))
diff --git a/diff.h b/diff.h
index 4d5a327..23cd90c 100644
--- a/diff.h
+++ b/diff.h
@@ -98,6 +98,7 @@ struct diff_options {
 
 	int stat_width;
 	int stat_name_width;
+	const char *word_regex;
 
 	/* this is set by diffcore for DIFF_FORMAT_PATCH */
 	int found_changes;
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index b22195f..4873486 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -63,4 +63,61 @@ test_expect_success 'word diff with runs of whitespace' '
 
 '
 
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+h(4),<GREEN>hh<RESET>[44]
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa<RESET> )
+EOF
+
+test_expect_success 'word diff with a regular expression' '
+
+	word_diff --color-words="[a-z]+"
+
+'
+
+echo 'aaa (aaa)' > pre
+echo 'aaa (aaa) aaa' > post
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index c29453b..be22f37 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1 +1 @@<RESET>
+aaa (aaa) <GREEN>aaa<RESET>
+EOF
+
+test_expect_success 'test parsing words for newline' '
+
+	word_diff --color-words="a+"
+
+'
+
+echo '(:' > pre
+echo '(' > post
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 289cb9d..2d06f37 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1 +1 @@<RESET>
+(<RED>:<RESET>
+EOF
+
+test_expect_success 'test when words are only removed at the end' '
+
+	word_diff --color-words=.
+
+'
+
 test_done
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH v4 5/7] color-words: enable REG_NEWLINE to help user
From: Thomas Rast @ 2009-01-17 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Santi Béjar,
	Boyd Stephen Smith Jr., Teemu Likonen
In-Reply-To: <1232209788-10408-5-git-send-email-trast@student.ethz.ch>

We silently truncate a match at the newline, which may lead to
unexpected behaviour, e.g., when matching "<[^>]*>" against

  <foo
  bar>

since then "<foo" becomes a word (and "bar>" doesn't!) even though the
regex said only angle-bracket-delimited things can be words.

To alleviate the problem slightly, use REG_NEWLINE so that negated
classes can't match a newline.  Of course newlines can still be
matched explicitly.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 diff.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/diff.c b/diff.c
index 9fb3d0d..00c661f 100644
--- a/diff.c
+++ b/diff.c
@@ -1544,7 +1544,8 @@ static void builtin_diff(const char *name_a,
 				ecbdata.diff_words->word_regex = (regex_t *)
 					xmalloc(sizeof(regex_t));
 				if (regcomp(ecbdata.diff_words->word_regex,
-						o->word_regex, REG_EXTENDED))
+						o->word_regex,
+						REG_EXTENDED | REG_NEWLINE))
 					die ("Invalid regular expression: %s",
 							o->word_regex);
 			}
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH v4 6/7] color-words: expand docs with precise semantics
From: Thomas Rast @ 2009-01-17 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Santi Béjar,
	Boyd Stephen Smith Jr., Teemu Likonen
In-Reply-To: <1232209788-10408-6-git-send-email-trast@student.ethz.ch>

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 Documentation/diff-options.txt |   15 ++++++++++-----
 1 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 2c1fa4b..8689a92 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -91,12 +91,17 @@ endif::git-format-patch[]
 	Turn off colored diff, even when the configuration file
 	gives the default to color output.
 
---color-words[=regex]::
-	Show colored word diff, i.e. color words which have changed.
+--color-words[=<regex>]::
+	Show colored word diff, i.e., color words which have changed.
+	By default, words are separated by whitespace.
 +
-Optionally, you can pass a regular expression that tells Git what the
-words are that you are looking for; The default is to interpret any
-stretch of non-whitespace as a word.
+When a <regex> is specified, every non-overlapping match of the
+<regex> is considered a word.  Anything between these matches is
+considered whitespace and ignored(!) for the purposes of finding
+differences.  You may want to append `|[^[:space:]]` to your regular
+expression to make sure that it matches all non-whitespace characters.
+A match that contains a newline is silently truncated(!) at the
+newline.
 
 --no-renames::
 	Turn off rename detection, even when the configuration
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH v4 3/7] color-words: change algorithm to allow for 0-character word boundaries
From: Thomas Rast @ 2009-01-17 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Santi Béjar,
	Boyd Stephen Smith Jr., Teemu Likonen
In-Reply-To: <1232209788-10408-3-git-send-email-trast@student.ethz.ch>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

Up until now, the color-words code assumed that word boundaries are
identical to white space characters.

Therefore, it could get away with a very simple scheme: it copied the
hunks, substituted newlines for each white space character, called
libxdiff with the processed text, and then identified the text to
output by the offsets (which agreed since the original text had the
same length).

This code was ugly, for a number of reasons:

- it was impossible to introduce 0-character word boundaries,

- we had to print everything word by word, and

- the code needed extra special handling of newlines in the removed part.

Fix all of these issues by processing the text such that

- we build word lists, separated by newlines,

- we remember the original offsets for every word, and

- after calling libxdiff on the wordlists, we parse the hunk headers, and
  find the corresponding offsets, and then

- we print the removed/added parts in one go.

The pre and post samples in the test were provided by Santi Béjar.

Note that there is some strange special handling of hunk headers where
one line range is 0 due to POSIX: in this case, the start is one too
low.  In other words a hunk header '@@ -1,0 +2 @@' actually means that
the line must be added after the _second_ line of the pre text, _not_
the first.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 diff.c                |  161 ++++++++++++++++++++++++++++---------------------
 t/t4034-diff-words.sh |   66 ++++++++++++++++++++
 2 files changed, 159 insertions(+), 68 deletions(-)
 create mode 100755 t/t4034-diff-words.sh

diff --git a/diff.c b/diff.c
index c111eef..37c886a 100644
--- a/diff.c
+++ b/diff.c
@@ -319,8 +319,10 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
 struct diff_words_buffer {
 	mmfile_t text;
 	long alloc;
-	long current; /* output pointer */
-	int suppressed_newline;
+	struct diff_words_orig {
+		const char *begin, *end;
+	} *orig;
+	int orig_nr, orig_alloc;
 };
 
 static void diff_words_append(char *line, unsigned long len,
@@ -335,80 +337,89 @@ static void diff_words_append(char *line, unsigned long len,
 
 struct diff_words_data {
 	struct diff_words_buffer minus, plus;
+	const char *current_plus;
 	FILE *file;
 };
 
-static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color,
-		int suppress_newline)
+static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 {
-	const char *ptr;
-	int eol = 0;
+	struct diff_words_data *diff_words = priv;
+	int minus_first, minus_len, plus_first, plus_len;
+	const char *minus_begin, *minus_end, *plus_begin, *plus_end;
 
-	if (len == 0)
+	if (line[0] != '@' || parse_hunk_header(line, len,
+			&minus_first, &minus_len, &plus_first, &plus_len))
 		return;
 
-	ptr  = buffer->text.ptr + buffer->current;
-	buffer->current += len;
+	/* POSIX requires that first be decremented by one if len == 0... */
+	if (minus_len) {
+		minus_begin = diff_words->minus.orig[minus_first].begin;
+		minus_end =
+			diff_words->minus.orig[minus_first + minus_len - 1].end;
+	} else
+		minus_begin = minus_end =
+			diff_words->minus.orig[minus_first].end;
 
-	if (ptr[len - 1] == '\n') {
-		eol = 1;
-		len--;
-	}
+	if (plus_len) {
+		plus_begin = diff_words->plus.orig[plus_first].begin;
+		plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end;
+	} else
+		plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
 
-	fputs(diff_get_color(1, color), file);
-	fwrite(ptr, len, 1, file);
-	fputs(diff_get_color(1, DIFF_RESET), file);
+	if (diff_words->current_plus != plus_begin)
+		fwrite(diff_words->current_plus,
+				plus_begin - diff_words->current_plus, 1,
+				diff_words->file);
+	if (minus_begin != minus_end)
+		color_fwrite_lines(diff_words->file,
+				diff_get_color(1, DIFF_FILE_OLD),
+				minus_end - minus_begin, minus_begin);
+	if (plus_begin != plus_end)
+		color_fwrite_lines(diff_words->file,
+				diff_get_color(1, DIFF_FILE_NEW),
+				plus_end - plus_begin, plus_begin);
 
-	if (eol) {
-		if (suppress_newline)
-			buffer->suppressed_newline = 1;
-		else
-			putc('\n', file);
-	}
-}
-
-static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
-{
-	struct diff_words_data *diff_words = priv;
-
-	if (diff_words->minus.suppressed_newline) {
-		if (line[0] != '+')
-			putc('\n', diff_words->file);
-		diff_words->minus.suppressed_newline = 0;
-	}
-
-	len--;
-	switch (line[0]) {
-		case '-':
-			print_word(diff_words->file,
-				   &diff_words->minus, len, DIFF_FILE_OLD, 1);
-			break;
-		case '+':
-			print_word(diff_words->file,
-				   &diff_words->plus, len, DIFF_FILE_NEW, 0);
-			break;
-		case ' ':
-			print_word(diff_words->file,
-				   &diff_words->plus, len, DIFF_PLAIN, 0);
-			diff_words->minus.current += len;
-			break;
-	}
+	diff_words->current_plus = plus_end;
 }
 
 /*
- * This function splits the words in buffer->text, and stores the list with
- * newline separator into out.
+ * This function splits the words in buffer->text, stores the list with
+ * newline separator into out, and saves the offsets of the original words
+ * in buffer->orig.
  */
 static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out)
 {
-	int i;
-	out->size = buffer->text.size;
-	out->ptr = xmalloc(out->size);
-	memcpy(out->ptr, buffer->text.ptr, out->size);
-	for (i = 0; i < out->size; i++)
-		if (isspace(out->ptr[i]))
-			out->ptr[i] = '\n';
-	buffer->current = 0;
+	int i, j;
+
+	out->size = 0;
+	out->ptr = xmalloc(buffer->text.size);
+
+	/* fake an empty "0th" word */
+	ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc);
+	buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr;
+	buffer->orig_nr = 1;
+
+	for (i = 0; i < buffer->text.size; i++) {
+		if (isspace(buffer->text.ptr[i]))
+			continue;
+		for (j = i + 1; j < buffer->text.size &&
+				!isspace(buffer->text.ptr[j]); j++)
+			; /* find the end of the word */
+
+		/* store original boundaries */
+		ALLOC_GROW(buffer->orig, buffer->orig_nr + 1,
+				buffer->orig_alloc);
+		buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i;
+		buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j;
+		buffer->orig_nr++;
+
+		/* store one word */
+		memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i);
+		out->ptr[out->size + j - i] = '\n';
+		out->size += j - i + 1;
+
+		i = j - 1;
+	}
 }
 
 /* this executes the word diff on the accumulated buffers */
@@ -419,22 +430,34 @@ static void diff_words_show(struct diff_words_data *diff_words)
 	xdemitcb_t ecb;
 	mmfile_t minus, plus;
 
+	/* special case: only removal */
+	if (!diff_words->plus.text.size) {
+		color_fwrite_lines(diff_words->file,
+			diff_get_color(1, DIFF_FILE_OLD),
+			diff_words->minus.text.size, diff_words->minus.text.ptr);
+		diff_words->minus.text.size = 0;
+		return;
+	}
+
+	diff_words->current_plus = diff_words->plus.text.ptr;
+
 	memset(&xpp, 0, sizeof(xpp));
 	memset(&xecfg, 0, sizeof(xecfg));
 	diff_words_fill(&diff_words->minus, &minus);
 	diff_words_fill(&diff_words->plus, &plus);
 	xpp.flags = XDF_NEED_MINIMAL;
-	xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
+	xecfg.ctxlen = 0;
 	xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
 		      &xpp, &xecfg, &ecb);
 	free(minus.ptr);
 	free(plus.ptr);
+	if (diff_words->current_plus != diff_words->plus.text.ptr +
+			diff_words->plus.text.size)
+		fwrite(diff_words->current_plus,
+			diff_words->plus.text.ptr + diff_words->plus.text.size
+			- diff_words->current_plus, 1,
+			diff_words->file);
 	diff_words->minus.text.size = diff_words->plus.text.size = 0;
-
-	if (diff_words->minus.suppressed_newline) {
-		putc('\n', diff_words->file);
-		diff_words->minus.suppressed_newline = 0;
-	}
 }
 
 typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
@@ -458,7 +481,9 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
 			diff_words_show(ecbdata->diff_words);
 
 		free (ecbdata->diff_words->minus.text.ptr);
+		free (ecbdata->diff_words->minus.orig);
 		free (ecbdata->diff_words->plus.text.ptr);
+		free (ecbdata->diff_words->plus.orig);
 		free(ecbdata->diff_words);
 		ecbdata->diff_words = NULL;
 	}
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
new file mode 100755
index 0000000..b22195f
--- /dev/null
+++ b/t/t4034-diff-words.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='word diff colors'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	git config diff.color.old red
+	git config diff.color.new green
+
+'
+
+decrypt_color () {
+	sed \
+		-e 's/.\[1m/<WHITE>/g' \
+		-e 's/.\[31m/<RED>/g' \
+		-e 's/.\[32m/<GREEN>/g' \
+		-e 's/.\[36m/<BROWN>/g' \
+		-e 's/.\[m/<RESET>/g'
+}
+
+word_diff () {
+	test_must_fail git diff --no-index "$@" pre post > output &&
+	decrypt_color < output > output.decrypted &&
+	test_cmp expect output.decrypted
+}
+
+cat > pre <<\EOF
+h(4)
+
+a = b + c
+EOF
+
+cat > post <<\EOF
+h(4),hh[44]
+
+a = b + c
+
+aa = a
+
+aeff = aeff * ( aaa )
+EOF
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+
+test_expect_success 'word diff with runs of whitespace' '
+
+	word_diff --color-words
+
+'
+
+test_done
-- 
1.6.1.315.g92577

^ permalink raw reply related

* [PATCH v4 7/7] color-words: make regex configurable via attributes
From: Thomas Rast @ 2009-01-17 16:29 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Santi Béjar,
	Boyd Stephen Smith Jr., Teemu Likonen
In-Reply-To: <1232209788-10408-7-git-send-email-trast@student.ethz.ch>

Make the --color-words splitting regular expression configurable via
the diff driver's 'wordregex' attribute.  The user can then set the
driver on a file in .gitattributes.  If a regex is given on the
command line, it overrides the driver's setting.

We also provide built-in regexes for the languages that already had
funcname patterns, and add an appropriate diff driver entry for C/++.
(The patterns are designed to run UTF-8 sequences into a single chunk
to make sure they remain readable.)

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 Documentation/diff-options.txt  |    4 ++
 Documentation/gitattributes.txt |   21 ++++++++++
 diff.c                          |   10 +++++
 t/t4034-diff-words.sh           |   36 ++++++++++++++++++
 userdiff.c                      |   78 +++++++++++++++++++++++++++++++-------
 userdiff.h                      |    1 +
 6 files changed, 135 insertions(+), 15 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 8689a92..1edb82e 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -102,6 +102,10 @@ differences.  You may want to append `|[^[:space:]]` to your regular
 expression to make sure that it matches all non-whitespace characters.
 A match that contains a newline is silently truncated(!) at the
 newline.
++
+The regex can also be set via a diff driver, see
+linkgit:gitattributes[1]; giving it explicitly overrides any diff
+driver setting.
 
 --no-renames::
 	Turn off rename detection, even when the configuration
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 8af22ec..ba3ba12 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -317,6 +317,8 @@ patterns are available:
 
 - `bibtex` suitable for files with BibTeX coded references.
 
+- `cpp` suitable for source code in the C and C++ languages.
+
 - `html` suitable for HTML/XHTML documents.
 
 - `java` suitable for source code in the Java language.
@@ -334,6 +336,25 @@ patterns are available:
 - `tex` suitable for source code for LaTeX documents.
 
 
+Customizing word diff
+^^^^^^^^^^^^^^^^^^^^^
+
+You can customize the rules that `git diff --color-words` uses to
+split words in a line, by specifying an appropriate regular expression
+in the "diff.*.wordregex" configuration variable.  For example, in TeX
+a backslash followed by a sequence of letters forms a command, but
+several such commands can be run together without intervening
+whitespace.  To separate them, use a regular expression such as
+
+------------------------
+[diff "tex"]
+	wordregex = "\\\\[a-zA-Z]+|[{}]|\\\\.|[^\\{}[:space:]]+"
+------------------------
+
+A built-in pattern is provided for all languages listed in the
+previous section.
+
+
 Performing text diffs of binary files
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/diff.c b/diff.c
index 00c661f..9fcde96 100644
--- a/diff.c
+++ b/diff.c
@@ -1380,6 +1380,12 @@ int diff_filespec_is_binary(struct diff_filespec *one)
 	return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
 }
 
+static const char *userdiff_word_regex(struct diff_filespec *one)
+{
+	diff_filespec_load_driver(one);
+	return one->driver->word_regex;
+}
+
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
 {
 	if (!options->a_prefix)
@@ -1540,6 +1546,10 @@ static void builtin_diff(const char *name_a,
 			ecbdata.diff_words =
 				xcalloc(1, sizeof(struct diff_words_data));
 			ecbdata.diff_words->file = o->file;
+			if (!o->word_regex)
+				o->word_regex = userdiff_word_regex(one);
+			if (!o->word_regex)
+				o->word_regex = userdiff_word_regex(two);
 			if (o->word_regex) {
 				ecbdata.diff_words->word_regex = (regex_t *)
 					xmalloc(sizeof(regex_t));
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 4873486..744221b 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -84,6 +84,41 @@ test_expect_success 'word diff with a regular expression' '
 
 '
 
+test_expect_success 'set a diff driver' '
+	git config diff.testdriver.wordregex "[^[:space:]]" &&
+	cat <<EOF > .gitattributes
+pre diff=testdriver
+post diff=testdriver
+EOF
+'
+
+test_expect_success 'option overrides default' '
+
+	word_diff --color-words="[a-z]+"
+
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+h(4)<GREEN>,hh[44]<RESET>
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+
+test_expect_success 'use default supplied by driver' '
+
+	word_diff --color-words
+
+'
+
 echo 'aaa (aaa)' > pre
 echo 'aaa (aaa) aaa' > post
 
@@ -100,6 +135,7 @@ test_expect_success 'test parsing words for newline' '
 
 	word_diff --color-words="a+"
 
+
 '
 
 echo '(:' > pre
diff --git a/userdiff.c b/userdiff.c
index 3681062..2b55509 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -6,14 +6,20 @@
 static int ndrivers;
 static int drivers_alloc;
 
-#define FUNCNAME(name, pattern) \
-	{ name, NULL, -1, { pattern, REG_EXTENDED } }
+#define PATTERNS(name, pattern, wordregex)			\
+	{ name, NULL, -1, { pattern, REG_EXTENDED }, wordregex }
 static struct userdiff_driver builtin_drivers[] = {
-FUNCNAME("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$"),
-FUNCNAME("java",
+PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
+	 "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("java",
 	 "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
-	 "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$"),
-FUNCNAME("objc",
+	 "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]="
+	 "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"
+	 "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("objc",
 	 /* Negate C statements that can look like functions */
 	 "!^[ \t]*(do|for|if|else|return|switch|while)\n"
 	 /* Objective-C methods */
@@ -21,20 +27,60 @@
 	 /* C functions */
 	 "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
 	 /* Objective-C class/protocol definitions */
-	 "^(@(implementation|interface|protocol)[ \t].*)$"),
-FUNCNAME("pascal",
+	 "^(@(implementation|interface|protocol)[ \t].*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+	 "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("pascal",
 	 "^((procedure|function|constructor|destructor|interface|"
 		"implementation|initialization|finalization)[ \t]*.*)$"
 	 "\n"
-	 "^(.*=[ \t]*(class|record).*)$"),
-FUNCNAME("php", "^[\t ]*((function|class).*)"),
-FUNCNAME("python", "^[ \t]*((class|def)[ \t].*)$"),
-FUNCNAME("ruby", "^[ \t]*((class|module|def)[ \t].*)$"),
-FUNCNAME("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$"),
-FUNCNAME("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$"),
+	 "^(.*=[ \t]*(class|record).*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
+	 "|<>|<=|>=|:=|\\.\\."
+	 "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("php", "^[\t ]*((function|class).*)",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
+	 "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"
+	 "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"
+	 "|[^[:space:]|[\x80-\xff]+"),
+	 /* -- */
+PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+	 /* -- */
+	 "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
+	 "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"
+	 "|[^[:space:]|[\x80-\xff]+"),
+PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+	 "[={}\"]|[^={}\" \t]+"),
+PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
+	 "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"),
+PATTERNS("cpp",
+	 /* Jump targets or access declarations */
+	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
+	 /* C/++ functions/methods at top level */
+	 "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
+	 /* compound type at top level */
+	 "^((struct|class|enum)[^;]*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+	 "|[^[:space:]]|[\x80-\xff]+"),
 { "default", NULL, -1, { NULL, 0 } },
 };
-#undef FUNCNAME
+#undef PATTERNS
 
 static struct userdiff_driver driver_true = {
 	"diff=true",
@@ -134,6 +180,8 @@ int userdiff_config(const char *k, const char *v)
 		return parse_string(&drv->external, k, v);
 	if ((drv = parse_driver(k, v, "textconv")))
 		return parse_string(&drv->textconv, k, v);
+	if ((drv = parse_driver(k, v, "wordregex")))
+		return parse_string(&drv->word_regex, k, v);
 
 	return 0;
 }
diff --git a/userdiff.h b/userdiff.h
index ba29457..c315159 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -11,6 +11,7 @@ struct userdiff_driver {
 	const char *external;
 	int binary;
 	struct userdiff_funcname funcname;
+	const char *word_regex;
 	const char *textconv;
 };
 
-- 
1.6.1.315.g92577

^ permalink raw reply related

* Re: [PATCH/RFC v4 0/5] N-th last checked out branch
From: Johannes Schindelin @ 2009-01-17 16:49 UTC (permalink / raw)
  To: Thomas Rast; +Cc: git, Junio C Hamano, Johannes Sixt, Johan Herland
In-Reply-To: <1232208597-29249-1-git-send-email-trast@student.ethz.ch>

Hi,

On Sat, 17 Jan 2009, Thomas Rast wrote:

> I [...] added a fixed up version of your patch on top, since it had some 
> context that is not in any version I have.

Thanks.

Yeah, I know, I have too many patches in my fork, but I'm working on it... 
:-)

Ciao,
Dscho

^ permalink raw reply

* Re: git-svn fails to fetch repository
From: Jay Soffian @ 2009-01-17 17:33 UTC (permalink / raw)
  To: Eric Wong; +Cc: Vladimir Pouzanov, git
In-Reply-To: <20090117104558.GC29598@dcvr.yhbt.net>

On Sat, Jan 17, 2009 at 5:45 AM, Eric Wong <normalperson@yhbt.net> wrote:
> At least you got a working combination working.  Does anybody know if
> macports git have any patches/changes to it that could cause this?

I installed the macports git and it worked fine for me. Same versions
of git and subversion. The only difference was that I was on macports
perl 5.8.9 and Vladimir was on macports perl 5.8.8.

If I have time, I'll try it with macports perl 5.8.8 and see if I can
reproduce. I'm pretty curious why the apply_textdelta() callback is
apparently running twice on the same file w/o the intervening close.
:-)

j.

^ permalink raw reply

* Re: [PATCH/RFC v4 3/5] sha1_name: support @{-N} syntax in get_sha1()
From: Johannes Schindelin @ 2009-01-17 17:55 UTC (permalink / raw)
  To: Thomas Rast; +Cc: git, Junio C Hamano, Johannes Sixt, Johan Herland
In-Reply-To: <1232208597-29249-4-git-send-email-trast@student.ethz.ch>

Hi,

On Sat, 17 Jan 2009, Thomas Rast wrote:

> diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh
> new file mode 100755
> index 0000000..1e49dd2
> --- /dev/null
> +++ b/t/t1505-rev-parse-last.sh
> @@ -0,0 +1,71 @@
> +#!/bin/sh
> +
> +test_description='test @{-N} syntax'
> +
> +. ./test-lib.sh
> +
> +
> +make_commit () {
> +	echo "$1" > "$1" &&
> +	git add "$1" &&
> +	git commit -m "$1"
> +}
> +
> +
> +test_expect_success 'setup' '
> +
> +	make_commit 1 &&
> +	git branch side &&
> +	make_commit 2 &&
> +	make_commit 3 &&
> +	git checkout side &&
> +	make_commit 4 &&
> +	git merge master &&
> +	git checkout master
> +
> +'
> +
> +# 1 -- 2 -- 3 master
> +#  \         \
> +#   \         \
> +#    --- 4 --- 5 side
> +#
> +# and 'side' should be the last branch
> +
> +git log --graph --all --pretty=oneline --decorate
> +

Maybe you want to squash this in, so that the output of "make test" is 
not cluttered by the graph?

-- snipsnap --
diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh
index 1e49dd2..72e8322 100755
--- a/t/t1505-rev-parse-last.sh
+++ b/t/t1505-rev-parse-last.sh
@@ -32,7 +32,11 @@ test_expect_success 'setup' '
 #
 # and 'side' should be the last branch
 
-git log --graph --all --pretty=oneline --decorate
+test_expect_success 'show a log (for debugging)' '
+
+	git log --graph --all --pretty=oneline --decorate
+
+'
 
 test_rev_equivalent () {
 
-- 
1.6.1.332.g9a59d

^ permalink raw reply related


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