Git development
 help / color / mirror / Atom feed
* Re: [PATCH] msvc: Fix compilation error due to missing mktemp() declaration
From: Erik Faye-Lund @ 2010-12-24  1:00 UTC (permalink / raw)
  To: Ramsay Jones; +Cc: Junio C Hamano, GIT Mailing-list
In-Reply-To: <4D139D91.8080503@ramsay1.demon.co.uk>

On Thu, Dec 23, 2010 at 8:05 PM, Ramsay Jones
<ramsay@ramsay1.demon.co.uk> wrote:
>
> Commit d1b6e6e (win32: use our own dirent.h, 2010-11-23) removed
> the compat/vcbuild/include/dirent.h compatibility header file.
> This file, among other things, included the <io.h> system header
> file which provides the declaration of the mktemp() function.
>
> In order to fix the compilation error, we add an include directive
> for <io.h> to the compat/vcbuild/include/unistd.h header. (The
> MinGW build includes <io.h> from it's <unistd.h> header too.)
>
> Signed-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
> ---

Thanks. But shouldn't this header be included in mingw.h (or perhaps
msvc.h) because of _get_osfhandle and _commit?

^ permalink raw reply

* Re: What's cooking in git.git (Dec 2010, #06; Tue, 21)
From: Nguyen Thai Ngoc Duy @ 2010-12-24  1:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vfwto2ytb.fsf@alter.siamese.dyndns.org>

On Fri, Dec 24, 2010 at 12:17 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:
>
>> On Wed, Dec 22, 2010 at 8:59 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>> * nd/struct-pathspec (2010-12-15) 21 commits
>>>  ...
>>>  (this branch is used by en/object-list-with-pathspec.)
>>>
>>> Rerolled again.  Getting nicer by the round ;-)
>>
>> With jj/icase-directory merged to master, match_pathspec() and
>> match_pathspec_depth() now diverse again.
>>
>> When I wrote match_pathspec_depth(), I assumed that match_pathspec()
>> would not change much and I would have more time for converting the
>> rest of git to use match_*_depth(). Looks like I need to add case
>> insensitive matching to struct pathspec and friends then remove
>> match_pathspec() in this series too. At least if somebody changes
>> match_pathspec() again, it would cause a conflict so I can catch it.
>
> While this topic is something I have long wanted to see, I have started
> feeling that this needs to cook a bit longer than be in the next release.
> So perhaps the best course of action might be to rebase the series once
> after the 1.7.4 feature freeze, cook it in 'next' for a while and make it
> part of the release after that.  I think at that point we may probably
> want to have other changes that are not strictly backward compatible but
> their incompatibilities do not matter in practice (e.g. cquoting pathspecs
> in the attributes file comes to mind, but I am sure there will be other
> changes that people wanted to have but we held them off due to worries on
> compatibility).
>
> What do you think?
>

No problem.
-- 
Duy

^ permalink raw reply

* Re: What's cooking in git.git (Dec 2010, #06; Tue, 21)
From: Nguyen Thai Ngoc Duy @ 2010-12-24  1:39 UTC (permalink / raw)
  To: Joshua Jensen; +Cc: Junio C Hamano, git
In-Reply-To: <4D13DCDD.3050300@workspacewhiz.com>

On Fri, Dec 24, 2010 at 6:35 AM, Joshua Jensen
<jjensen@workspacewhiz.com> wrote:
> Having said that, I have had 100 people using the jj/icase-directory series
> on Windows daily for 4 months now without issue.  Prior to that, a majority
> of the series had been used for a full year by a dozen people.  In any case,
> the improvement on non-case sensitive file systems is the difference between
> night and day, and the series has helped prevent a number of messes that
> occurred without it (git add readme.txt and git add Readme.txt, for
> example... ugh...).
>
> More than Windows, this series also affects Mac OS X in a positive manner,
> though the case sensitivity problems can be considered worse.  When you
> change directories at the command line, the command line retains the case
> you used to change directory, and then Git uses that case as the relative
> path into the repository.  Ugh... this is different than on Windows where
> the file system's directory case is retained at the Command Prompt as you
> change directories.  (Cygwin actually appears to have the problem, too, but
> MinGW, what msysGit is built upon, does not.)
>
> The Mac OS X issue listed above is not a reason not to publish the series,
> though, as the fixes necessary to make that work are in completely different
> areas in Git than the current jj/icase-directory series.
>
> Finally, I'm sitting on a bunch of other case sensitivity refinements, but
> I'd like to get one series published before evolving this more.  I'd like to
> get the other ones out there for discussion, but they build on the current
> series.

If you have not known already, path in "git log ref -- path" must be
case sensitive. Solving that is not hard: ce_path_match() and
tree_entry_interesting() are the ones that do path matching. Those
functions are nearly replaced in this series. I'll add
case-insensitive support to them, so you can worry about other places.

> In reference to above, where is match_pathspec_depth()?  I can only find
> match_pathspec().

 Introduced in this series, nd/setup.
-- 
Duy

^ permalink raw reply

* [PATCH 0/4] teach vcs-svn/line_buffer to handle multiple input files
From: Jonathan Nieder @ 2010-12-24  8:05 UTC (permalink / raw)
  To: git; +Cc: David Barr, Thomas Rast, Ramkumar Ramachandra

Hi David et al,

This collection of patches comes from the svndiff0 series[1].  They
are not urgent --- the motivation is for svn-fe to be able to keep
separate track of input from stdin (svnrdump) and the report-fd (blobs
from fast-import) for the coming Text-delta support --- but ideally I
would like to see them applied at the start of the next merge window,
since they change API that other patches use.  See [*] below for the
open question.

The idea: instead of keeping the input file handle and input buffer as
global variables, pack them in a struct and let the calling program
keep track of them.

Patch 1 makes a previously global buffer local to the two functions
that use it.  Performance impact should be negligibile.  Ideally the
buffer would not be needed at all --- there is enough buffering at
lower layers already --- but stdio does not provide the calls that
would be needed to eliminate it (in particular a wrapper for
sendfile(2)).

Patch 2 replaces a use of the obj_pool library with a strbuf.  The
main immediate effect is to improve error handling behavior (more
importantly, this is needed for patches 3 and 4 since obj_pool is
defined to be global).

Patches 3 and 4 are the main patches, collecting input state in a
struct and moving resposibility for that struct to the calling
program, respectively.

The patches have already received a lot of testing.

[*]
I am not sure whether this is the right approach for reading from the
report-fd.  To avoid deadlock, we cannot issue a blocking read(2)
after the trailing newline has been read from an expected line or the
nth byte has been read in fixed-length input.  This would rule out
fread/fgets if implemented as follows with too large an internal
buffer:

 1. fill internal buffer completely (or stop when an error or
    end of file is encountered)
 2. fill caller's buffer from internal buffer

glibc fread/fgets do not work that way (and in fact will never
deadlock for us).  What about other platforms?  The standards (C,
POSIX) do not make it obvious.

 - maybe setvbuf(f, NULL, _IOLBF, 0) would make fgets safe to use
 - maybe setvbuf(f, NULL, _IONBF, 0) would make fread safe to use.
   On the other hand, it is not clear to me what unbuffered input
   means in this context.  That flag does not do anything meaningful
   on glibc, for example, for input streams.

If all else fails, setting the O_NONBLOCK flag with fcntl (this
could presumably be implemented as SetNamedPipeHandleState(...,
PIPE_NOWAIT) on Windows) would avoid trouble.  After any failing
operation we would have to check that the error is EAGAIN and
clear the error indicator.  Simple.  But it would be even better
to learn that that is not needed.

So far I have been playing it safe with the read(fd, buf, 1) trick
but that does not have great performance, as David noticed[2].

Thoughts?

Jonathan Nieder (4):
  vcs-svn: Eliminate global byte_buffer[] array
  vcs-svn: Replace buffer_read_string memory pool with a strbuf
  vcs-svn: Collect line_buffer data in a struct
  vcs-svn: Teach line_buffer to handle multiple input files

 test-line-buffer.c      |   17 ++++++-----
 vcs-svn/fast_export.c   |    6 ++--
 vcs-svn/fast_export.h   |    5 +++-
 vcs-svn/line_buffer.c   |   66 ++++++++++++++++++++--------------------------
 vcs-svn/line_buffer.h   |   25 +++++++++++++-----
 vcs-svn/line_buffer.txt |    5 ++-
 vcs-svn/svndump.c       |   29 +++++++++++---------
 7 files changed, 82 insertions(+), 71 deletions(-)

[1] http://thread.gmane.org/gmane.comp.version-control.git/158731
[2] http://colabti.org/irclogger/irclogger_log/git-devel?date=2010-12-18

^ permalink raw reply

* [PATCH 1/4] vcs-svn: eliminate global byte_buffer
From: Jonathan Nieder @ 2010-12-24  8:08 UTC (permalink / raw)
  To: git; +Cc: David Barr, Thomas Rast, Ramkumar Ramachandra
In-Reply-To: <20101224080505.GA29681@burratino>

Date: Sun, 10 Oct 2010 21:37:10 -0500

The data stored in byte_buffer[] is always either discarded or
written to stdout immediately.  No need for it to persist between
function calls.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
For the line_buffer library.  Decreases BSS size.  Increase stack
overhead of two I/O functions by 4096 bytes.  Performance effect
hasn't been measured.  Alas there is no stdio wrapper for sendfile; if
there were, we could eliminate the buffer altogether.

Of course the goal is to make line_buffer more easily reusable, by
eliminating _all_ global state.  I assume David would like this but I
don't remember if he said so.

 vcs-svn/line_buffer.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
index 1543567..f22c94f 100644
--- a/vcs-svn/line_buffer.c
+++ b/vcs-svn/line_buffer.c
@@ -14,7 +14,6 @@
 obj_pool_gen(blob, char, 4096)
 
 static char line_buffer[LINE_BUFFER_LEN];
-static char byte_buffer[COPY_BUFFER_LEN];
 static FILE *infile;
 
 int buffer_init(const char *filename)
@@ -68,6 +67,7 @@ char *buffer_read_string(uint32_t len)
 
 void buffer_copy_bytes(uint32_t len)
 {
+	char byte_buffer[COPY_BUFFER_LEN];
 	uint32_t in;
 	while (len > 0 && !feof(infile) && !ferror(infile)) {
 		in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
@@ -83,6 +83,7 @@ void buffer_copy_bytes(uint32_t len)
 
 void buffer_skip_bytes(uint32_t len)
 {
+	char byte_buffer[COPY_BUFFER_LEN];
 	uint32_t in;
 	while (len > 0 && !feof(infile) && !ferror(infile)) {
 		in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
-- 
1.7.2.3.554.gc9b5c.dirty

^ permalink raw reply related

* [PATCH 2/4] vcs-svn: replace buffer_read_string memory pool with a strbuf
From: Jonathan Nieder @ 2010-12-24  8:17 UTC (permalink / raw)
  To: git; +Cc: David Barr, Thomas Rast, Ramkumar Ramachandra
In-Reply-To: <20101224080505.GA29681@burratino>

Date: Sat, 6 Nov 2010 12:01:28 -0500

obj_pool is inherently global and does not use the standard growing
factor alloc_nr, which makes it feel out of place in the git codebase.
Plus it is overkill for this application: all that is needed is a
buffer that can grow between requests to accomodate larger strings.
Use a strbuf instead.

As a side effect, this improves the error handling: allocation
failures will result in a clean exit instead of segfaults.  It would
be nice to add a test case (using ulimit or failmalloc) but that can
wait for another day.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Requires jn/thinner-wrapper (from master) if contrib/svn-fe/svn-fe is
to build without linking to libz et al.

The initial size of the per-line buffer shrinks from 4096 to 0 (well,
maybe 16 or so).  strbuf_fread is not inline.  I haven't looked into
the effect on performance from these changes.

I find obj_pool tricky to use correctly (see 3c93983, vcs-svn: fix
intermittent repo_tree corruption, 2010-12-05 for example) so I look
forward to eliminating obj_pool from the vcs-svn/ dir altogether.
Excitingly enough, David has already done that, it seems[1].

[1] git://github.com/barrbrain/git.git vcs-svn-incremental

 vcs-svn/line_buffer.c |   16 ++++++----------
 1 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
index f22c94f..6f32f28 100644
--- a/vcs-svn/line_buffer.c
+++ b/vcs-svn/line_buffer.c
@@ -5,15 +5,13 @@
 
 #include "git-compat-util.h"
 #include "line_buffer.h"
-#include "obj_pool.h"
+#include "strbuf.h"
 
 #define LINE_BUFFER_LEN 10000
 #define COPY_BUFFER_LEN 4096
 
-/* Create memory pool for char sequence of known length */
-obj_pool_gen(blob, char, 4096)
-
 static char line_buffer[LINE_BUFFER_LEN];
+static struct strbuf blob_buffer = STRBUF_INIT;
 static FILE *infile;
 
 int buffer_init(const char *filename)
@@ -58,11 +56,9 @@ char *buffer_read_line(void)
 
 char *buffer_read_string(uint32_t len)
 {
-	char *s;
-	blob_free(blob_pool.size);
-	s = blob_pointer(blob_alloc(len + 1));
-	s[fread(s, 1, len, infile)] = '\0';
-	return ferror(infile) ? NULL : s;
+	strbuf_reset(&blob_buffer);
+	strbuf_fread(&blob_buffer, len, infile);
+	return ferror(infile) ? NULL : blob_buffer.buf;
 }
 
 void buffer_copy_bytes(uint32_t len)
@@ -94,5 +90,5 @@ void buffer_skip_bytes(uint32_t len)
 
 void buffer_reset(void)
 {
-	blob_reset();
+	strbuf_release(&blob_buffer);
 }
-- 
1.7.2.3.554.gc9b5c.dirty

^ permalink raw reply related

* [PATCH 3/4] vcs-svn: collect line_buffer data in a struct
From: Jonathan Nieder @ 2010-12-24  8:18 UTC (permalink / raw)
  To: git; +Cc: David Barr, Thomas Rast, Ramkumar Ramachandra
In-Reply-To: <20101224080505.GA29681@burratino>

Date: Sun, 10 Oct 2010 21:39:21 -0500

Prepare for the line_buffer lib to support input from multiple files,
by collecting global state in a struct that can be easily passed
around.

No API change yet.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
A simple search-and-replace kind of job.  If gcc is smart, there
should be no change in code size or performance, but I haven't
checked.  

 vcs-svn/line_buffer.c |   45 ++++++++++++++++++++++-----------------------
 vcs-svn/line_buffer.h |   11 +++++++++++
 2 files changed, 33 insertions(+), 23 deletions(-)

diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
index 6f32f28..e7bc230 100644
--- a/vcs-svn/line_buffer.c
+++ b/vcs-svn/line_buffer.c
@@ -7,17 +7,16 @@
 #include "line_buffer.h"
 #include "strbuf.h"
 
-#define LINE_BUFFER_LEN 10000
 #define COPY_BUFFER_LEN 4096
-
-static char line_buffer[LINE_BUFFER_LEN];
-static struct strbuf blob_buffer = STRBUF_INIT;
-static FILE *infile;
+static struct line_buffer buf_ = LINE_BUFFER_INIT;
+static struct line_buffer *buf;
 
 int buffer_init(const char *filename)
 {
-	infile = filename ? fopen(filename, "r") : stdin;
-	if (!infile)
+	buf = &buf_;
+
+	buf->infile = filename ? fopen(filename, "r") : stdin;
+	if (!buf->infile)
 		return -1;
 	return 0;
 }
@@ -25,10 +24,10 @@ int buffer_init(const char *filename)
 int buffer_deinit(void)
 {
 	int err;
-	if (infile == stdin)
-		return ferror(infile);
-	err = ferror(infile);
-	err |= fclose(infile);
+	if (buf->infile == stdin)
+		return ferror(buf->infile);
+	err = ferror(buf->infile);
+	err |= fclose(buf->infile);
 	return err;
 }
 
@@ -36,13 +35,13 @@ int buffer_deinit(void)
 char *buffer_read_line(void)
 {
 	char *end;
-	if (!fgets(line_buffer, sizeof(line_buffer), infile))
+	if (!fgets(buf->line_buffer, sizeof(buf->line_buffer), buf->infile))
 		/* Error or data exhausted. */
 		return NULL;
-	end = line_buffer + strlen(line_buffer);
+	end = buf->line_buffer + strlen(buf->line_buffer);
 	if (end[-1] == '\n')
 		end[-1] = '\0';
-	else if (feof(infile))
+	else if (feof(buf->infile))
 		; /* No newline at end of file.  That's fine. */
 	else
 		/*
@@ -51,23 +50,23 @@ char *buffer_read_line(void)
 		 * but for now let's return an error.
 		 */
 		return NULL;
-	return line_buffer;
+	return buf->line_buffer;
 }
 
 char *buffer_read_string(uint32_t len)
 {
-	strbuf_reset(&blob_buffer);
-	strbuf_fread(&blob_buffer, len, infile);
-	return ferror(infile) ? NULL : blob_buffer.buf;
+	strbuf_reset(&buf->blob_buffer);
+	strbuf_fread(&buf->blob_buffer, len, buf->infile);
+	return ferror(buf->infile) ? NULL : buf->blob_buffer.buf;
 }
 
 void buffer_copy_bytes(uint32_t len)
 {
 	char byte_buffer[COPY_BUFFER_LEN];
 	uint32_t in;
-	while (len > 0 && !feof(infile) && !ferror(infile)) {
+	while (len > 0 && !feof(buf->infile) && !ferror(buf->infile)) {
 		in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
-		in = fread(byte_buffer, 1, in, infile);
+		in = fread(byte_buffer, 1, in, buf->infile);
 		len -= in;
 		fwrite(byte_buffer, 1, in, stdout);
 		if (ferror(stdout)) {
@@ -81,14 +80,14 @@ void buffer_skip_bytes(uint32_t len)
 {
 	char byte_buffer[COPY_BUFFER_LEN];
 	uint32_t in;
-	while (len > 0 && !feof(infile) && !ferror(infile)) {
+	while (len > 0 && !feof(buf->infile) && !ferror(buf->infile)) {
 		in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
-		in = fread(byte_buffer, 1, in, infile);
+		in = fread(byte_buffer, 1, in, buf->infile);
 		len -= in;
 	}
 }
 
 void buffer_reset(void)
 {
-	strbuf_release(&blob_buffer);
+	strbuf_release(&buf->blob_buffer);
 }
diff --git a/vcs-svn/line_buffer.h b/vcs-svn/line_buffer.h
index 9c78ae1..4ae1133 100644
--- a/vcs-svn/line_buffer.h
+++ b/vcs-svn/line_buffer.h
@@ -1,6 +1,17 @@
 #ifndef LINE_BUFFER_H_
 #define LINE_BUFFER_H_
 
+#include "strbuf.h"
+
+#define LINE_BUFFER_LEN 10000
+
+struct line_buffer {
+	char line_buffer[LINE_BUFFER_LEN];
+	struct strbuf blob_buffer;
+	FILE *infile;
+};
+#define LINE_BUFFER_INIT {"", STRBUF_INIT, NULL}
+
 int buffer_init(const char *filename);
 int buffer_deinit(void);
 char *buffer_read_line(void);
-- 
1.7.2.3.554.gc9b5c.dirty

^ permalink raw reply related

* [PATCH 4/4] vcs-svn: teach line_buffer to handle multiple input files
From: Jonathan Nieder @ 2010-12-24  8:28 UTC (permalink / raw)
  To: git; +Cc: David Barr, Thomas Rast, Ramkumar Ramachandra
In-Reply-To: <20101224080505.GA29681@burratino>

Date: Sun, 10 Oct 2010 21:41:06 -0500

Collect the line_buffer state in a newly public line_buffer struct.
Callers can use multiple line_buffers to manage input from multiple
files at a time.

svn-fe's delta applier will use this to stream a delta from svnrdump
and the preimage it applies to from fast-import at the same time.

The tests don't take advantage of the new features, but I think that's
okay.  It is easier to find lingering examples of nonreentrant code by
searching for "static" in line_buffer.c.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
That's it.  The API change feels inevitable to me, perhaps because it
has been in use for so long.  Hopefully it can be a good basis for
future changes:

 - buffer_tmpfile_{init,rewind,prepare_to_read}, for when files are
   just a place to stash data for later
 - buffer_fdopen to read from a file descriptor
 - buffer_interactive for deadlock avoidance (if that turns out to
   be needed)

I would be happy to hear any thoughts you have, even as basic as
"looks ok" or "confusing".

thanks,
Jonathan

 test-line-buffer.c      |   17 +++++++++--------
 vcs-svn/fast_export.c   |    6 +++---
 vcs-svn/fast_export.h   |    5 ++++-
 vcs-svn/line_buffer.c   |   20 ++++++++------------
 vcs-svn/line_buffer.h   |   14 +++++++-------
 vcs-svn/line_buffer.txt |    5 +++--
 vcs-svn/svndump.c       |   29 ++++++++++++++++-------------
 7 files changed, 50 insertions(+), 46 deletions(-)

diff --git a/test-line-buffer.c b/test-line-buffer.c
index c11bf7f..f9af892 100644
--- a/test-line-buffer.c
+++ b/test-line-buffer.c
@@ -22,25 +22,26 @@ static uint32_t strtouint32(const char *s)
 
 int main(int argc, char *argv[])
 {
+	struct line_buffer buf = LINE_BUFFER_INIT;
 	char *s;
 
 	if (argc != 1)
 		usage("test-line-buffer < input.txt");
-	if (buffer_init(NULL))
+	if (buffer_init(&buf, NULL))
 		die_errno("open error");
-	while ((s = buffer_read_line())) {
-		s = buffer_read_string(strtouint32(s));
+	while ((s = buffer_read_line(&buf))) {
+		s = buffer_read_string(&buf, strtouint32(s));
 		fputs(s, stdout);
 		fputc('\n', stdout);
-		buffer_skip_bytes(1);
-		if (!(s = buffer_read_line()))
+		buffer_skip_bytes(&buf, 1);
+		if (!(s = buffer_read_line(&buf)))
 			break;
-		buffer_copy_bytes(strtouint32(s) + 1);
+		buffer_copy_bytes(&buf, strtouint32(s) + 1);
 	}
-	if (buffer_deinit())
+	if (buffer_deinit(&buf))
 		die("input error");
 	if (ferror(stdout))
 		die("output error");
-	buffer_reset();
+	buffer_reset(&buf);
 	return 0;
 }
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 6cfa256..260cf50 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -63,14 +63,14 @@ void fast_export_commit(uint32_t revision, uint32_t author, char *log,
 	printf("progress Imported commit %"PRIu32".\n\n", revision);
 }
 
-void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len)
+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len, struct line_buffer *input)
 {
 	if (mode == REPO_MODE_LNK) {
 		/* svn symlink blobs start with "link " */
-		buffer_skip_bytes(5);
+		buffer_skip_bytes(input, 5);
 		len -= 5;
 	}
 	printf("blob\nmark :%"PRIu32"\ndata %"PRIu32"\n", mark, len);
-	buffer_copy_bytes(len);
+	buffer_copy_bytes(input, len);
 	fputc('\n', stdout);
 }
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index 2aaaea5..054e7d5 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -1,11 +1,14 @@
 #ifndef FAST_EXPORT_H_
 #define FAST_EXPORT_H_
 
+#include "line_buffer.h"
+
 void fast_export_delete(uint32_t depth, uint32_t *path);
 void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
 			uint32_t mark);
 void fast_export_commit(uint32_t revision, uint32_t author, char *log,
 			uint32_t uuid, uint32_t url, unsigned long timestamp);
-void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len);
+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len,
+		      struct line_buffer *input);
 
 #endif
diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
index e7bc230..806932b 100644
--- a/vcs-svn/line_buffer.c
+++ b/vcs-svn/line_buffer.c
@@ -8,20 +8,16 @@
 #include "strbuf.h"
 
 #define COPY_BUFFER_LEN 4096
-static struct line_buffer buf_ = LINE_BUFFER_INIT;
-static struct line_buffer *buf;
 
-int buffer_init(const char *filename)
+int buffer_init(struct line_buffer *buf, const char *filename)
 {
-	buf = &buf_;
-
 	buf->infile = filename ? fopen(filename, "r") : stdin;
 	if (!buf->infile)
 		return -1;
 	return 0;
 }
 
-int buffer_deinit(void)
+int buffer_deinit(struct line_buffer *buf)
 {
 	int err;
 	if (buf->infile == stdin)
@@ -32,7 +28,7 @@ int buffer_deinit(void)
 }
 
 /* Read a line without trailing newline. */
-char *buffer_read_line(void)
+char *buffer_read_line(struct line_buffer *buf)
 {
 	char *end;
 	if (!fgets(buf->line_buffer, sizeof(buf->line_buffer), buf->infile))
@@ -53,14 +49,14 @@ char *buffer_read_line(void)
 	return buf->line_buffer;
 }
 
-char *buffer_read_string(uint32_t len)
+char *buffer_read_string(struct line_buffer *buf, uint32_t len)
 {
 	strbuf_reset(&buf->blob_buffer);
 	strbuf_fread(&buf->blob_buffer, len, buf->infile);
 	return ferror(buf->infile) ? NULL : buf->blob_buffer.buf;
 }
 
-void buffer_copy_bytes(uint32_t len)
+void buffer_copy_bytes(struct line_buffer *buf, uint32_t len)
 {
 	char byte_buffer[COPY_BUFFER_LEN];
 	uint32_t in;
@@ -70,13 +66,13 @@ void buffer_copy_bytes(uint32_t len)
 		len -= in;
 		fwrite(byte_buffer, 1, in, stdout);
 		if (ferror(stdout)) {
-			buffer_skip_bytes(len);
+			buffer_skip_bytes(buf, len);
 			return;
 		}
 	}
 }
 
-void buffer_skip_bytes(uint32_t len)
+void buffer_skip_bytes(struct line_buffer *buf, uint32_t len)
 {
 	char byte_buffer[COPY_BUFFER_LEN];
 	uint32_t in;
@@ -87,7 +83,7 @@ void buffer_skip_bytes(uint32_t len)
 	}
 }
 
-void buffer_reset(void)
+void buffer_reset(struct line_buffer *buf)
 {
 	strbuf_release(&buf->blob_buffer);
 }
diff --git a/vcs-svn/line_buffer.h b/vcs-svn/line_buffer.h
index 4ae1133..fb37390 100644
--- a/vcs-svn/line_buffer.h
+++ b/vcs-svn/line_buffer.h
@@ -12,12 +12,12 @@ struct line_buffer {
 };
 #define LINE_BUFFER_INIT {"", STRBUF_INIT, NULL}
 
-int buffer_init(const char *filename);
-int buffer_deinit(void);
-char *buffer_read_line(void);
-char *buffer_read_string(uint32_t len);
-void buffer_copy_bytes(uint32_t len);
-void buffer_skip_bytes(uint32_t len);
-void buffer_reset(void);
+int buffer_init(struct line_buffer *buf, const char *filename);
+int buffer_deinit(struct line_buffer *buf);
+char *buffer_read_line(struct line_buffer *buf);
+char *buffer_read_string(struct line_buffer *buf, uint32_t len);
+void buffer_copy_bytes(struct line_buffer *buf, uint32_t len);
+void buffer_skip_bytes(struct line_buffer *buf, uint32_t len);
+void buffer_reset(struct line_buffer *buf);
 
 #endif
diff --git a/vcs-svn/line_buffer.txt b/vcs-svn/line_buffer.txt
index 8906fb1..f8eaa4d 100644
--- a/vcs-svn/line_buffer.txt
+++ b/vcs-svn/line_buffer.txt
@@ -14,14 +14,15 @@ Calling sequence
 
 The calling program:
 
+ - initializes a `struct line_buffer` to LINE_BUFFER_INIT
  - specifies a file to read with `buffer_init`
  - processes input with `buffer_read_line`, `buffer_read_string`,
    `buffer_skip_bytes`, and `buffer_copy_bytes`
  - closes the file with `buffer_deinit`, perhaps to start over and
    read another file.
 
-Before exiting, the caller can use `buffer_reset` to deallocate
-resources for the benefit of profiling tools.
+When finished, the caller can use `buffer_reset` to deallocate
+resources.
 
 Functions
 ---------
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index 53d0215..3bba0fe 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -30,6 +30,8 @@
 /* Create memory pool for log messages */
 obj_pool_gen(log, char, 4096)
 
+static struct line_buffer input = LINE_BUFFER_INIT;
+
 static char* log_copy(uint32_t length, char *log)
 {
 	char *buffer;
@@ -113,14 +115,14 @@ static void read_props(void)
 	uint32_t key = ~0;
 	char *val = NULL;
 	char *t;
-	while ((t = buffer_read_line()) && strcmp(t, "PROPS-END")) {
+	while ((t = buffer_read_line(&input)) && strcmp(t, "PROPS-END")) {
 		if (!strncmp(t, "K ", 2)) {
 			len = atoi(&t[2]);
-			key = pool_intern(buffer_read_string(len));
-			buffer_read_line();
+			key = pool_intern(buffer_read_string(&input, len));
+			buffer_read_line(&input);
 		} else if (!strncmp(t, "V ", 2)) {
 			len = atoi(&t[2]);
-			val = buffer_read_string(len);
+			val = buffer_read_string(&input, len);
 			if (key == keys.svn_log) {
 				/* Value length excludes terminating nul. */
 				rev_ctx.log = log_copy(len + 1, val);
@@ -135,7 +137,7 @@ static void read_props(void)
 				node_ctx.type = REPO_MODE_LNK;
 			}
 			key = ~0;
-			buffer_read_line();
+			buffer_read_line(&input);
 		}
 	}
 }
@@ -177,9 +179,10 @@ static void handle_node(void)
 		node_ctx.type = node_ctx.srcMode;
 
 	if (node_ctx.mark)
-		fast_export_blob(node_ctx.type, node_ctx.mark, node_ctx.textLength);
+		fast_export_blob(node_ctx.type,
+				 node_ctx.mark, node_ctx.textLength, &input);
 	else if (node_ctx.textLength != LENGTH_UNKNOWN)
-		buffer_skip_bytes(node_ctx.textLength);
+		buffer_skip_bytes(&input, node_ctx.textLength);
 }
 
 static void handle_revision(void)
@@ -198,7 +201,7 @@ void svndump_read(const char *url)
 	uint32_t key;
 
 	reset_dump_ctx(pool_intern(url));
-	while ((t = buffer_read_line())) {
+	while ((t = buffer_read_line(&input))) {
 		val = strstr(t, ": ");
 		if (!val)
 			continue;
@@ -250,7 +253,7 @@ void svndump_read(const char *url)
 			node_ctx.propLength = atoi(val);
 		} else if (key == keys.content_length) {
 			len = atoi(val);
-			buffer_read_line();
+			buffer_read_line(&input);
 			if (active_ctx == REV_CTX) {
 				read_props();
 			} else if (active_ctx == NODE_CTX) {
@@ -258,7 +261,7 @@ void svndump_read(const char *url)
 				active_ctx = REV_CTX;
 			} else {
 				fprintf(stderr, "Unexpected content length header: %"PRIu32"\n", len);
-				buffer_skip_bytes(len);
+				buffer_skip_bytes(&input, len);
 			}
 		}
 	}
@@ -270,7 +273,7 @@ void svndump_read(const char *url)
 
 void svndump_init(const char *filename)
 {
-	buffer_init(filename);
+	buffer_init(&input, filename);
 	repo_init();
 	reset_dump_ctx(~0);
 	reset_rev_ctx(0);
@@ -285,7 +288,7 @@ void svndump_deinit(void)
 	reset_dump_ctx(~0);
 	reset_rev_ctx(0);
 	reset_node_ctx(NULL);
-	if (buffer_deinit())
+	if (buffer_deinit(&input))
 		fprintf(stderr, "Input error\n");
 	if (ferror(stdout))
 		fprintf(stderr, "Output error\n");
@@ -294,7 +297,7 @@ void svndump_deinit(void)
 void svndump_reset(void)
 {
 	log_reset();
-	buffer_reset();
+	buffer_reset(&input);
 	repo_reset();
 	reset_dump_ctx(~0);
 	reset_rev_ctx(0);
-- 
1.7.2.3.554.gc9b5c.dirty

^ permalink raw reply related

* Re: [RFC PATCH v7 4/9] gitweb: Prepare for splitting gitweb
From: Jonathan Nieder @ 2010-12-24  9:29 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, J.H., John 'Warthog9' Hawley
In-Reply-To: <20101222235618.7998.17447.stgit@localhost.localdomain>

Jakub Narebski wrote:

> Prepare gitweb for having been split into modules that are to be
> installed alongside gitweb in 'lib/' subdirectory, by adding
> 
>   use lib __DIR__.'/lib';
> 
> to gitweb.perl (to main gitweb script), and preparing for putting
> modules (relative path) in $(GITWEB_MODULES) in gitweb/Makefile.

Spelled out, this means modules would typically go in

	/usr/share/gitweb/lib

Is that the right place?  I suspect something like

	/usr/lib/gitweb/

could make sense in some installations for two reasons:

 - even braindamaged webserver configurations would not serve lib/
   as static files in that case;

 - if some modules are implemented in C for speed, they would need
   to go in /usr/lib anyway to follow usual filesystem conventions.

Does the Makefile let us override the directory with such a setting?

> While at it pass GITWEBLIBDIR in addition to GITWEB_TEST_INSTALLED to
> allow testing installed version of gitweb and installed version of
> modules (for future tests which would check individual (sub)modules).
> 
> Using __DIR__ from Dir::Self module (not in core, that's why currently
> gitweb includes excerpt of code from Dir::Self defining __DIR__) was
> chosen over using FindBin-based solution (in core since perl 5.00307,
> while gitweb itself requires at least perl 5.8.0) because FindBin uses
> BEGIN block

This explanation and the code below leave me nervous that the answer
might be "no". ;-)

[...]
> --- a/gitweb/gitweb.perl
> +++ b/gitweb/gitweb.perl
> @@ -10,6 +10,14 @@
>  use 5.008;
>  use strict;
>  use warnings;
> +
> +use File::Spec;
> +# __DIR__ is taken from Dir::Self __DIR__ fragment
> +sub __DIR__ () {
> +	File::Spec->rel2abs(join '', (File::Spec->splitpath(__FILE__))[0, 1]);
> +}
> +use lib __DIR__ . '/lib';
> +
>  use CGI qw(:standard :escapeHTML -nosticky);
>  use CGI::Util qw(unescape);
>  use CGI::Carp qw(fatalsToBrowser);

^ permalink raw reply

* Re: [RFC PATCH v7 6/9] gitweb/lib - Simple output capture by redirecting STDOUT to file
From: Jonathan Nieder @ 2010-12-24  9:49 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, J.H., John 'Warthog9' Hawley
In-Reply-To: <20101222235705.7998.76695.stgit@localhost.localdomain>

Jakub Narebski wrote:

> This patch was based on "gitweb: add output buffering and associated
> functions" patch by John 'Warthog9' Hawley (J.H.) in "Gitweb caching v7"
> series, and on code of Capture::Tiny by David Golden (Apache License 2.0).

Micronit: if the license of Capture::Tiny were relevant then we would be
in trouble, I think.  (Apache-2.0 and GPLv2 aren't compatible licenses.)
Luckily

[...]
> +# taken from Capture::Tiny by David Golden, Apache License 2.0
> +# with debugging stripped out
> +sub _relayer {
> +	my ($fh, $layers) = @_;
> +
> +	my %seen = ( unix => 1, perlio => 1); # filter these out
> +	my @unique = grep { !$seen{$_}++ } @$layers;
> +
> +	binmode($fh, join(":", ":raw", @unique));
> +}

looks trivial enough.  Maybe either avoiding mention of the license or
clarifying that that is not intended to be the sole license for the
stripped-down code would help?

^ permalink raw reply

* Bug reports & patches: git-svn, git-completion, git diff hunk headers for Pascal
From: Алексей Крезов @ 2010-12-24 12:46 UTC (permalink / raw)
  To: git

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

Hello!
I've found some bugs in Git:
1. git-svn:
  git svn set-tree/dcommit do not respect svn.pathnameencoding config variable
  although git svn fetch does
  here is the my patch - see attach git-svn.patch
2. git-completion
  when GIT_PS1_SHOWDIRTYSTATE is set diff.ignoreSubmodules config
  variable is not respected
  here is the my patch - see attach git-completion.bash.patch
3. CLASS procedures/functions are not detected (just
procedures/functions do) in hunk headers
for git diff for Pascal files
  here is the my patch - see attach userdiff.c.patch

P.S. All patches is against Git v1.7.3.4
  

mailto:zapped@mail.ru

[-- Attachment #2: userdiff.c.patch --]
[-- Type: application/octet-stream, Size: 489 bytes --]

diff --git a/userdiff.c b/userdiff.c
index f9e05b5..259a382 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -52,7 +52,7 @@ PATTERNS("objc",
 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
 	 "|[^[:space:]]|[\x80-\xff]+"),
 PATTERNS("pascal",
-	 "^((procedure|function|constructor|destructor|interface|"
+	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
 		"implementation|initialization|finalization)[ \t]*.*)$"
 	 "\n"
 	 "^(.*=[ \t]*(class|record).*)$",

[-- Attachment #3: git-svn.patch --]
[-- Type: application/octet-stream, Size: 370 bytes --]

diff --git a/git-svn.perl b/git-svn.perl
index 757de82..399bf4c 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -4451,6 +4451,7 @@ sub new {
 	$self->{path_prefix} = length $self->{svn_path} ?
 	                       "$self->{svn_path}/" : '';
 	$self->{config} = $opts->{config};
+	$self->{pathnameencoding} = Git::config('svn.pathnameencoding');
 	return $self;
 }
 

[-- Attachment #4: git-completion.bash.patch --]
[-- Type: application/octet-stream, Size: 761 bytes --]

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d3037fc..50fc385 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -280,7 +280,8 @@ __git_ps1 ()
 		elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
 			if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
 				if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
-					git diff --no-ext-diff --quiet --exit-code || w="*"
+					is=$(git config diff.ignoreSubmodules)
+					git diff --no-ext-diff --quiet --exit-code --ignore-submodules=$is || w="*"
 					if git rev-parse --quiet --verify HEAD >/dev/null; then
 						git diff-index --cached --quiet HEAD -- || i="+"
 					else

^ permalink raw reply related

* [PATCH 0/5] Custom extended SHA-1 syntax
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy

Merry Christmas!

This series introduces two new syntax: @{~foo} and ^{~foo}.
Both syntax allow an external program to run and return the result. So
users can extend the ref/sha1 syntax as they want. External commands are
defined similar to aliases in config.

The former does ref->ref mapping. The latter does SHA-1->SHA-1
mapping. Both allow @{} and ^{} in the result respectively, so you can
make a chain of mapping (*). This should allow Kevin to add "search
the n-th commit that matches 'foo'" syntax.

I think this is something nice to have. For one thing, commonly used
mappings can be collected in config file and distributed. Shell requirements
can be loosened because you don't need shell backquotes
(OK I'm always on a shell so this is moot, but an IDE, maybe).

OK time for movies..

(*) They can also make infinite chain. I don't think I can stop them
from doing that.

Nguyễn Thái Ngọc Duy (5):
  alias: add functions to do param substitution and alias running
  get_sha1: allow custom SHA-1 mapping with $SHA1^{~alias} syntax
  sha1_name: move interpret_nth_prior_checkout closer to
    interpret_branch_name
  interpret_branch_name: takes @{u} code out and reorder the function
  get_sha1: allow custom ref mapping with $ref@{~alias} syntax

 Documentation/config.txt    |   18 +++
 Documentation/revisions.txt |   12 ++
 alias.c                     |  167 ++++++++++++++++++++++-
 cache.h                     |   13 ++
 sha1_name.c                 |  321 ++++++++++++++++++++++++++++++++----------
 t/t1511-rev-parse-caret.sh  |   45 ++++++
 t/t1512-rev-parse-at.sh     |   63 +++++++++
 7 files changed, 557 insertions(+), 82 deletions(-)
 create mode 100755 t/t1512-rev-parse-at.sh

-- 
1.7.3.3.476.g10a82

^ permalink raw reply

* [PATCH 1/5] alias: add functions to do param substitution and alias running
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy
In-Reply-To: <1293199669-19016-1-git-send-email-pclouds@gmail.com>

These functions help substitute %foo% in an alias command to real
values, then run the alias and return the first line from stdout.

The normal use case is:

    /* extract all %xxx% from cmd to params */
    extract_alias_params(cmd, params);
    param = lookup_alias_param(params, "%foo%");
    if (param)
        param->value = "value for %foo%";
    param = lookup_alias_param(params, "%bar%");
    if (param)
        param->value = "value for %bar%";
    /* substitute %foo% and %bar% */
    expand_alias_params(cmd, params);
    free_alias_params(params);
    if (!get_alias_oneline(alias, cmd, output))
        /* do something with output->buf here */

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 alias.c |  139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 cache.h |   11 +++++
 2 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/alias.c b/alias.c
index eb9f08b..6626bb0 100644
--- a/alias.c
+++ b/alias.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "run-command.h"
 
 static const char *alias_key;
 static char *alias_val;
@@ -85,3 +86,141 @@ int split_cmdline(char *cmdline, const char ***argv)
 const char *split_cmdline_strerror(int split_cmdline_errno) {
 	return split_cmdline_errors[-split_cmdline_errno-1];
 }
+
+int extract_alias_params(const char *cmd, struct alias_param **params)
+{
+	const char *s = cmd;
+	int nr_param = 0;
+
+	*params = NULL;
+	while (s && (s = strchr(s, '%')) != NULL) {
+		int len = strcspn(s+1, "% ")+2;
+		if (len < 2 || s[len-1] != '%')
+			return error("malformed parameter at %s", s);
+		nr_param++;
+		*params = xrealloc(*params, sizeof(struct alias_param)*nr_param);
+		(*params)[nr_param-1].param = xstrndup(s, len);
+		(*params)[nr_param-1].pos = s - cmd;
+		(*params)[nr_param-1].value = NULL;
+		s += len;
+	}
+
+	nr_param++;
+	*params = xrealloc(*params, sizeof(struct alias_param)*nr_param);
+	(*params)[nr_param-1].param = NULL;
+	(*params)[nr_param-1].value = NULL;
+	return 0;
+}
+
+struct alias_param *lookup_alias_param(struct alias_param *params, const char *param)
+{
+	int i;
+	for (i = 0; params[i].param; i++)
+		if (!strcmp(params[i].param, param))
+			return params+i;
+	return NULL;
+}
+
+int expand_alias_params(struct strbuf *cmd, const struct alias_param *params)
+{
+	int i, offset = 0;
+
+	/* TODO: quote for '!' commands */
+	for (i = 0; params[i].param; i++) {
+		if (!params[i].value)
+			return error("param %s not substituted", params[i].param);
+		strbuf_splice(cmd,
+			      params[i].pos + offset, strlen(params[i].param),
+			      params[i].value, strlen(params[i].value));
+		offset += strlen(params[i].value) - strlen(params[i].param);
+	}
+
+	return 0;
+}
+
+void free_alias_params(struct alias_param *params)
+{
+	int i;
+	for (i = 0; params[i].param; i++) {
+		free(params[i].param);
+		free(params[i].value);
+	}
+	free(params);
+}
+
+static void *wait_and_finish(void *arg)
+{
+	struct child_process *cp = arg;
+	char buf[1024];
+	while (xread(cp->out, buf, 1024) > 0)
+		;
+	close(cp->out);
+	finish_command(cp);
+	free(cp->argv);
+	free(cp);
+	return (void *) (intptr_t) 0;
+}
+
+static int start_support_alias(const char *alias, char *cmd, struct child_process **cpp)
+{
+	struct child_process *cp;
+	const char **argv;
+	int count;
+
+	cp = xmalloc(sizeof(struct child_process));
+	memset(cp, 0, sizeof(struct child_process));
+	cp->in = 0;
+	cp->out = -1;
+
+	if (cmd[0] == '!') {
+		argv = xmalloc(sizeof(*argv)*4);
+		argv[0] = "/bin/sh";
+		argv[1] = "-c";
+		argv[2] = cmd+1;
+		argv[3] = NULL;
+	}
+	else {
+		count = split_cmdline(cmd, &argv);
+		if (count < 0) {
+			free(cp);
+			return error("Bad alias %s: %s", cmd, split_cmdline_strerror(count));
+		}
+		cp->git_cmd = 1;
+	}
+
+	cp->argv = argv;
+	if (start_command(cp)) {
+		error("Failed to run %s", cmd);
+		free(cp);
+		free(argv);
+		return -1;
+	}
+	*cpp = cp;
+	return 0;
+}
+
+int get_alias_oneline(const char *alias, char *cmd, struct strbuf *ref)
+{
+	struct child_process *cp;
+	FILE *fp;
+	int ret;
+
+	ret = start_support_alias(alias, cmd, &cp);
+	if (ret)
+		return ret;
+
+	fp = fdopen(cp->out, "r");
+	ret = strbuf_getline(ref, fp, '\n');
+	if (!ret) {
+		/* let it finish, if there's error, users should know */
+#ifdef NO_PTHREADS
+		wait_and_finish(cp);
+#else
+		pthread_t thread;
+		pthread_create(&thread, NULL, wait_and_finish, cp);
+#endif
+		return 0;
+	}
+	wait_and_finish(cp);
+	return -1;
+}
diff --git a/cache.h b/cache.h
index e83bc2d..20a37ff 100644
--- a/cache.h
+++ b/cache.h
@@ -1113,10 +1113,21 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
 void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
+struct alias_param {
+	char *param;
+	int pos;
+	char *value;
+};
+
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
+int extract_alias_params(const char *cmd, struct alias_param **params);
+struct alias_param *lookup_alias_param(struct alias_param *params, const char *param);
+int expand_alias_params(struct strbuf *cmd, const struct alias_param *params);
+void free_alias_params(struct alias_param *params);
+int get_alias_oneline(const char *alias, char *cmd, struct strbuf *result);
 
 /* git.c */
 struct startup_info {
-- 
1.7.3.3.476.g10a82

^ permalink raw reply related

* [PATCH 2/5] get_sha1: allow custom SHA-1 mapping with $SHA1^{~alias} syntax
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy
In-Reply-To: <1293199669-19016-1-git-send-email-pclouds@gmail.com>

"alias" is defined in config as sha1.alias. %sha1% and %arg% in the
alias command will be substituted.

The alias command is supposed to return a piece of text that
get_sha1() can consume, preferably straight SHA-1.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config.txt    |    9 ++++
 Documentation/revisions.txt |    7 +++
 alias.c                     |   23 +++++++++--
 cache.h                     |    1 +
 sha1_name.c                 |   86 +++++++++++++++++++++++++++++++++++++++++++
 t/t1511-rev-parse-caret.sh  |   45 ++++++++++++++++++++++
 6 files changed, 166 insertions(+), 5 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 0f85793..0c4fb66 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -588,6 +588,15 @@ it will be treated as a shell command.  For example, defining
 executed from the top-level directory of a repository, which may
 not necessarily be the current directory.
 
+sha1.*::
+	Extended SHA-1 syntax aliases. These aliases are similar to alias.*
+	but each will correspond to a '{caret}\{{tilde}alias\}' syntax (see
+	linkgit:gitrevisions[7]). '%sha1%' in the alias command will be
+	substituted with resolved SHA-1 before the caret. If
+	'{caret}\{{tilde}alias:extra\}' syntax is used, '%arg%' in the
+	alias command will be substituted with 'extra'. The command is
+	supposed to return an extended SHA-1 syntax.
+
 am.keepcr::
 	If true, git-am will call git-mailsplit for patches in mbox format
 	with parameter '--keep-cr'. In this case git-mailsplit will
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 174fa8e..413f91c 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -106,6 +106,13 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
   and dereference the tag recursively until a non-tag object is
   found.
 
+* A suffix '{caret}' followed by an SHA-1 alias with a leading
+  '{tilde}', all enclosed in brace pair (e.g.
+  `v0.998{caret}\{{tilde}alias\}`) will invoke the corresponding
+  alias command, specified in config (see linkgit:git-config[1],
+  key 'sha1.*'). The command is supposed to return an extended
+  SHA-1.
+
 * A suffix '{caret}' to a revision parameter followed by a brace
   pair that contains a text led by a slash (e.g. `HEAD^{/fix nasty bug}`):
   this is the same as `:/fix nasty bug` syntax below except that
diff --git a/alias.c b/alias.c
index 6626bb0..29a9903 100644
--- a/alias.c
+++ b/alias.c
@@ -1,12 +1,12 @@
 #include "cache.h"
 #include "run-command.h"
 
-static const char *alias_key;
 static char *alias_val;
 
 static int alias_lookup_cb(const char *k, const char *v, void *cb)
 {
-	if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
+	const char *key = cb;
+	if (!strcmp(k, key)) {
 		if (!v)
 			return config_error_nonbool(k);
 		alias_val = xstrdup(v);
@@ -15,14 +15,27 @@ static int alias_lookup_cb(const char *k, const char *v, void *cb)
 	return 0;
 }
 
-char *alias_lookup(const char *alias)
+static char *generic_alias_lookup(const char *alias, const char *namespase)
 {
-	alias_key = alias;
+	struct strbuf key = STRBUF_INIT;
+	strbuf_addstr(&key, namespase);
+	strbuf_addstr(&key, alias);
 	alias_val = NULL;
-	git_config(alias_lookup_cb, NULL);
+	git_config(alias_lookup_cb, key.buf);
+	strbuf_release(&key);
 	return alias_val;
 }
 
+char *alias_lookup(const char *alias)
+{
+	return generic_alias_lookup(alias, "alias.");
+}
+
+char *sha1_alias_lookup(const char *alias)
+{
+	return generic_alias_lookup(alias, "sha1.");
+}
+
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
 static const char *split_cmdline_errors[] = {
diff --git a/cache.h b/cache.h
index 20a37ff..56a1b18 100644
--- a/cache.h
+++ b/cache.h
@@ -1120,6 +1120,7 @@ struct alias_param {
 };
 
 char *alias_lookup(const char *alias);
+char *sha1_alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
diff --git a/sha1_name.c b/sha1_name.c
index c5c59ce..3a98a50 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -529,6 +529,76 @@ struct object *peel_to_type(const char *name, int namelen,
 	}
 }
 
+/*
+ * Handle $SHA1^{~alias} syntax. Config key sha1.alias will be used.
+ * The following parameters are substituted:
+ *
+ *  - %sha1%  SHA-1 before ^{~alias}, required
+ *  - %arg%   extra arguments after colon in SHA1^{~alias:args}
+ *
+ * The alias returns a string, preferabbly SHA-1.
+ */
+static int peel_alias(const char *name, int len, const char *sp,
+		      const unsigned char *input_sha1,
+		      struct strbuf *result)
+{
+	struct alias_param *params, *param;
+	struct strbuf alias = STRBUF_INIT;
+	struct strbuf cmd = STRBUF_INIT;
+	char *s;
+	int ret, arg_len = 0;
+
+	if (name[len-1] != '}')
+		return -1;
+	len -= sp - name + 1; /* remove $SHA1^{~ and } */
+	if (!len)
+		return error("$SHA1^{~alias} syntax with no alias");
+
+	s = strchr(sp, ':');
+	if (s) {
+		arg_len = (sp + len) - (s + 1);
+		len = s - sp;
+	}
+	strbuf_add(&alias, sp, len);
+	s = sha1_alias_lookup(alias.buf);
+	if (!s) {
+		ret = error("unable to find SHA-1 alias '%s'", alias.buf);
+		goto done;
+	}
+	strbuf_attach(&cmd, s, strlen(s), strlen(s)+1);
+
+	extract_alias_params(cmd.buf, &params);
+	param = lookup_alias_param(params, "%sha1%");
+	if (!param) {		/* %sha1% is mandatory */
+		ret = error("%%sha1%% not found in alias '%s'", alias.buf);
+		goto done;
+	}
+	param->value = xstrdup(sha1_to_hex(input_sha1));
+
+	param = lookup_alias_param(params, "%arg%");
+	if (param && arg_len)
+		param->value = xstrndup(sp + len + 1,arg_len);
+	else if (param) {
+		ret = error("Alias '%s' needs arguments", alias.buf);
+		goto done;
+	}
+	else if (arg_len) {
+		ret = error("Arguments are given but alias '%s' does not need them", alias.buf);
+		goto done;
+	}
+
+	expand_alias_params(&cmd, params);
+	free_alias_params(params);
+
+	trace_printf("trace: sha1 alias expansion: %s => %s\n",
+		     alias.buf, cmd.buf);
+	ret = get_alias_oneline(alias.buf, cmd.buf, result);
+done:
+	strbuf_release(&alias);
+	strbuf_release(&cmd);
+	return ret;
+}
+
 static int peel_onion(const char *name, int len, unsigned char *sha1)
 {
 	unsigned char outer[20];
@@ -566,6 +636,22 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
 		expected_type = OBJ_NONE;
 	else if (sp[0] == '/')
 		expected_type = OBJ_COMMIT;
+	else if (sp[0] == '~') {
+		struct strbuf new_name = STRBUF_INIT;
+
+		if (get_sha1_1(name, sp - name - 2, outer))
+			return -1;
+
+		if (peel_alias(name, len, sp + 1, outer, &new_name))
+			return -1;
+		if (get_sha1_1(new_name.buf, new_name.len, sha1)) {
+			strbuf_release(&new_name);
+			return -1;
+		}
+
+		strbuf_release(&new_name);
+		return 0;
+	}
 	else
 		return -1;
 
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
index e043cb7..b99bfa2 100755
--- a/t/t1511-rev-parse-caret.sh
+++ b/t/t1511-rev-parse-caret.sh
@@ -70,4 +70,49 @@ test_expect_success 'ref^{/Initial}' '
 	test_cmp expected actual
 '
 
+test_expect_success 'ref^{~}' '
+	test_must_fail git rev-parse HEAD^{~}
+'
+
+test_expect_success 'ref^{~non-existent}' '
+	test_must_fail git rev-parse HEAD^{~non-existent}
+'
+
+test_expect_success 'ref^{~simple}' '
+	git config sha1.simple "rev-parse %sha1%" &&
+	test_must_fail git rev-parse master^{~simple:something} &&
+	git rev-parse master^{~simple} >actual &&
+	git rev-parse master >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{~external}' '
+	git config sha1.external "!echo %sha1%" &&
+	test_must_fail git rev-parse master^{~external:something} &&
+	git rev-parse master^{~external} >actual &&
+	git rev-parse master >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{~witharg:args}' '
+	git config sha1.witharg "!echo %sha1%^{%arg%}" &&
+	test_must_fail git rev-parse master^{~witharg} &&
+	git rev-parse master^{~witharg:tree} >actual &&
+	git rev-parse master^{tree} >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{~simple}^{~external}' '
+	git rev-parse master^{~simple}^{~external} >actual &&
+	git rev-parse master >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref^{~mapping}' '
+	git config sha1.mapping "!echo %sha1%^{~simple}" &&
+	git rev-parse master^{~mapping} >actual &&
+	git rev-parse master >expected &&
+	test_cmp expected actual
+'
+
 test_done
-- 
1.7.3.3.476.g10a82

^ permalink raw reply related

* [PATCH 3/5] sha1_name: move interpret_nth_prior_checkout closer to interpret_branch_name
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy
In-Reply-To: <1293199669-19016-1-git-send-email-pclouds@gmail.com>

interpret_nth_prior_checkout() is a subfunction of
interpret_branch_name(). So it's better to let them be close together
(i.e. not letting get_sha1_mb() stays in between).

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 sha1_name.c |   84 +++++++++++++++++++++++++++++-----------------------------
 1 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index 3a98a50..dcdf1e6 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -883,6 +883,48 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 	return 0;
 }
 
+int get_sha1_mb(const char *name, unsigned char *sha1)
+{
+	struct commit *one, *two;
+	struct commit_list *mbs;
+	unsigned char sha1_tmp[20];
+	const char *dots;
+	int st;
+
+	dots = strstr(name, "...");
+	if (!dots)
+		return get_sha1(name, sha1);
+	if (dots == name)
+		st = get_sha1("HEAD", sha1_tmp);
+	else {
+		struct strbuf sb;
+		strbuf_init(&sb, dots - name);
+		strbuf_add(&sb, name, dots - name);
+		st = get_sha1(sb.buf, sha1_tmp);
+		strbuf_release(&sb);
+	}
+	if (st)
+		return st;
+	one = lookup_commit_reference_gently(sha1_tmp, 0);
+	if (!one)
+		return -1;
+
+	if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+		return -1;
+	two = lookup_commit_reference_gently(sha1_tmp, 0);
+	if (!two)
+		return -1;
+	mbs = get_merge_bases(one, two, 1);
+	if (!mbs || mbs->next)
+		st = -1;
+	else {
+		st = 0;
+		hashcpy(sha1, mbs->item->object.sha1);
+	}
+	free_commit_list(mbs);
+	return st;
+}
+
 /*
  * Parse @{-N} syntax, return the number of characters parsed
  * if successful; otherwise signal an error with negative value.
@@ -931,48 +973,6 @@ release_return:
 	return retval;
 }
 
-int get_sha1_mb(const char *name, unsigned char *sha1)
-{
-	struct commit *one, *two;
-	struct commit_list *mbs;
-	unsigned char sha1_tmp[20];
-	const char *dots;
-	int st;
-
-	dots = strstr(name, "...");
-	if (!dots)
-		return get_sha1(name, sha1);
-	if (dots == name)
-		st = get_sha1("HEAD", sha1_tmp);
-	else {
-		struct strbuf sb;
-		strbuf_init(&sb, dots - name);
-		strbuf_add(&sb, name, dots - name);
-		st = get_sha1(sb.buf, sha1_tmp);
-		strbuf_release(&sb);
-	}
-	if (st)
-		return st;
-	one = lookup_commit_reference_gently(sha1_tmp, 0);
-	if (!one)
-		return -1;
-
-	if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
-		return -1;
-	two = lookup_commit_reference_gently(sha1_tmp, 0);
-	if (!two)
-		return -1;
-	mbs = get_merge_bases(one, two, 1);
-	if (!mbs || mbs->next)
-		st = -1;
-	else {
-		st = 0;
-		hashcpy(sha1, mbs->item->object.sha1);
-	}
-	free_commit_list(mbs);
-	return st;
-}
-
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
-- 
1.7.3.3.476.g10a82

^ permalink raw reply related

* [PATCH 4/5] interpret_branch_name: takes @{u} code out and reorder the function
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy
In-Reply-To: <1293199669-19016-1-git-send-email-pclouds@gmail.com>

Previously interpret_branch_name() is structured as:

 - check for @{-N}
 - if there's still some more chars left, recurse to try again
 - check for @{upstream}

Now it looks like

 - check for @{-N}
 - check for @{upstream}
 - still more? recurse

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 sha1_name.c |   99 ++++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 57 insertions(+), 42 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index dcdf1e6..cdf14c7 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -973,6 +973,27 @@ release_return:
 	return retval;
 }
 
+static int interpret_at_upstream(const char *at, struct strbuf *real_ref)
+{
+	struct branch *upstream;
+	int cplen = strlen(at);
+	int len;
+	char *upstream_ref;
+
+	len = upstream_mark(at, cplen);
+	if (!len)
+		return -1;
+	upstream = branch_get(real_ref->buf);
+	if (!upstream
+	    || !upstream->merge
+	    || !upstream->merge[0]->dst)
+		return error("No upstream branch found for '%s'", real_ref->buf);
+	upstream_ref = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
+	cplen = strlen(upstream_ref);
+	strbuf_attach(real_ref, upstream_ref, cplen, cplen+1);
+	return len;
+}
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -996,55 +1017,49 @@ release_return:
  */
 int interpret_branch_name(const char *name, struct strbuf *buf)
 {
-	char *cp;
-	struct branch *upstream;
 	int namelen = strlen(name);
-	int len = interpret_nth_prior_checkout(name, buf);
-	int tmp_len;
-
-	if (!len)
-		return len; /* syntax Ok, not enough switches */
-	if (0 < len && len == namelen)
-		return len; /* consumed all */
-	else if (0 < len) {
-		/* we have extra data, which might need further processing */
-		struct strbuf tmp = STRBUF_INIT;
-		int used = buf->len;
-		int ret;
-
-		strbuf_add(buf, name + len, namelen - len);
-		ret = interpret_branch_name(buf->buf, &tmp);
-		/* that data was not interpreted, remove our cruft */
-		if (ret < 0) {
-			strbuf_setlen(buf, used);
-			return len;
-		}
-		strbuf_reset(buf);
-		strbuf_addbuf(buf, &tmp);
-		strbuf_release(&tmp);
-		/* tweak for size of {-N} versus expanded ref name */
-		return ret - used + len;
-	}
+	struct strbuf tmp = STRBUF_INIT;
+	int ret, len = -1, used = 0;
+	const char *cp;
 
 	cp = strchr(name, '@');
 	if (!cp)
 		return -1;
-	tmp_len = upstream_mark(cp, namelen - (cp - name));
-	if (!tmp_len)
+	if (cp == name)
+		len = interpret_nth_prior_checkout(cp, buf);
+	else {
+		strbuf_reset(buf);
+		strbuf_add(buf, name, cp - name);
+		used += cp - name;
+	}
+
+	if (len == -1)
+		len = interpret_at_upstream(cp, buf);
+	if (len == -1) {
+		strbuf_reset(buf);
 		return -1;
-	len = cp + tmp_len - name;
-	cp = xstrndup(name, cp - name);
-	upstream = branch_get(*cp ? cp : NULL);
-	if (!upstream
-	    || !upstream->merge
-	    || !upstream->merge[0]->dst)
-		return error("No upstream branch found for '%s'", cp);
-	free(cp);
-	cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
+	}
+
+	if (!len)
+		return len; /* syntax Ok, not enough switches */
+
+	used += len;
+	if (used == namelen)
+		return used; /* consumed all */
+
+	/* we have extra data, which might need further processing */
+	len = buf->len;
+	strbuf_add(buf, name + used, namelen - used);
+	ret = interpret_branch_name(buf->buf, &tmp);
+	/* that data was not interpreted, remove our cruft */
+	if (ret < 0) {
+		strbuf_setlen(buf, len);
+		return used;
+	}
 	strbuf_reset(buf);
-	strbuf_addstr(buf, cp);
-	free(cp);
-	return len;
+	strbuf_addbuf(buf, &tmp);
+	strbuf_release(&tmp);
+	return used + (ret - len);
 }
 
 int strbuf_branchname(struct strbuf *sb, const char *name)
-- 
1.7.3.3.476.g10a82

^ permalink raw reply related

* [PATCH 5/5] get_sha1: allow custom ref mapping with $ref@{~alias} syntax
From: Nguyễn Thái Ngọc Duy @ 2010-12-24 14:07 UTC (permalink / raw)
  To: git; +Cc: kevin, Nguyễn Thái Ngọc Duy
In-Reply-To: <1293199669-19016-1-git-send-email-pclouds@gmail.com>

Similar to $SHA1^{~alias}, the alias is defined as ref.alias. It is
supposed to return something that can resolve to a ref.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config.txt    |    9 +++++
 Documentation/revisions.txt |    5 +++
 alias.c                     |    5 +++
 cache.h                     |    1 +
 sha1_name.c                 |   70 +++++++++++++++++++++++++++++++++++++++++-
 t/t1512-rev-parse-at.sh     |   63 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 151 insertions(+), 2 deletions(-)
 create mode 100755 t/t1512-rev-parse-at.sh

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 0c4fb66..a20b467 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -597,6 +597,15 @@ sha1.*::
 	alias command will be substituted with 'extra'. The command is
 	supposed to return an extended SHA-1 syntax.
 
+ref.*::
+	Ref translation aliases. These aliases are similar to alias.*
+	but each will correspond to a '@\{{tilde}alias\}' syntax (see
+	linkgit:gitrevisions[7]). '%ref%' in the alias command will be
+	substituted with the ref before the '@'. If
+	'@\{{tilde}alias:extra\}' syntax is used, '%arg%' in the alias
+	command will be substituted with 'extra'. The command is supposed
+	to return a ref.
+
 am.keepcr::
 	If true, git-am will call git-mailsplit for patches in mbox format
 	with parameter '--keep-cr'. In this case git-mailsplit will
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 413f91c..86b0b63 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -80,6 +80,11 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
   the branch the ref is set to build on top of.  Missing ref defaults
   to the current branch.
 
+* A ref followed by '@\{{tilde}alias\}' will invoke the corresponding
+  alias command, specified in config (see linkgit:git-config[1],
+  key 'ref.*'). The command is supposed to return a ref. The
+  result ref can contain '@\{..\}' syntax.
+
 * A suffix '{caret}' to a revision parameter (e.g. 'HEAD{caret}') means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   'rev{caret}'
diff --git a/alias.c b/alias.c
index 29a9903..242401e 100644
--- a/alias.c
+++ b/alias.c
@@ -36,6 +36,11 @@ char *sha1_alias_lookup(const char *alias)
 	return generic_alias_lookup(alias, "sha1.");
 }
 
+char *ref_alias_lookup(const char *alias)
+{
+	return generic_alias_lookup(alias, "ref.");
+}
+
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
 static const char *split_cmdline_errors[] = {
diff --git a/cache.h b/cache.h
index 56a1b18..1d4ce65 100644
--- a/cache.h
+++ b/cache.h
@@ -1121,6 +1121,7 @@ struct alias_param {
 
 char *alias_lookup(const char *alias);
 char *sha1_alias_lookup(const char *alias);
+char *ref_alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
diff --git a/sha1_name.c b/sha1_name.c
index cdf14c7..2734f24 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -359,7 +359,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 	if (len && str[len-1] == '}') {
 		for (at = len-2; at >= 0; at--) {
 			if (str[at] == '@' && str[at+1] == '{') {
-				if (!upstream_mark(str + at, len - at)) {
+				if (!upstream_mark(str + at, len - at) &&
+				    str[at+2] != '~') {
 					reflog_len = (len-1) - (at+2);
 					len = at;
 				}
@@ -994,6 +995,69 @@ static int interpret_at_upstream(const char *at, struct strbuf *real_ref)
 	return len;
 }
 
+static int interpret_at_alias(const char *at, struct strbuf *real_ref)
+{
+	struct alias_param *params, *param;
+	struct strbuf alias = STRBUF_INIT;
+	struct strbuf cmd = STRBUF_INIT;
+	char *s;
+	int ret, len, arg_len = 0;
+
+	if (at[1] != '{' || at[2] != '~')
+		return -1;
+	len = strcspn(at + 3, ":}");
+	if (!len)
+		return error("$ref@{~alias} syntax with no alias");
+	if (at[3 + len] == ':') {
+		s = strchr(at + 3 + len + 1, '}');
+		if (!s)
+			return error("$ref@{~alias} does not end with }");
+		arg_len = s - at - (3 + len + 1);
+	}
+	strbuf_add(&alias, at + 3, len);
+	s = ref_alias_lookup(alias.buf);
+	if (!s) {
+		ret = error("unable to find ref alias '%s'", alias.buf);
+		goto done;
+	}
+
+	strbuf_attach(&cmd, s, strlen(s), strlen(s)+1);
+	extract_alias_params(cmd.buf, &params);
+	param = lookup_alias_param(params, "%ref%");
+	if (param && real_ref->len)
+		param->value = xstrdup(real_ref->buf);
+	else if (param) {
+		ret = error("%%ref%% not found in alias '%s'", cmd.buf);
+		goto done;
+	}
+	else if (real_ref->len) {
+		ret = error("%%ref%% is required but alias '%s' is not followed by a ref", cmd.buf);
+		goto done;
+	}
+	param = lookup_alias_param(params, "%arg%");
+	if (param && arg_len) {
+		param->value = xstrndup(at + len + 4, arg_len);
+		len += arg_len + 1;
+	}
+	else if (param) {
+		ret = error("Alias '%s' needs arguments", alias.buf);
+		goto done;
+	}
+	else if (arg_len) {
+		ret = error("Arguments are given but alias '%s' does not need them", alias.buf);
+		goto done;
+	}
+	expand_alias_params(&cmd, params);
+	free_alias_params(params);
+
+	trace_printf("trace: ref alias expansion: %s => %s\n", alias.buf, cmd.buf);
+	ret = get_alias_oneline(alias.buf, cmd.buf, real_ref) ? -1 : len + 4;
+done:
+	strbuf_release(&alias);
+	strbuf_release(&cmd);
+	return ret;
+}
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -1035,6 +1099,8 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
 
 	if (len == -1)
 		len = interpret_at_upstream(cp, buf);
+	if (len == -1)
+		len = interpret_at_alias(cp, buf);
 	if (len == -1) {
 		strbuf_reset(buf);
 		return -1;
@@ -1044,7 +1110,7 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
 		return len; /* syntax Ok, not enough switches */
 
 	used += len;
-	if (used == namelen)
+	if (used == namelen && !strstr(buf->buf, "@{"))
 		return used; /* consumed all */
 
 	/* we have extra data, which might need further processing */
diff --git a/t/t1512-rev-parse-at.sh b/t/t1512-rev-parse-at.sh
new file mode 100755
index 0000000..6594146
--- /dev/null
+++ b/t/t1512-rev-parse-at.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='tests for $ref@{something}'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit foo &&
+	git branch foo &&
+	test_commit bar &&
+	git branch bar &&
+	test_commit zoo-bar &&
+	git branch zoo/bar &&
+	test_commit zoo-zoo-bar &&
+	git branch zoo/zoo/bar
+'
+
+test_expect_success 'ref@{~}' '
+	test_must_fail git rev-parse HEAD@{~}
+'
+
+test_expect_success 'ref@{~non-existent}' '
+	test_must_fail git rev-parse HEAD@{~non-existent}
+'
+
+test_expect_success 'ref@{~simple}' '
+	git config ref.simple "symbolic-ref %ref%" &&
+	test_must_fail git rev-parse HEAD@{~simple:something} &&
+	git rev-parse HEAD@{~simple} >actual &&
+	git rev-parse HEAD >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref@{~external}' '
+	git config ref.external "!echo zoo/%ref%" &&
+	test_must_fail git rev-parse bar@{~external:something} &&
+	git rev-parse bar@{~external} >actual &&
+	git rev-parse zoo/bar >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref@{~witharg:args}' '
+	git config ref.witharg "!echo %ref%/%arg%" &&
+	test_must_fail git rev-parse zoo@{~witharg} &&
+	git rev-parse zoo@{~witharg:bar} >actual &&
+	git rev-parse zoo/bar >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref@{~external}@{~external}' '
+	git rev-parse bar@{~external}@{~external} >actual &&
+	git rev-parse zoo/zoo/bar >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'ref@{~mapping}' '
+	git config ref.mapping "!echo %ref%@{~external}@{~external}" &&
+	git rev-parse bar@{~mapping} >actual &&
+	git rev-parse zoo/zoo/bar >expected &&
+	test_cmp expected actual
+'
+
+test_done
-- 
1.7.3.3.476.g10a82

^ permalink raw reply related

* Re: Bug reports & patches: git-svn, git-completion, git diff hunk headers for Pascal
From: Matthieu Moy @ 2010-12-24 14:10 UTC (permalink / raw)
  To: Алексей Крезов
  Cc: git
In-Reply-To: <6110634424.20101224154626@mail.ru>

Алексей Крезов <zapped@mail.ru> writes:

> Hello!

Hi,

> I've found some bugs in Git:

Thanks for your interest and your patches, but please read
Documentation/SubmittingPatches in th git sources. In particular:

* Patches cannot be included without your signoff.

* Please send the patches inline, together with the commit message, so
  that review is easy, and that our maintener can easily apply it.
  Doing local commits, and using git send-email will make it easy.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply

* Re: Bug reports & patches: git-svn, git-completion, git diff hunk headers for Pascal
From: Tay Ray Chuan @ 2010-12-24 14:25 UTC (permalink / raw)
  To: Matthieu Moy
  Cc: Алексей Крезов,
	git
In-Reply-To: <vpq1v575kiq.fsf@bauges.imag.fr>

2010/12/24 Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>:
> Алексей Крезов <zapped@mail.ru> writes:
>
>> Hello!
>
> Hi,
>
>> I've found some bugs in Git:
>
> Thanks for your interest and your patches, but please read
> Documentation/SubmittingPatches in th git sources. In particular:

for your convenience:

  https://github.com/git/git/blob/master/Documentation/SubmittingPatches

-- 
Cheers,
Ray Chuan

^ permalink raw reply

* Re: What's cooking in git.git (Dec 2010, #06; Tue, 21)
From: Joshua Jensen @ 2010-12-24 16:53 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: Junio C Hamano, git
In-Reply-To: <AANLkTikaBfVw-4eD9qeGSdM5xKxq-gJ-3oAe4B2qt4od@mail.gmail.com>

----- Original Message -----
From: Nguyen Thai Ngoc Duy
Date: 12/23/2010 6:39 PM
> If you have not known already, path in "git log ref -- path" must be
> case sensitive. Solving that is not hard: ce_path_match() and
> tree_entry_interesting() are the ones that do path matching. Those
> functions are nearly replaced in this series. I'll add
> case-insensitive support to them, so you can worry about other places.
As I recall (I'd have to examine other unsubmitted case insensitivity 
patches), merely adding case insensitivity support to ce_path_match() is 
not enough.  The cache is stored alphabetically in a case sensitive 
fashion.  That means filenames starting with 'A' are stored in a 
completely different place than filenames starting with 'a'.  Certain 
parts of the code call ce_path_match() and then walk the cache 
sequentially for possible matches.  It aborts long before hitting the 
'a' filename.

I have a patch that appears to resolve most of these issues.  For 
core.ignorecase=true, when the cache is read, it is re-sorted 
alphabetically in a case insensitive manner.  ce_path_match() still 
needs fixes, but the rest were covered by the case insensitive cache.  
'A' and 'a' are not interleaved, and the combination of sequential and 
binary(?) searches Git uses are successful.  Finally, when the cache is 
written, I re-sort the cache in a case insensitive fashion.

Hmmm... I think this was also needed for the *_name_compare() functions, 
too.

Anyway, just something to consider.

Josh

^ permalink raw reply

* Re: What's cooking in git.git (Dec 2010, #06; Tue, 21)
From: Junio C Hamano @ 2010-12-24 19:23 UTC (permalink / raw)
  To: Joshua Jensen; +Cc: Nguyen Thai Ngoc Duy, git
In-Reply-To: <4D13DCDD.3050300@workspacewhiz.com>

Joshua Jensen <jjensen@workspacewhiz.com> writes:

> From: Junio C Hamano
> Date: 12/23/2010 10:17 AM
>> Nguyen Thai Ngoc Duy<pclouds@gmail.com>  writes:
>>
>>> On Wed, Dec 22, 2010 at 8:59 AM, Junio C Hamano<gitster@pobox.com>  wrote:
>>> With jj/icase-directory merged to master, match_pathspec() and
>>> match_pathspec_depth() now diverse again.
>>>
>>> When I wrote match_pathspec_depth(), I assumed that match_pathspec()
>>> would not change much and I would have more time for converting the
>>> rest of git to use match_*_depth(). Looks like I need to add case
>>> insensitive matching to struct pathspec and friends then remove
>>> match_pathspec() in this series too. At least if somebody changes
>>> match_pathspec() again, it would cause a conflict so I can catch it.
>> While this topic is something I have long wanted to see, I have started
>> feeling that this needs to cook a bit longer than be in the next release.
>> So perhaps the best course of action might be to rebase the series once
>> after the 1.7.4 feature freeze, cook it in 'next' for a while and make it
>> part of the release after that.  I think at that point we may probably
>> want to have other changes that are not strictly backward compatible but
>> their incompatibilities do not matter in practice (e.g. cquoting pathspecs
>> in the attributes file comes to mind, but I am sure there will be other
>> changes that people wanted to have but we held them off due to worries on
>> compatibility).
>>
>> What do you think?
> ...
> Having said that, I have had 100 people using the jj/icase-directory
> series on Windows daily for 4 months now without issue.  Prior to
> that, a majority of the series had been used for a full year by a
> dozen people....

Just to make sure nobody misunderstood me, what I was proposing to put on
hold was not the "case insensitivity" work of yours, which is already
scheduled to be part of the coming release.

^ permalink raw reply

* Re: What's cooking in git.git (Dec 2010, #06; Tue, 21)
From: Joshua Jensen @ 2010-12-24 20:02 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Nguyen Thai Ngoc Duy, git
In-Reply-To: <7vzkrvynz5.fsf@alter.siamese.dyndns.org>

----- Original Message -----
From: Junio C Hamano
Date: 12/24/2010 12:23 PM
> Joshua Jensen<jjensen@workspacewhiz.com>  writes:
>
>> Having said that, I have had 100 people using the jj/icase-directory
>> series on Windows daily for 4 months now without issue.  Prior to
>> that, a majority of the series had been used for a full year by a
>> dozen people....
> Just to make sure nobody misunderstood me, what I was proposing to put on
> hold was not the "case insensitivity" work of yours, which is already
> scheduled to be part of the coming release.
Ah.  I completely misinterpreted your statement.

Well, that's exciting.  After it goes live, I'll have some more case 
insensitivity patches forthcoming for discussion.

-Josh

^ permalink raw reply

* [PATCH 2/3] Fixes bug: git-svn: svn.pathnameencoding is not respected with dcommit/set-tree
From: Zapped @ 2010-12-25  1:20 UTC (permalink / raw)
  To: git
In-Reply-To: <1293240049-7744-1-git-send-email-zapped@mail.ru>

git-svn dcommit/set-tree fails when svn.pathnameencoding is set for native OS encoding (e.g. cp1251 for Windows) though git-svn fetch/clone works well

Signed-off-by: Zapped <zapped@mail.ru>
---
 git-svn.perl |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 757de82..399bf4c 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -4451,6 +4451,7 @@ sub new {
 	$self->{path_prefix} = length $self->{svn_path} ?
 	                       "$self->{svn_path}/" : '';
 	$self->{config} = $opts->{config};
+	$self->{pathnameencoding} = Git::config('svn.pathnameencoding');
 	return $self;
 }
 
-- 
1.7.3.4.3.g3f811

^ permalink raw reply related

* [PATCH 1/3] Fixes bug: git-diff: class methods are not detected in hunk headers for Pascal
From: Zapped @ 2010-12-25  1:20 UTC (permalink / raw)
  To: git

Signed-off-by: Zapped <zapped@mail.ru>
---
 userdiff.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index f9e05b5..259a382 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -52,7 +52,7 @@ PATTERNS("objc",
 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
 	 "|[^[:space:]]|[\x80-\xff]+"),
 PATTERNS("pascal",
-	 "^((procedure|function|constructor|destructor|interface|"
+	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
 		"implementation|initialization|finalization)[ \t]*.*)$"
 	 "\n"
 	 "^(.*=[ \t]*(class|record).*)$",
-- 
1.7.3.4.3.g3f811

^ permalink raw reply related

* [PATCH 3/3] Fixes bug: GIT_PS1_SHOWDIRTYSTATE is no not respect diff.ignoreSubmodules config variable
From: Zapped @ 2010-12-25  1:20 UTC (permalink / raw)
  To: git
In-Reply-To: <1293240049-7744-1-git-send-email-zapped@mail.ru>

Signed-off-by: Zapped <zapped@mail.ru>
---
 contrib/completion/git-completion.bash |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d3037fc..50fc385 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -280,7 +280,8 @@ __git_ps1 ()
 		elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
 			if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
 				if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
-					git diff --no-ext-diff --quiet --exit-code || w="*"
+					is=$(git config diff.ignoreSubmodules)
+					git diff --no-ext-diff --quiet --exit-code --ignore-submodules=$is || w="*"
 					if git rev-parse --quiet --verify HEAD >/dev/null; then
 						git diff-index --cached --quiet HEAD -- || i="+"
 					else
-- 
1.7.3.4.3.g3f811

^ 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