Git development
 help / color / mirror / Atom feed
* Re: [PATCH v2] make diff --color-words customizable
From: Johannes Schindelin @ 2009-01-10 11:45 UTC (permalink / raw)
  To: Thomas Rast; +Cc: git, Junio C Hamano, Teemu Likonen
In-Reply-To: <200901101225.10719.trast@student.ethz.ch>

Hi,

On Sat, 10 Jan 2009, Thomas Rast wrote:

> Johannes Schindelin wrote:
> > BTW I did not really think about the issue you raised about the newlines, 
> > as I seemed to remember that the idea was to substitute all non-word 
> > characters with newlines, so that the offsets in the substituted text are 
> > the same as in the original text.
> 
> Ok, so here's a very simple example: Suppose you have the word regex
> 'x+|y+' and compare these two lines:
> 
> A: xxyyxy
> B: xyxyy

Ah, I see.

> > So I still find your patch way too large
> 
> I can't think of a simpler way to do it, and yours unfortunately doesn't 
> work.

Well, the thing I tried to hint at: it is not good to have a monster 
patch, as nobody will review it.

In your case, I imagine it would be much easier to get reviewers if you 
had

	patch 1/4 refactor color-words to allow for 0-character word 
		boundaries
	patch 2/4 allow regular expressions to define what makes a word
	patch 3/4 add option to specify word boundary regexps via
		attributes
	patch 4/4 test word boundary regexps

And I admit that I documented the code lousily, but that does not mean 
that you should repeat that mistake.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH v2] t7501-commit.sh: explicitly check that -F prevents invoking the editor
From: Johannes Schindelin @ 2009-01-10 11:48 UTC (permalink / raw)
  To: Adeodato Simó; +Cc: git, gitster
In-Reply-To: <20090110103252.GA32151@chistera.yi.org>

[-- Attachment #1: Type: TEXT/PLAIN, Size: 816 bytes --]

Hi,

On Sat, 10 Jan 2009, Adeodato Simó wrote:

> * Johannes Schindelin [Sat, 10 Jan 2009 11:19:43 +0100]:
> 
> > >  test_expect_success '--signoff' '
> > >  	echo "yet another content *narf*" >> foo &&
> > > -	echo "zort" | (
> > > -		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
> > > -		git commit -s -F - foo
> > > -	) &&
> > > +	echo "zort" | git commit -s -F - foo &&
> > >  	git cat-file commit HEAD | sed "1,/^$/d" > output &&
> > >  	test_cmp expect output
> > >  '
> 
> > AFAICT this still tests if -F - launches an editor, except that it _does_ 
> > launch the editor, waiting for the user to quit the editor.  Which is bad.
> 
> The default value of VISUAL for the test suite is ":" AFAICS. Hence,
> even if it's called, it will return immediately.

Ah.  Okay then.

Sorry for the noise,
Dscho

^ permalink raw reply

* Re: Git - Pushing to a production website
From: Sitaram Chamarty @ 2009-01-10 11:50 UTC (permalink / raw)
  To: git
In-Reply-To: <20090109222344.3539138a@family.dyweni.com>

On 2009-01-10, <4jxDQ6FQee2H@dyweni.com> <4jxDQ6FQee2H@dyweni.com> wrote:

> Our company's website is stored in a GIT Repository.
>
> The repository is coded for our test server.  When we push updates to
> the production server, have manually run a script to patch several
> files to make the code work on the production server (i.e. port
> numbers, etc).
>
> I'd like to write a script to email me whenever someone changes files
> on the production server without checking those changes back into git
> (i.e. running 'git status | grep "nothing to commit" ...').

Shouldn't they change it in a sandbox and push it to prod
when it gets done instead of directly changing on prod?

> However, this approach get confused by the files patched to work
> correctly.
>
> Is there any way to 'save' those patched files so they don't get
> reported by 'git status', yet not mung up the git history every time
> we push out an update?

If you can enforce no changes directly to prod, you can have
the prod server's "master" branch be the one that QA or
whatever pushes to (no direct changes on prod).

You'd manually (one-time) create a branch called
prod_patches where you'd make just the changes needed (port
numbers etc as you said).

This would be the "checked out" branch.

On each push to master, a hook would just "cd wherever; git
rebase master"; the port changes would carry over.

^ permalink raw reply

* [PATCH] add is_dot_or_dotdot inline function
From: Alexander Potashev @ 2009-01-10 12:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Alexander Potashev

A new inline function is_dot_or_dotdot is used to check if the
directory name is either "." or "..". It returns a non-zero value if
the given string is "." or "..". It's applicable to a lot of Git
source code.

Signed-off-by: Alexander Potashev <aspotashev@gmail.com>
---
 builtin-count-objects.c |    5 ++---
 builtin-fsck.c          |   14 ++++----------
 builtin-prune.c         |   14 ++++----------
 builtin-rerere.c        |   11 +++++------
 dir.c                   |   12 ++++--------
 dir.h                   |    6 ++++++
 entry.c                 |    5 ++---
 remote.c                |    6 ++----
 transport.c             |    4 +---
 9 files changed, 30 insertions(+), 47 deletions(-)

diff --git a/builtin-count-objects.c b/builtin-count-objects.c
index ab35b65..62fd1f0 100644
--- a/builtin-count-objects.c
+++ b/builtin-count-objects.c
@@ -5,6 +5,7 @@
  */
 
 #include "cache.h"
+#include "dir.h"
 #include "builtin.h"
 #include "parse-options.h"
 
@@ -21,9 +22,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
 		const char *cp;
 		int bad = 0;
 
-		if ((ent->d_name[0] == '.') &&
-		    (ent->d_name[1] == 0 ||
-		     ((ent->d_name[1] == '.') && (ent->d_name[2] == 0))))
+		if (is_dot_or_dotdot(ent->d_name))
 			continue;
 		for (cp = ent->d_name; *cp; cp++) {
 			int ch = *cp;
diff --git a/builtin-fsck.c b/builtin-fsck.c
index 297b2c4..79b87ed 100644
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
@@ -10,6 +10,7 @@
 #include "tree-walk.h"
 #include "fsck.h"
 #include "parse-options.h"
+#include "dir.h"
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
@@ -395,19 +396,12 @@ static void fsck_dir(int i, char *path)
 	while ((de = readdir(dir)) != NULL) {
 		char name[100];
 		unsigned char sha1[20];
-		int len = strlen(de->d_name);
 
-		switch (len) {
-		case 2:
-			if (de->d_name[1] != '.')
-				break;
-		case 1:
-			if (de->d_name[0] != '.')
-				break;
+		if (is_dot_or_dotdot(de->d_name))
 			continue;
-		case 38:
+		if (strlen(de->d_name) == 38) {
 			sprintf(name, "%02x", i);
-			memcpy(name+2, de->d_name, len+1);
+			memcpy(name+2, de->d_name, 39);
 			if (get_sha1_hex(name, sha1) < 0)
 				break;
 			add_sha1_list(sha1, DIRENT_SORT_HINT(de));
diff --git a/builtin-prune.c b/builtin-prune.c
index 7b4ec80..545e9c1 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -5,6 +5,7 @@
 #include "builtin.h"
 #include "reachable.h"
 #include "parse-options.h"
+#include "dir.h"
 
 static const char * const prune_usage[] = {
 	"git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
@@ -61,19 +62,12 @@ static int prune_dir(int i, char *path)
 	while ((de = readdir(dir)) != NULL) {
 		char name[100];
 		unsigned char sha1[20];
-		int len = strlen(de->d_name);
 
-		switch (len) {
-		case 2:
-			if (de->d_name[1] != '.')
-				break;
-		case 1:
-			if (de->d_name[0] != '.')
-				break;
+		if (is_dot_or_dotdot(de->d_name))
 			continue;
-		case 38:
+		if (strlen(de->d_name) == 38) {
 			sprintf(name, "%02x", i);
-			memcpy(name+2, de->d_name, len+1);
+			memcpy(name+2, de->d_name, 39);
 			if (get_sha1_hex(name, sha1) < 0)
 				break;
 
diff --git a/builtin-rerere.c b/builtin-rerere.c
index d4dec6b..bd8fc77 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "dir.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "xdiff/xdiff.h"
@@ -59,17 +60,15 @@ static void garbage_collect(struct string_list *rr)
 	git_config(git_rerere_gc_config, NULL);
 	dir = opendir(git_path("rr-cache"));
 	while ((e = readdir(dir))) {
-		const char *name = e->d_name;
-		if (name[0] == '.' &&
-		    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
+		if (is_dot_or_dotdot(e->d_name))
 			continue;
-		then = rerere_created_at(name);
+		then = rerere_created_at(e->d_name);
 		if (!then)
 			continue;
-		cutoff = (has_resolution(name)
+		cutoff = (has_resolution(e->d_name)
 			  ? cutoff_resolve : cutoff_noresolve);
 		if (then < now - cutoff * 86400)
-			string_list_append(name, &to_remove);
+			string_list_append(e->d_name, &to_remove);
 	}
 	for (i = 0; i < to_remove.nr; i++)
 		unlink_rr_item(to_remove.items[i].string);
diff --git a/dir.c b/dir.c
index 0131983..3347f46 100644
--- a/dir.c
+++ b/dir.c
@@ -585,10 +585,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
 			int len, dtype;
 			int exclude;
 
-			if ((de->d_name[0] == '.') &&
-			    (de->d_name[1] == 0 ||
-			     !strcmp(de->d_name + 1, ".") ||
-			     !strcmp(de->d_name + 1, "git")))
+			if (is_dot_or_dotdot(de->d_name) ||
+			     !strcmp(de->d_name, ".git"))
 				continue;
 			len = strlen(de->d_name);
 			/* Ignore overly long pathnames! */
@@ -793,10 +791,8 @@ int remove_dir_recursively(struct strbuf *path, int only_empty)
 	len = path->len;
 	while ((e = readdir(dir)) != NULL) {
 		struct stat st;
-		if ((e->d_name[0] == '.') &&
-		    ((e->d_name[1] == 0) ||
-		     ((e->d_name[1] == '.') && e->d_name[2] == 0)))
-			continue; /* "." and ".." */
+		if (is_dot_or_dotdot(e->d_name))
+			continue;
 
 		strbuf_setlen(path, len);
 		strbuf_addstr(path, e->d_name);
diff --git a/dir.h b/dir.h
index 768425a..e1640a8 100644
--- a/dir.h
+++ b/dir.h
@@ -77,6 +77,12 @@ extern int file_exists(const char *);
 extern char *get_relative_cwd(char *buffer, int size, const char *dir);
 extern int is_inside_dir(const char *dir);
 
+static inline int is_dot_or_dotdot(const char *name)
+{
+	return name[0] == '.' && (name[1] == '\0' ||
+		(name[1] == '.' && name[2] == '\0')); /* "." and ".." */
+}
+
 extern void setup_standard_excludes(struct dir_struct *dir);
 extern int remove_dir_recursively(struct strbuf *path, int only_empty);
 
diff --git a/entry.c b/entry.c
index aa2ee46..5f24816 100644
--- a/entry.c
+++ b/entry.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "blob.h"
+#include "dir.h"
 
 static void create_directories(const char *path, const struct checkout *state)
 {
@@ -62,9 +63,7 @@ static void remove_subtree(const char *path)
 	*name++ = '/';
 	while ((de = readdir(dir)) != NULL) {
 		struct stat st;
-		if ((de->d_name[0] == '.') &&
-		    ((de->d_name[1] == 0) ||
-		     ((de->d_name[1] == '.') && de->d_name[2] == 0)))
+		if (is_dot_or_dotdot(de->d_name))
 			continue;
 		strcpy(name, de->d_name);
 		if (lstat(pathbuf, &st))
diff --git a/remote.c b/remote.c
index 570e112..d7079c6 100644
--- a/remote.c
+++ b/remote.c
@@ -4,6 +4,7 @@
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
+#include "dir.h"
 
 static struct refspec s_tag_refspec = {
 	0,
@@ -634,10 +635,7 @@ static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
 
 static int valid_remote_nick(const char *name)
 {
-	if (!name[0] || /* not empty */
-	    (name[0] == '.' && /* not "." */
-	     (!name[1] || /* not ".." */
-	      (name[1] == '.' && !name[2]))))
+	if (!name[0] || is_dot_or_dotdot(name))
 		return 0;
 	return !strchr(name, '/'); /* no slash */
 }
diff --git a/transport.c b/transport.c
index 56831c5..9ad4a16 100644
--- a/transport.c
+++ b/transport.c
@@ -50,9 +50,7 @@ static int read_loose_refs(struct strbuf *path, int name_offset,
 	memset (&list, 0, sizeof(list));
 
 	while ((de = readdir(dir))) {
-		if (de->d_name[0] == '.' && (de->d_name[1] == '\0' ||
-				(de->d_name[1] == '.' &&
-				 de->d_name[2] == '\0')))
+		if (is_dot_or_dotdot(de->d_name))
 			continue;
 		ALLOC_GROW(list.entries, list.nr + 1, list.alloc);
 		list.entries[list.nr++] = xstrdup(de->d_name);
-- 
1.6.1.76.gc123b.dirty

^ permalink raw reply related

* Re: What's cooking in git.git (Jan 2009, #01; Mon, 05)
From: Miklos Vajna @ 2009-01-10 13:08 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jonas Fonseca, git
In-Reply-To: <7vr63by9cw.fsf@gitster.siamese.dyndns.org>

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

On Fri, Jan 09, 2009 at 10:15:59PM -0800, Junio C Hamano <gitster@pobox.com> wrote:
> >   > git diff | git apply -R -
> >   fatal: can't open patch '-': No such file or directory
> 
> Thanks.  I think this patch would fix it.

My bad, I did not test with explicit '-'.

Thanks for the fix!

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* trouble getting git cvsimport working
From: Caleb Cushing @ 2009-01-10 13:16 UTC (permalink / raw)
  To: git

not sure what I'm doing wrong.

actual cvs command

cvs -d :pserver:anonymous@anoncvs.gentoo.org:/var/cvsroot co gentoo-x86

what I'm trying

git cvsimport -a -v -d
:pserver:anonymous@anoncvs.gentoo.org/var/cvsroot/ gentoo-x86 -C
portage

Initialized empty Git repository in /home/portdev/cvs/portage/.git/
Running cvsps...
parse error on third token
WARNING: malformed CVS version: no data
WARNING: malformed CVS version str: (UNKNOWN CLIENT)
WARNING: Your CVS client version:
[(UNKNOWN CLIENT)]
and/or server version:
[(UNKNOWN SERVER)]
are too old to properly support the rlog command.
This command was introduced in 1.11.1.  Cvsps
will use log instead, but PatchSet numbering
may become unstable due to pruned empty
directories.

sh: cvs: command not found
DONE; creating master branch
fatal: refs/heads/origin: not a valid SHA1
fatal: master: not a valid SHA1
fatal: You are on a branch yet to be born
checkout failed: 32768

help would be appreciated.
-- 
Caleb Cushing

http://xenoterracide.blogspot.com

^ permalink raw reply

* Main branch being maintained with 'git am', how do mere mortals interact without too much conflicts?
From: Junichi Uekawa @ 2009-01-10 11:11 UTC (permalink / raw)
  To: git

Hi,

I've been maintaining my Git repository (monthlyreport.git) where most
people do not have push access, and I'm taking patches through e-mail
with 'git am'.

It often happens that I'm receiving patches which won't apply without
a merge ('git am -3') and happen to be conflict-resolving often,
because people work off a branch a few days before, and try to send
patches nearer the deadline (This is a monthly meeting resume, which
people are expected to submit their material, so this is kind of
normal).


One thing I'm worried is that users apparently have to throw away
their own change or do some conflict resolution.


User does 
  git pull xxxx
  edit ...
  git add 
  git commit 
  git format-patch -o ... HEAD^


I do bunch of 
    git am -3 (which usually has a conflict of some way or other)
    git add XXXX
    git am -3 --resolve
    git push


User then find that when doing

  git pull

again, a conflict will occur. 



I am thinking of recommending the users to create a branch

  git checkout -b my-work-for-2009-01 origin
  edit ...
  git add
  git commit
  git format-patch -o ... HEAD^
  send the email

and do

  git checkout master 
  git pull

and throw away their branches when they are included upstream.



Something tells me the problem is that I'm probably using a workflow
that resembles SVN too much, and users aren't used to branches yet.
Has anybody found this to be a problem, or better yet, is there a 
better workflow?



-- 
dancer@{netfort.gr.jp,debian.org}

^ permalink raw reply

* Re: [PATCH v2] make diff --color-words customizable
From: Jakub Narebski @ 2009-01-10 13:36 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Davide Libenzi, Thomas Rast
In-Reply-To: <alpine.DEB.1.00.0901101237050.30769@pacific.mpi-cbg.de>

On Sat, 10 Jan 2009, Johannes Schindelin wrote:
> On Sat, 10 Jan 2009, Jakub Narebski wrote:
> > Thomas Rast wrote:
> > 
> > > --color-words works (and always worked) by splitting words onto one
> > > line each, and using the normal line-diff machinery to get a word
> > > diff. 
> > 
> > Cannot we generalize diff machinery / use underlying LCS diff engine
> > instead of going through line diff?
> 
> What do you think we're doing?  libxdiff is pretty hardcoded to newlines.  
> That's why we're substituting non-word characters with newlines.

Isn't Meyers algorithm used by libxdiff based on LCS, largest common
subsequence, and doesn't it generate from the mathematical point of
view "diff" between two sequences (two arrays) which just happen to
be lines? It is a bit strange that libxdiff doesn't export its low
level algorithm...

-- 
Jakub Narebski
Poland

^ permalink raw reply

* [PATCH 2/6] Refactor git_set_argv0_path() to git_extract_argv0_path()
From: Steffen Prohaska @ 2009-01-10 13:50 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Johannes Schindelin, Johannes Sixt, Steve Haslam,
	Steffen Prohaska
In-Reply-To: <1231595452-27698-1-git-send-email-prohaska@zib.de>

From: Steve Haslam <shaslam@lastminute.com>

This commit moves the code that computes the dirname of argv[0]
from git.c's main() to git_set_argv0_path() and renames the function
to git_extract_argv0_path().  This makes the code in git.c's main
less cluttered, and we can use the dirname computation from other
main() functions too.

[ spr:
 - split Steve's original commit and wrote new commit message.
 - Integrated Johannes Schindelin's
   cca1704897e7fdb182f68d4c48a437c5d7bc5203 while rebasing onto master.
]

Signed-off-by: Steve Haslam <shaslam@lastminute.com>
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
 exec_cmd.c |   14 ++++++++++++--
 exec_cmd.h |    2 +-
 git.c      |   19 +++++--------------
 3 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/exec_cmd.c b/exec_cmd.c
index 669b82e..576c9ae 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -63,9 +63,19 @@ const char *system_path(const char *path)
 	return path;
 }
 
-void git_set_argv0_path(const char *path)
+const char *git_extract_argv0_path(const char *argv0)
 {
-	argv0_path = path;
+	const char *slash = argv0 + strlen(argv0);
+
+	while (argv0 <= slash && !is_dir_sep(*slash))
+		slash--;
+
+	if (slash >= argv0) {
+		argv0_path = xstrndup(argv0, slash - argv0);
+		return slash + 1;
+	}
+
+	return argv0;
 }
 
 void git_set_argv_exec_path(const char *exec_path)
diff --git a/exec_cmd.h b/exec_cmd.h
index 594f961..392e903 100644
--- a/exec_cmd.h
+++ b/exec_cmd.h
@@ -2,7 +2,7 @@
 #define GIT_EXEC_CMD_H
 
 extern void git_set_argv_exec_path(const char *exec_path);
-extern void git_set_argv0_path(const char *path);
+extern const char* git_extract_argv0_path(const char *path);
 extern const char* git_exec_path(void);
 extern void setup_path(void);
 extern const char **prepare_git_cmd(const char **argv);
diff --git a/git.c b/git.c
index a53e24f..b99b1b2 100644
--- a/git.c
+++ b/git.c
@@ -419,22 +419,13 @@ static void execv_dashed_external(const char **argv)
 
 int main(int argc, const char **argv)
 {
-	const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
-	char *slash = (char *)cmd + strlen(cmd);
+	const char *cmd;
 	int done_alias = 0;
 
-	/*
-	 * Take the basename of argv[0] as the command
-	 * name, and the dirname as the default exec_path
-	 * if we don't have anything better.
-	 */
-	while (cmd <= slash && !is_dir_sep(*slash))
-		slash--;
-	if (cmd <= slash) {
-		*slash++ = 0;
-		git_set_argv0_path(cmd);
-		cmd = slash;
-	}
+	if (argv[0] && *argv[0])
+		cmd = git_extract_argv0_path(argv[0]);
+	else
+		cmd = "git-help";
 
 	/*
 	 * "git-xxxx" is the same as "git xxxx", but we obviously:
-- 
1.6.1.85.g32c5d

^ permalink raw reply related

* [PATCH 1/6] Move computation of absolute paths from Makefile to runtime and compute prefix on the fly if RUNTIME_PREFIX set
From: Steffen Prohaska @ 2009-01-10 13:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin, Johannes Sixt, Steffen Prohaska

This is the first commit of a series that adds support for
relocatable binaries (called RUNTIME_PREFIX).  Such binaries can be
moved together with the system configuration files to a different
directory, as long as the relative paths from the binary to the
configuration files is preserved.  This functionality is essential
on Windows where we deliver git binaries with an installer that
allows to freely choose the installation location.  The commit
series implements RUNTIME_PREFIX only on Windows.  Adding support
on Unix should not be too hard, though.

This first commits makes all paths relative in the Makefile and
teaches system_path() to add the prefix instead.  We used to
compute absolute paths in the Makefile and passed them to C as
defines.  We now pass relative paths to C and call system_path() to
add the prefix at runtime.

If RUNTIME_PREFIX is unset we use the static prefix.  This will be
the default on Unix.  Thus, the behavior on Unix will stay
identical to the old implementation, which added the prefix in the
Makefile.

If RUNTIME_PREFIX is set the prefix is computed from the location
of the executable.  In this case, system_path() tries to strip
known directories that executables can be located in from the path
of the executable.  If the path is successfully stripped it is used
as the prefix.  For example, if the executable is
"/msysgit/bin/git" and BINDIR is "bin", then the prefix computed is
"/msysgit".

If the runtime prefix computation fails, we fall back to the static
prefix specified in the makefile.  This can be the case if the
executable is not installed at a known location.  Note that our
test system sets GIT_CONFIG_NOSYSTEM to tell git to ignore global
configuration files during testing.  Hence testing does not trigger
the fall back.

Note that the implementation requires argv0_path to be set to an
absolute path, which is currently the case only on Windows.
argv0_path must point to the directory of the executable.  We use
assert() to verify this in debug builds.  On Windows, the wrapper
for main() (see compat/mingw.h) guarantees that argv0_path is
correctly initialized.  On Unix, further work is required before
RUNTIME_PREFIX can be enabled.

Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
 Makefile       |   45 +++++++++++++++++++++++++++++----------------
 builtin-help.c |    4 ++--
 exec_cmd.c     |   54 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 81 insertions(+), 22 deletions(-)

diff --git a/Makefile b/Makefile
index dee97c1..a7728a3 100644
--- a/Makefile
+++ b/Makefile
@@ -179,28 +179,32 @@ STRIP ?= strip
 # Among the variables below, these:
 #   gitexecdir
 #   template_dir
+#   mandir
+#   infodir
 #   htmldir
 #   ETC_GITCONFIG (but not sysconfdir)
-# can be specified as a relative path ../some/where/else (which must begin
-# with ../); this is interpreted as relative to $(bindir) and "git" at
+# can be specified as a relative path some/where/else;
+# this is interpreted as relative to $(prefix) and "git" at
 # runtime figures out where they are based on the path to the executable.
 # This can help installing the suite in a relocatable way.
 
 prefix = $(HOME)
-bindir = $(prefix)/bin
-mandir = $(prefix)/share/man
-infodir = $(prefix)/share/info
-gitexecdir = $(prefix)/libexec/git-core
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+mandir = share/man
+infodir = share/info
+gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
-template_dir = $(sharedir)/git-core/templates
-htmldir=$(sharedir)/doc/git-doc
+template_dir = share/git-core/templates
+htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
 sysconfdir = /etc
+ETC_GITCONFIG = $(sysconfdir)/gitconfig
 else
 sysconfdir = $(prefix)/etc
+ETC_GITCONFIG = etc/gitconfig
 endif
 lib = lib
-ETC_GITCONFIG = $(sysconfdir)/gitconfig
 # DESTDIR=
 
 # default configuration for gitweb
@@ -1027,6 +1031,9 @@ ifdef INTERNAL_QSORT
 	COMPAT_CFLAGS += -DINTERNAL_QSORT
 	COMPAT_OBJS += compat/qsort.o
 endif
+ifdef RUNTIME_PREFIX
+	COMPAT_CFLAGS += -DRUNTIME_PREFIX
+endif
 
 ifdef NO_PTHREADS
 	THREADED_DELTA_SEARCH =
@@ -1086,6 +1093,7 @@ ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
 
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
+bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
 mandir_SQ = $(subst ','\'',$(mandir))
 infodir_SQ = $(subst ','\'',$(infodir))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
@@ -1251,7 +1259,12 @@ git.o git.spec \
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
 
 exec_cmd.o: exec_cmd.c GIT-CFLAGS
-	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $<
+	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
+		'-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
+		'-DBINDIR="$(bindir_relative_SQ)"' \
+		'-DPREFIX="$(prefix_SQ)"' \
+		$<
+
 builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
 
@@ -1400,17 +1413,17 @@ remove-dashes:
 
 ### Installation rules
 
-ifeq ($(firstword $(subst /, ,$(template_dir))),..)
-template_instdir = $(bindir)/$(template_dir)
-else
+ifeq ($(abspath $(template_dir)),$(template_dir))
 template_instdir = $(template_dir)
+else
+template_instdir = $(prefix)/$(template_dir)
 endif
 export template_instdir
 
-ifeq ($(firstword $(subst /, ,$(gitexecdir))),..)
-gitexec_instdir = $(bindir)/$(gitexecdir)
-else
+ifeq ($(abspath $(gitexecdir)),$(gitexecdir))
 gitexec_instdir = $(gitexecdir)
+else
+gitexec_instdir = $(prefix)/$(gitexecdir)
 endif
 gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
 export gitexec_instdir
diff --git a/builtin-help.c b/builtin-help.c
index f076efa..9b57a74 100644
--- a/builtin-help.c
+++ b/builtin-help.c
@@ -329,7 +329,7 @@ static void setup_man_path(void)
 	 * old_path, the ':' at the end will let 'man' to try
 	 * system-wide paths after ours to find the manual page. If
 	 * there is old_path, we need ':' as delimiter. */
-	strbuf_addstr(&new_path, GIT_MAN_PATH);
+	strbuf_addstr(&new_path, system_path(GIT_MAN_PATH));
 	strbuf_addch(&new_path, ':');
 	if (old_path)
 		strbuf_addstr(&new_path, old_path);
@@ -375,7 +375,7 @@ static void show_man_page(const char *git_cmd)
 static void show_info_page(const char *git_cmd)
 {
 	const char *page = cmd_to_page(git_cmd);
-	setenv("INFOPATH", GIT_INFO_PATH, 1);
+	setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
 	execlp("info", "info", "gitman", page, NULL);
 }
 
diff --git a/exec_cmd.c b/exec_cmd.c
index cdd35f9..669b82e 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -9,11 +9,57 @@ static const char *argv0_path;
 
 const char *system_path(const char *path)
 {
-	if (!is_absolute_path(path) && argv0_path) {
-		struct strbuf d = STRBUF_INIT;
-		strbuf_addf(&d, "%s/%s", argv0_path, path);
-		path = strbuf_detach(&d, NULL);
+	static const char *prefix;
+
+	if (is_absolute_path(path)) {
+		return path;
+	}
+
+#ifdef RUNTIME_PREFIX
+	assert(argv0_path);
+	assert(is_absolute_path(argv0_path));
+
+	if (!prefix) {
+		const char *strip[] = {
+			GIT_EXEC_PATH,
+			BINDIR,
+			0
+		};
+		const char **s;
+
+		for (s = strip; *s; s++) {
+			const char *sargv = argv0_path + strlen(argv0_path);
+			const char *ss = *s + strlen(*s);
+			while (argv0_path < sargv && *s < ss
+				&& (*sargv == *ss ||
+				    (is_dir_sep(*sargv) && is_dir_sep(*ss)))) {
+				sargv--;
+				ss--;
+			}
+			if (*s == ss) {
+				struct strbuf d = STRBUF_INIT;
+				/* We also skip the trailing directory separator. */
+				assert(sargv - argv0_path - 1 >= 0);
+				strbuf_add(&d, argv0_path, sargv - argv0_path - 1);
+				prefix = strbuf_detach(&d, NULL);
+				break;
+			}
+		}
 	}
+
+	if (!prefix) {
+		prefix = PREFIX;
+		fprintf(stderr, "RUNTIME_PREFIX requested, "
+				"but prefix computation failed.  "
+				"Using static fallback '%s'.\n", prefix);
+	}
+#else
+	prefix = PREFIX;
+#endif
+
+	struct strbuf d = STRBUF_INIT;
+	strbuf_addf(&d, "%s/%s", prefix, path);
+	path = strbuf_detach(&d, NULL);
 	return path;
 }
 
-- 
1.6.1.85.g32c5d

^ permalink raw reply related

* [PATCH 6/6] Windows: Revert to default paths and convert them by RUNTIME_PREFIX
From: Steffen Prohaska @ 2009-01-10 13:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin, Johannes Sixt, Steffen Prohaska
In-Reply-To: <1231595452-27698-5-git-send-email-prohaska@zib.de>

The RUNTIME_PREFIX mechanism allows us to use the default paths on
Windows too.  Defining RUNTIME_PREFIX explicitly requests for
translation of paths relative to the executable at runtime.

Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
 Makefile |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index a7728a3..a5f6cad 100644
--- a/Makefile
+++ b/Makefile
@@ -789,6 +789,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
 	SNPRINTF_RETURNS_BOGUS = YesPlease
 	NO_SVN_TESTS = YesPlease
 	NO_PERL_MAKEMAKER = YesPlease
+	RUNTIME_PREFIX = YesPlease
 	NO_POSIX_ONLY_PROGRAMS = YesPlease
 	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 	COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
@@ -797,9 +798,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
 	COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o
 	EXTLIBS += -lws2_32
 	X = .exe
-	gitexecdir = ../libexec/git-core
-	template_dir = ../share/git-core/templates/
-	ETC_GITCONFIG = ../etc/gitconfig
 endif
 ifneq (,$(findstring arm,$(uname_M)))
 	ARM_SHA1 = YesPlease
-- 
1.6.1.85.g32c5d

^ permalink raw reply related

* [PATCH 5/6] Modify setup_path() to only add git_exec_path() to PATH
From: Steffen Prohaska @ 2009-01-10 13:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin, Johannes Sixt, Steffen Prohaska
In-Reply-To: <1231595452-27698-4-git-send-email-prohaska@zib.de>

Searching git programs only in the highest priority location is
sufficient.  It does not make sense that some of the required
programs are located at the highest priority location but other
programs are picked up from a lower priority exec-path.  If
exec-path is overridden a complete set of commands should be
provided, otherwise several different versions could get mixed,
which is likely to cause confusion.

If a user explicitly overrides the default location (by --exec-path
or GIT_EXEC_PATH), we now expect that all the required programs are
found there.  Instead of adding the directories "argv_exec_path",
"getenv(EXEC_PATH_ENVIRONMENT)", and "system_path(GIT_EXEC_PATH)"
to PATH, we now rely on git_exec_path(), which implements the same
order, but only returns the highest priority location to search for
executables.

Accessing only the location with highest priority is also required
for testing executables built with RUNTIME_PREFIX.  The call to
system_path() should be avoided if RUNTIME_PREFIX is set and the
executable is not installed at its final destination.  Because we
test before installing, we want to avoid calling system_path()
during tests.  The modifications in this commit avoid calling
system_path(GIT_EXEC_PATH) if a higher-priority location is
provided, which is the case when running the tests.

Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
 exec_cmd.c |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/exec_cmd.c b/exec_cmd.c
index 576c9ae..ec38b0a 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -117,9 +117,7 @@ void setup_path(void)
 	const char *old_path = getenv("PATH");
 	struct strbuf new_path = STRBUF_INIT;
 
-	add_path(&new_path, argv_exec_path);
-	add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT));
-	add_path(&new_path, system_path(GIT_EXEC_PATH));
+	add_path(&new_path, git_exec_path());
 	add_path(&new_path, argv0_path);
 
 	if (old_path)
-- 
1.6.1.85.g32c5d

^ permalink raw reply related

* [PATCH 3/6] Glean libexec path from argv[0] for git-upload-pack and git-receive-pack.
From: Steffen Prohaska @ 2009-01-10 13:50 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Johannes Schindelin, Johannes Sixt, Steve Haslam,
	Steffen Prohaska
In-Reply-To: <1231595452-27698-2-git-send-email-prohaska@zib.de>

From: Steve Haslam <shaslam@lastminute.com>

If the user specified the full path to git-upload-pack as the -u option to
"git clone" when cloning a remote repository, and git was not on the default
PATH on the remote machine, git-upload-pack was failing to exec
git-pack-objects.

By making the argv[0] path (if any) available to setup_path(), this will
allow finding the "git" executable in the same directory as
"git-upload-pack". The default built in to exec_cmd.c is to look for "git"
in the ".../libexec/git-core" directory, but it is not installed there (any
longer).

Much the same applies to invoking git-receive-pack from a non-PATH location
using the "--exec" argument to "git push".

[ spr: split Steve's original commit into two commits. ]

Signed-off-by: Steve Haslam <shaslam@lastminute.com>
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
 builtin-receive-pack.c |    3 +++
 upload-pack.c          |    3 +++
 2 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index db67c31..bbedcfe 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -579,6 +579,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 	int i;
 	char *dir = NULL;
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	argv++;
 	for (i = 1; i < argc; i++) {
 		const char *arg = *argv++;
diff --git a/upload-pack.c b/upload-pack.c
index e5adbc0..c469a60 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -616,6 +616,9 @@ int main(int argc, char **argv)
 	int i;
 	int strict = 0;
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
-- 
1.6.1.85.g32c5d

^ permalink raw reply related

* [PATCH 4/6] Add calls to git_extract_argv0_path() in programs that call git_config_*
From: Steffen Prohaska @ 2009-01-10 13:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin, Johannes Sixt, Steffen Prohaska
In-Reply-To: <1231595452-27698-3-git-send-email-prohaska@zib.de>

Programs that use git_config need to find the global configuration.
When runtime prefix computation is enabled, this requires that
git_extract_argv0_path() is called early in the program's main().

This commit adds the necessary calls.

Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
 daemon.c             |    3 +++
 fast-import.c        |    4 ++++
 hash-object.c        |    4 ++++
 http-push.c          |    3 +++
 imap-send.c          |    4 ++++
 index-pack.c         |    4 ++++
 merge-index.c        |    4 ++++
 merge-tree.c         |    4 ++++
 mktag.c              |    4 ++++
 mktree.c             |    4 ++++
 pack-redundant.c     |    4 ++++
 patch-id.c           |    4 ++++
 unpack-file.c        |    4 ++++
 update-server-info.c |    4 ++++
 var.c                |    4 ++++
 15 files changed, 58 insertions(+), 0 deletions(-)

diff --git a/daemon.c b/daemon.c
index 540700e..1c5e604 100644
--- a/daemon.c
+++ b/daemon.c
@@ -937,6 +937,9 @@ int main(int argc, char **argv)
 	gid_t gid = 0;
 	int i;
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
diff --git a/fast-import.c b/fast-import.c
index a6bce66..7762205 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -150,6 +150,7 @@ Format of STDIN stream:
 #include "refs.h"
 #include "csum-file.h"
 #include "quote.h"
+#include "exec_cmd.h"
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -2405,6 +2406,9 @@ int main(int argc, const char **argv)
 {
 	unsigned int i, show_stats = 1;
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 	git_config(git_pack_config, NULL);
 	if (!pack_compression_seen && core_compression_seen)
diff --git a/hash-object.c b/hash-object.c
index 846e91a..a689cc6 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -8,6 +8,7 @@
 #include "blob.h"
 #include "quote.h"
 #include "parse-options.h"
+#include "exec_cmd.h"
 
 static void hash_fd(int fd, const char *type, int write_object, const char *path)
 {
@@ -81,6 +82,9 @@ int main(int argc, const char **argv)
 
 	type = blob_type;
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	git_config(git_default_config, NULL);
 
 	argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0);
diff --git a/http-push.c b/http-push.c
index a4b7d08..dd2b2b1 100644
--- a/http-push.c
+++ b/http-push.c
@@ -2179,6 +2179,9 @@ int main(int argc, char **argv)
 	struct ref *ref;
 	char *rewritten_url = NULL;
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 
 	remote = xcalloc(sizeof(*remote), 1);
diff --git a/imap-send.c b/imap-send.c
index c3fa0df..6e92aee 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -23,6 +23,7 @@
  */
 
 #include "cache.h"
+#include "exec_cmd.h"
 #ifdef NO_OPENSSL
 typedef void *SSL;
 #endif
@@ -1389,6 +1390,9 @@ int main(int argc, char **argv)
 	int total, n = 0;
 	int nongit_ok;
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	/* init the random number generator */
 	arc4_init();
 
diff --git a/index-pack.c b/index-pack.c
index 2931511..0df297b 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -8,6 +8,7 @@
 #include "tree.h"
 #include "progress.h"
 #include "fsck.h"
+#include "exec_cmd.h"
 
 static const char index_pack_usage[] =
 "git index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
@@ -880,6 +881,9 @@ int main(int argc, char **argv)
 	struct pack_idx_entry **idx_objects;
 	unsigned char pack_sha1[20];
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	/*
 	 * We wish to read the repository's config file if any, and
 	 * for that it is necessary to call setup_git_directory_gently().
diff --git a/merge-index.c b/merge-index.c
index 7827e87..5d89ead 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "run-command.h"
+#include "exec_cmd.h"
 
 static const char *pgm;
 static const char *arguments[9];
@@ -93,6 +94,9 @@ int main(int argc, char **argv)
 	if (argc < 3)
 		usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 	read_cache();
 
diff --git a/merge-tree.c b/merge-tree.c
index 2d1413e..165ccfc 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -2,6 +2,7 @@
 #include "tree-walk.h"
 #include "xdiff-interface.h"
 #include "blob.h"
+#include "exec_cmd.h"
 
 static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
 static int resolve_directories = 1;
@@ -344,6 +345,9 @@ int main(int argc, char **argv)
 	if (argc != 4)
 		usage(merge_tree_usage);
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 
 	buf1 = get_tree_descriptor(t+0, argv[1]);
diff --git a/mktag.c b/mktag.c
index ba3d495..ff32f8a 100644
--- a/mktag.c
+++ b/mktag.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "tag.h"
+#include "exec_cmd.h"
 
 /*
  * A signature file has a very simple fixed format: four lines
@@ -159,6 +160,9 @@ int main(int argc, char **argv)
 	if (argc != 1)
 		usage("git-mktag < signaturefile");
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 
 	if (strbuf_read(&buf, 0, 4096) < 0) {
diff --git a/mktree.c b/mktree.c
index 514fd9b..8ac0d57 100644
--- a/mktree.c
+++ b/mktree.c
@@ -6,6 +6,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "tree.h"
+#include "exec_cmd.h"
 
 static struct treeent {
 	unsigned mode;
@@ -70,6 +71,9 @@ int main(int ac, char **av)
 	unsigned char sha1[20];
 	int line_termination = '\n';
 
+	if (av[0] && *av[0])
+		git_extract_argv0_path(av[0]);
+
 	setup_git_directory();
 
 	while ((1 < ac) && av[1][0] == '-') {
diff --git a/pack-redundant.c b/pack-redundant.c
index e93eb96..8ff119b 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -7,6 +7,7 @@
 */
 
 #include "cache.h"
+#include "exec_cmd.h"
 
 #define BLKSIZE 512
 
@@ -601,6 +602,9 @@ int main(int argc, char **argv)
 	unsigned char *sha1;
 	char buf[42]; /* 40 byte sha1 + \n + \0 */
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	setup_git_directory();
 
 	for (i = 1; i < argc; i++) {
diff --git a/patch-id.c b/patch-id.c
index 871f1d2..ff460e2 100644
--- a/patch-id.c
+++ b/patch-id.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "exec_cmd.h"
 
 static void flush_current_id(int patchlen, unsigned char *id, git_SHA_CTX *c)
 {
@@ -79,6 +80,9 @@ int main(int argc, char **argv)
 	if (argc != 1)
 		usage(patch_id_usage);
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	generate_id_list();
 	return 0;
 }
diff --git a/unpack-file.c b/unpack-file.c
index bcdc8bb..f8bfda7 100644
--- a/unpack-file.c
+++ b/unpack-file.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "blob.h"
+#include "exec_cmd.h"
 
 static char *create_temp_file(unsigned char *sha1)
 {
@@ -25,6 +26,9 @@ int main(int argc, char **argv)
 {
 	unsigned char sha1[20];
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	if (argc != 2)
 		usage("git-unpack-file <sha1>");
 	if (get_sha1(argv[1], sha1))
diff --git a/update-server-info.c b/update-server-info.c
index 7e8209e..286a4dd 100644
--- a/update-server-info.c
+++ b/update-server-info.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "exec_cmd.h"
 
 static const char update_server_info_usage[] =
 "git update-server-info [--force]";
@@ -19,6 +20,9 @@ int main(int ac, char **av)
 	if (i != ac)
 		usage(update_server_info_usage);
 
+	if (av[0] && *av[0])
+		git_extract_argv0_path(av[0]);
+
 	setup_git_directory();
 
 	return !!update_server_info(force);
diff --git a/var.c b/var.c
index f1eb314..33457dc 100644
--- a/var.c
+++ b/var.c
@@ -4,6 +4,7 @@
  * Copyright (C) Eric Biederman, 2005
  */
 #include "cache.h"
+#include "exec_cmd.h"
 
 static const char var_usage[] = "git var [-l | <variable>]";
 
@@ -56,6 +57,9 @@ int main(int argc, char **argv)
 		usage(var_usage);
 	}
 
+	if (argv[0] && *argv[0])
+		git_extract_argv0_path(argv[0]);
+
 	setup_git_directory_gently(&nongit);
 	val = NULL;
 
-- 
1.6.1.85.g32c5d

^ permalink raw reply related

* Re: [PATCH v2] make diff --color-words customizable
From: Johannes Schindelin @ 2009-01-10 14:08 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Davide Libenzi, Thomas Rast
In-Reply-To: <200901101436.48149.jnareb@gmail.com>

Hi,

On Sat, 10 Jan 2009, Jakub Narebski wrote:

> On Sat, 10 Jan 2009, Johannes Schindelin wrote:
> > On Sat, 10 Jan 2009, Jakub Narebski wrote:
> > > Thomas Rast wrote:
> > > 
> > > > --color-words works (and always worked) by splitting words onto one
> > > > line each, and using the normal line-diff machinery to get a word
> > > > diff. 
> > > 
> > > Cannot we generalize diff machinery / use underlying LCS diff engine
> > > instead of going through line diff?
> > 
> > What do you think we're doing?  libxdiff is pretty hardcoded to newlines.  
> > That's why we're substituting non-word characters with newlines.
> 
> Isn't Meyers algorithm used by libxdiff based on LCS, largest common
> subsequence, and doesn't it generate from the mathematical point of
> view "diff" between two sequences (two arrays) which just happen to
> be lines? It is a bit strange that libxdiff doesn't export its low
> level algorithm...

Umm.

It _is_ Myers' algorithm.  It just so happens that libxdiff hardcodes 
newline to be the separator.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] gitweb: suggest name for OPML view
From: Jakub Narebski @ 2009-01-10 14:10 UTC (permalink / raw)
  To: Giuseppe Bilotta; +Cc: git, Petr Baudis, Junio C Hamano
In-Reply-To: <1230900570-25324-1-git-send-email-giuseppe.bilotta@gmail.com>

On Fri, 2 Jan 2009, Giuseppe Bilotta wrote:

> Suggest opml.xml as name for OPML view by providing the appropriate
> header, consistently with similar usage in project_index view.

It is not name for a view, but more of default filename when saving
it. While it is good idea to have consistency, I guess that while
'project_index' view and other non-HTML views are meant to be 
downloaded and saved (snapshots, patches, patchsets), OPML view
is meant to be used on-line, just like web feeds in RSS and Atom
formats which are non-HTML too but do not have Content-Disposition
header set. 

But I do not use OPML. Anyone?

> 
> Signed-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
> ---
>  gitweb/gitweb.perl |    6 +++++-
>  1 files changed, 5 insertions(+), 1 deletions(-)
> 
> diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
> index b164001..995bc1a 100755
> --- a/gitweb/gitweb.perl
> +++ b/gitweb/gitweb.perl
> @@ -6122,7 +6122,11 @@ sub git_atom {
>  sub git_opml {
>  	my @list = git_get_projects_list();
>  
> -	print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
> +	print $cgi->header(
> +		-type => 'text/xml',
> +		-charset => 'utf-8',
> +		-content_disposition => 'inline; filename="opml.xml"');
> +
>  	print <<XML;
>  <?xml version="1.0" encoding="utf-8"?>
>  <opml version="1.0">
> -- 
> 1.5.6.5
> 
> 

-- 
Jakub Narebski
Poland

^ permalink raw reply

* Re: [PATCH v4] submodule: allow tracking of the newest revision of a branch in a submodule
From: Johannes Schindelin @ 2009-01-10 14:23 UTC (permalink / raw)
  To: Fabian Franz; +Cc: git, j.sixt, hjemli, gitster
In-Reply-To: <1231553410-7541-1-git-send-email-git@fabian-franz.de>

Hi,

On Sat, 10 Jan 2009, Fabian Franz wrote:

> Submodules currently only allow tracking a specific revision and each 
> update in a submodule leads to a new commit in the master repository. 
> However some users may want to always track the newest revision of a 
> specific (named) tag or branch or HEAD. For example the user might want 
> to track a staging branch in all submodules.

I wonder how you want to deal with "git bisect".  Or for that matter, with 
"git checkout HEAD^500".

Ciao,
Dscho

^ permalink raw reply

* Re: trouble getting git cvsimport working
From: Johannes Schindelin @ 2009-01-10 14:25 UTC (permalink / raw)
  To: Caleb Cushing; +Cc: git
In-Reply-To: <81bfc67a0901100516w10ea77e8n8734713b071d69b9@mail.gmail.com>

Hi,

On Sat, 10 Jan 2009, Caleb Cushing wrote:

> sh: cvs: command not found

What's this?

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH 3/6] Glean libexec path from argv[0] for git-upload-pack and git-receive-pack.
From: Johannes Schindelin @ 2009-01-10 14:34 UTC (permalink / raw)
  To: Steffen Prohaska; +Cc: Junio C Hamano, git, Johannes Sixt, Steve Haslam
In-Reply-To: <1231595452-27698-3-git-send-email-prohaska@zib.de>

Hi,

On Sat, 10 Jan 2009, Steffen Prohaska wrote:

> From: Steve Haslam <shaslam@lastminute.com>
> 
> If the user specified the full path to git-upload-pack as the -u option to
> "git clone" when cloning a remote repository, and git was not on the default
> PATH on the remote machine, git-upload-pack was failing to exec
> git-pack-objects.
> 
> By making the argv[0] path (if any) available to setup_path(), this will
> allow finding the "git" executable in the same directory as
> "git-upload-pack". The default built in to exec_cmd.c is to look for "git"
> in the ".../libexec/git-core" directory, but it is not installed there (any
> longer).
> 
> Much the same applies to invoking git-receive-pack from a non-PATH location
> using the "--exec" argument to "git push".
> 
> [ spr: split Steve's original commit into two commits. ]

I think you do not want to have that [ ... ] in the commit message, but 
after the "--".

Further, it would make sense to have these 2 patches independently, 
provided that a test is added with which we could verify that the patches 
are actually necessary for upload-pack/receive-pack.

Logically, and to avoid committing a broken revision, 1/6 should come 
last, methinks.

Ciao,
Dscho

^ permalink raw reply

* Re: trouble getting git cvsimport working
From: Caleb Cushing @ 2009-01-10 14:35 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <alpine.DEB.1.00.0901101524310.30769@pacific.mpi-cbg.de>

>  > sh: cvs: command not found
>
>
> What's this?

does git need more than cvsps to work (I assumed my package manager
had installed all needed deps)? I installed cvs after you pointed that
out...

git cvsimport -a -v -d
:pserver:anonymous@anoncvs.gentoo.org/var/cvsroot/ gentoo-x86 -C
portage
Initialized empty Git repository in /home/portdev/cvs/portage/.git/
Running cvsps...
parse error on third token
WARNING: malformed CVS version str: Server: (unknown)
WARNING: Your CVS client version:
[Client: Concurrent Versions System (CVS) 1.12.12 (client)]
and/or server version:
[Server: (unknown)]
are too old to properly support the rlog command.
This command was introduced in 1.11.1.  Cvsps
will use log instead, but PatchSet numbering
may become unstable due to pruned empty
directories.

cvs log: warning: failed to open /home/portdev/.cvspass for reading:
No such file or directory
cvs log: in directory .:
cvs [log aborted]: there is no version here; run 'cvs checkout' first
DONE; creating master branch
fatal: refs/heads/origin: not a valid SHA1
fatal: master: not a valid SHA1
fatal: You are on a branch yet to be born
checkout failed: 32768
-- 
Caleb Cushing

http://xenoterracide.blogspot.com

^ permalink raw reply

* Re: Main branch being maintained with 'git am', how do mere mortals interact without too much conflicts?
From: Peter Harris @ 2009-01-10 14:47 UTC (permalink / raw)
  To: Junichi Uekawa; +Cc: git
In-Reply-To: <87vdsntnyd.dancerj%dancer@netfort.gr.jp>

On Sat, Jan 10, 2009 at 6:11 AM, Junichi Uekawa wrote:
> I am thinking of recommending the users to create a branch
...
> and throw away their branches when they are included upstream.

Yes, with a patch based workflow, this is almost required; all of the
commits will at least have different committer information.

There's nothing wrong with this approach.

> Something tells me the problem is that I'm probably using a workflow
> that resembles SVN too much, and users aren't used to branches yet.
> Has anybody found this to be a problem, or better yet, is there a
> better workflow?

If you need the commits to be identical, and you don't mind your email
consisting of a binary blob attachment, you can ask your contributors
to send you a bundle instead of a series of patches. "git help bundle"
for details.

Peter Harris

^ permalink raw reply

* Re: trouble getting git cvsimport working
From: Johannes Schindelin @ 2009-01-10 14:55 UTC (permalink / raw)
  To: Caleb Cushing; +Cc: git
In-Reply-To: <81bfc67a0901100635i4d33c294x393a8d2dfc2b0e0d@mail.gmail.com>

Hi,

On Sat, 10 Jan 2009, Caleb Cushing wrote:

> git cvsimport -a -v -d
> :pserver:anonymous@anoncvs.gentoo.org/var/cvsroot/ gentoo-x86 -C
> portage
> Initialized empty Git repository in /home/portdev/cvs/portage/.git/
> Running cvsps...
> parse error on third token
> WARNING: malformed CVS version str: Server: (unknown)
> WARNING: Your CVS client version:
> [Client: Concurrent Versions System (CVS) 1.12.12 (client)]
> and/or server version:
> [Server: (unknown)]
> are too old to properly support the rlog command.
> This command was introduced in 1.11.1.  Cvsps
> will use log instead, but PatchSet numbering
> may become unstable due to pruned empty
> directories.

This is the problem.

> cvs log: warning: failed to open /home/portdev/.cvspass for reading:
> No such file or directory
> cvs log: in directory .:
> cvs [log aborted]: there is no version here; run 'cvs checkout' first

And this is the solution: Just try

	$ cvs -d :pserver:anonymous@anoncvs.gentoo.org/var/cvsroot/ \
		co gentoo-x86
	$ cd gentoo-x86
	$ git cvsimport -v

(I assume it is a live repository, so -a is dangerous.)

Hth,
Dscho

^ permalink raw reply

* Re: Main branch being maintained with 'git am', how do mere mortals interact without too much conflicts?
From: Santi Béjar @ 2009-01-10 14:47 UTC (permalink / raw)
  To: Junichi Uekawa; +Cc: git
In-Reply-To: <87vdsntnyd.dancerj%dancer@netfort.gr.jp>

2009/1/10 Junichi Uekawa <dancer@netfort.gr.jp>:
> Hi,
>
> I've been maintaining my Git repository (monthlyreport.git) where most
> people do not have push access, and I'm taking patches through e-mail
> with 'git am'.
>
> It often happens that I'm receiving patches which won't apply without
> a merge ('git am -3') and happen to be conflict-resolving often,
> because people work off a branch a few days before, and try to send
> patches nearer the deadline (This is a monthly meeting resume, which
> people are expected to submit their material, so this is kind of
> normal).
>
>
> One thing I'm worried is that users apparently have to throw away
> their own change or do some conflict resolution.
>
>
> User does
>  git pull xxxx
>  edit ...
>  git add
>  git commit
>  git format-patch -o ... HEAD^
>
>
> I do bunch of
>    git am -3 (which usually has a conflict of some way or other)
>    git add XXXX
>    git am -3 --resolve
>    git push
>
>
> User then find that when doing
>
>  git pull
>
> again, a conflict will occur.
>
>
>
> I am thinking of recommending the users to create a branch
>
>  git checkout -b my-work-for-2009-01 origin
>  edit ...
>  git add
>  git commit
>  git format-patch -o ... HEAD^
>  send the email
>
> and do
>
>  git checkout master
>  git pull
>
> and throw away their branches when they are included upstream.
>
>
>
> Something tells me the problem is that I'm probably using a workflow
> that resembles SVN too much, and users aren't used to branches yet.
> Has anybody found this to be a problem, or better yet, is there a
> better workflow?

I think your workflow is fine. When someone sends a patch s/he has to
know that their branch is a throw away branch to create the patch.
Another thing is if you send it as a public repository or as a bundle,
and even then the maintainer can decide to apply it merging or
cherry-picking.

There is a doc explaining " An overview of recommended workflows with
git" in git:

http://www.kernel.org/pub/software/scm/git/docs/gitworkflows.html

HTH,
Santi

^ permalink raw reply

* Re: What's cooking in git.git (Jan 2009, #01; Mon, 05)
From: Jonas Fonseca @ 2009-01-10 15:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Miklos Vajna, git
In-Reply-To: <7vr63by9cw.fsf@gitster.siamese.dyndns.org>

On Sat, Jan 10, 2009 at 01:15, Junio C Hamano <gitster@pobox.com> wrote:
> "Jonas Fonseca" <jonas.fonseca@gmail.com> writes:
>
>> On Tue, Jan 6, 2009 at 01:33, Junio C Hamano <gitster@pobox.com> wrote:
>>> ----------------------------------------------------------------
>>> * mv/apply-parse-opt (Sun Dec 28 00:03:57 2008 +0100) 1 commit
>>>  + parse-opt: migrate builtin-apply.
>>
>> This broke apply for me after updating to the current "next" earlier
>> today.
>
> Thanks.  I think this patch would fix it.

Thank you for the quick fix.

-- 
Jonas Fonseca

^ permalink raw reply

* [PATCH/RFC] shortlog: add option to group together different names/emails of an author
From: Adeodato Simó @ 2009-01-10 15:16 UTC (permalink / raw)
  To: git; +Cc: Adeodato Simó

It's common for repositories to contain commits with different spellings of
an author name, or different email addresses. The shortlog command tries to
alleviate this by using .mailmap files. However, maintaining a .mailmap file
up to date is a manual process, and it does not help when shortlog is
invoked with the -e option and different email addresses for an author are
involved.

This commit introduces a -j/--join-uids option that uses a very dumb logic
to detect different spellings and addresses of a same author. In particular,
it just joins commits when either the name or the address had been
previously seen, attaching the commit to that previous id. In other words,
these three ids will be joined:

    Author: Joe Developer <joe@example.com>
    Author: Joe R. Developer <joe_r@example.com>
    Author: Joe R. Developer <joe@example.com>

but only because of the third spelling. The first two alone would be left
separate. When the names and addresses are printed, the most common spelling
and address are used.

Incidentally, there is f817546 in git.git which has this author information:

    Author: Wincent Colaiuta <gitster@pobox.com>

Which makes all of Wincent's commits to be assigned to Junio with -j. This
is easily fixed with an entry for gitster@pobox.com in .mailmap, which this
commit includes. (And then, only f817546 is be assigned to Junio.)

Signed-off-by: Adeodato Simó <dato@net.com.org.es>
---
This is my scratching of my own itch: I was used to `bzr author-stats`,
which is equivalent to `git shortlog -jsne`. I realize -sn comes close,
but I like having the email address listed. Please let me know what you
think.

Tests and a mention in git-shortlog.txt are missing. That'll come next
when/if I'm told this has a chance of inclusion. :-)

The code is valgrind'ed. I'm not completely confident, though, bugs will
not be hiding in corner cases. Also, I don't see any appreciable
slowdown with this version in git.git, particularly not between the
current git-shortlog and this new when run without -j (not when run with
-j either, but that's less critical).

This patch applies on top of my as/maint-shortlog-cleanup branch.

 .mailmap           |    1 +
 builtin-shortlog.c |  280 +++++++++++++++++++++++++++++++++++++++++++++-------
 shortlog.h         |   14 ++-
 3 files changed, 256 insertions(+), 39 deletions(-)

diff --git a/.mailmap b/.mailmap
index 373476b..f86d8a7 100644
--- a/.mailmap
+++ b/.mailmap
@@ -27,6 +27,7 @@ Joachim Berdal Haga <cjhaga@fys.uio.no>
 Jon Loeliger <jdl@freescale.com>
 Jon Seymour <jon@blackcubes.dyndns.org>
 Jonathan Nieder <jrnieder@uchicago.edu>
+Junio C Hamano <gitster@pobox.com>
 Junio C Hamano <junio@twinsun.com>
 Karl Hasselström <kha@treskal.com>
 Kent Engstrom <kent@lysator.liu.se>
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 90e76ae..af155b9 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -9,24 +9,79 @@
 #include "shortlog.h"
 #include "parse-options.h"
 
+struct idinfo {
+	int count;
+	size_t idx;
+};
+
 static char const * const shortlog_usage[] = {
 	"git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]",
 	"",
 	"[rev-opts] are documented in git-rev-list(1)",
 	NULL
 };
-
-static int compare_by_number(const void *a1, const void *a2)
-{
-	const struct string_list_item *i1 = a1, *i2 = a2;
-	const struct string_list *l1 = i1->util, *l2 = i2->util;
+
+static int compare_by_count(const void *a1, const void *a2)
+{
+	const struct idinfo *i1 = a1, *i2 = a2;
+
+	if (i1->count < i2->count)
+		return 1;
+	else if (i1->count == i2->count)
+		return 0;
+	else
+		return -1;
+}
+
+static int compare_by_idx_and_count(const void *a1, const void *a2)
+{
+	const struct string_list_item *it1 = a1, *it2 = a2;
+	const struct idinfo *i1 = it1->util, *i2 = it2->util;
+
+	if (i1->idx < i2->idx)
+		return -1;
+	else if (i1->idx > i2->idx)
+		return 1;
+	else if (i1->count < i2->count)
+		return 1;
+	else if (i1->count > i2->count)
+		return -1;
+	else
+		return 0;
+}
+
+static int compare_by_nr(const void *a1, const void *a2)
+{
+	const struct string_list *l1 = a1, *l2 = a2;
 
 	if (l1->nr < l2->nr)
 		return 1;
-	else if (l1->nr == l2->nr)
+	else if (l1->nr > l2->nr)
+		return -1;
+	else if (l1->nr == 0)
 		return 0;
 	else
+		return strcmp(l1->items[0].util, l2->items[0].util);
+}
+
+static int compare_by_first_util_str(const void *a1, const void *a2)
+{
+	const struct string_list *l1 = a1, *l2 = a2;
+	if (l1->nr && l2->nr)
+		return strcmp(l1->items[0].util, l2->items[0].util);
+	else if (!l1->nr && !l2->nr)
+		return 0;
+	else if (l1->nr)
 		return -1;
+	else
+		return 1;
+}
+
+static inline void alloc_grow_all_lines(struct shortlog *log)
+{
+	ALLOC_GROW(log->all_lines, log->nr + 1, log->alloc);
+	memset(log->all_lines + log->nr,
+	       0, (log->alloc - log->nr) * sizeof(struct string_list));
 }
 
 static void insert_one_record(struct shortlog *log,
@@ -35,9 +90,11 @@ static void insert_one_record(struct shortlog *log,
 {
 	const char *dot3 = log->common_repo_prefix;
 	char *buffer, *p;
-	struct string_list_item *item;
+	struct string_list_item *item, *name, *email;
 	char namebuf[1024];
-	size_t len;
+	char emailbuf[1024];
+	struct idinfo *nu, *eu;
+	size_t len, idx;
 	const char *eol;
 	const char *boemail, *eoemail;
 
@@ -61,16 +118,117 @@ static void insert_one_record(struct shortlog *log,
 	else
 		len = strlen(namebuf);
 
-	if (log->email) {
-		size_t room = sizeof(namebuf) - len - 1;
-		int maillen = eoemail - boemail + 1;
-		snprintf(namebuf + len, room, " %.*s", maillen, boemail);
-	}
-
-	item = string_list_insert(namebuf, &log->list);
-	if (item->util == NULL)
-		item->util = xcalloc(1, sizeof(struct string_list));
-
+	/*
+	 * log->all_lines is an array of string_lists where each list
+	 * contains all the records by an author. The author information
+	 * for all_lines[i] is in the element in log->names that has
+	 * the "idx" member set to i.
+	 *
+	 * If join_uids is on, we try to detect different spellings and
+	 * different email addresses of a same author: when saving a
+	 * record, we check if we've already seen either the name or the
+	 * address. If we have, we append to that author (and save the
+	 * new name/address as alternative spelling). If we have seen
+	 * both, but they point to different authors, we merge the
+	 * entries, and always associate the result with the address.
+	 *
+	 * To merge in the right order, each record in all_lines[x] has
+	 * an id (autocounter) in the "util" member.
+	 */
+	if (!log->join_uids) {
+		if (log->email) {
+			size_t room = sizeof(namebuf) - len - 1;
+			int maillen = eoemail - boemail + 1;
+			snprintf(namebuf + len, room,
+				 " %.*s", maillen, boemail);
+		}
+		name = string_list_insert(namebuf, &log->names);
+
+		if (name->util == NULL) {
+			alloc_grow_all_lines(log);
+			name->util = nu = xcalloc(1, sizeof(struct idinfo));
+			nu->idx = idx = log->nr++;
+			nu->count = 1;
+		}
+		else {
+			nu = name->util;
+			idx = nu->idx;
+			nu->count++;
+		}
+		goto write_line; /* Save one precious level of indentation. */
+	}
+
+	int maillen = eoemail - boemail - 1;
+	snprintf(emailbuf, sizeof(emailbuf), "%.*s", maillen, boemail+1);
+
+	name = string_list_insert(namebuf, &log->names);
+	email = string_list_insert(emailbuf, &log->emails);
+
+	if (name->util == NULL && email->util == NULL) {
+		alloc_grow_all_lines(log);
+		nu = xcalloc(1, sizeof(struct idinfo));
+		eu = xcalloc(1, sizeof(struct idinfo));
+		idx = nu->idx = eu->idx = log->nr++;
+	}
+	else if (name->util == NULL) {
+		nu = xcalloc(1, sizeof(struct idinfo));
+		eu = email->util;
+		idx = nu->idx = eu->idx;
+	}
+	else if (email->util == NULL) {
+		nu = name->util;
+		eu = xcalloc(1, sizeof(struct idinfo));
+		idx = eu->idx = nu->idx;
+	}
+	else {
+		nu = name->util;
+		eu = email->util;
+
+		if (nu->idx != eu->idx) {
+			/* Merge both entries. */
+			int i, j, oldidx;
+			struct idinfo *info;
+			struct string_list new = { NULL, 0, 0, 0 };
+			struct string_list *l1 = &log->all_lines[nu->idx];
+			struct string_list *l2 = &log->all_lines[eu->idx];
+
+			for (i = 0, j = 0; i < l1->nr && j < l2->nr; ) {
+				int c1 = (intptr_t) l1->items[i].util;
+				int c2 = (intptr_t) l2->items[j].util;
+				if (c1 < c2)
+					string_list_append(l1->items[i++].string, &new);
+				else
+					string_list_append(l2->items[j++].string, &new);
+			}
+			while (i < l1->nr) {
+				string_list_append(l1->items[i++].string, &new);
+			}
+			while (j < l2->nr) {
+				string_list_append(l2->items[j++].string, &new);
+			}
+
+			oldidx = nu->idx; /* Always favour the email. */
+
+			for (i = 0; i < log->names.nr; i++)
+				if ((info = log->names.items[i].util)->idx == oldidx)
+					info->idx = eu->idx;
+
+			for (i = 0; i < log->emails.nr; i++)
+				if ((info = log->emails.items[i].util)->idx == oldidx)
+					info->idx = eu->idx;
+
+			string_list_clear(l1, 0);
+			string_list_clear(l2, 0);
+			memcpy(l2, &new, sizeof(struct string_list));
+		}
+		idx = nu->idx;
+	}
+	nu->count++;
+	eu->count++;
+	name->util = nu;
+	email->util = eu;
+
+write_line:
 	/* Skip any leading whitespace, including any blank lines. */
 	while (*oneline && isspace(*oneline))
 		oneline++;
@@ -100,7 +258,8 @@ static void insert_one_record(struct shortlog *log,
 		}
 	}
 
-	string_list_append(buffer, item->util);
+	item = string_list_append(buffer, &log->all_lines[idx]);
+	item->util = (void*)(intptr_t) log->commit_count++;
 }
 
 static void read_from_stdin(struct shortlog *log)
@@ -218,10 +377,12 @@ void shortlog_init(struct shortlog *log)
 
 	read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
 
-	log->list.strdup_strings = 1;
 	log->wrap = DEFAULT_WRAPLEN;
 	log->in1 = DEFAULT_INDENT1;
 	log->in2 = DEFAULT_INDENT2;
+
+	log->names.strdup_strings = 1;
+	log->emails.strdup_strings = 1;
 }
 
 int cmd_shortlog(int argc, const char **argv, const char *prefix)
@@ -237,6 +398,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 			    "Suppress commit descriptions, only provides commit count"),
 		OPT_BOOLEAN('e', "email", &log.email,
 			    "Show the email address of each author"),
+		OPT_BOOLEAN('j', "join-uids", &log.join_uids,
+			    "Group together different spellings and addresses of an author"),
 		{ OPTION_CALLBACK, 'w', NULL, &log, "w[,i1[,i2]]",
 			"Linewrap output", PARSE_OPT_OPTARG, &parse_wrap_args },
 		OPT_END(),
@@ -285,16 +448,65 @@ parse_done:
 void shortlog_output(struct shortlog *log)
 {
 	int i, j;
-	if (log->sort_by_number)
-		qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
-			compare_by_number);
-	for (i = 0; i < log->list.nr; i++) {
-		struct string_list *onelines = log->list.items[i].util;
-
+
+	/*
+	 * We do some pre-processing to find the author name for each item
+	 * in log->all_lines, saving it in all_lines[i].util. If join_uids
+	 * is on, the most common spelling and address will be used.
+	 */
+	qsort(log->names.items, log->names.nr,
+	      sizeof(struct string_list_item), compare_by_idx_and_count);
+
+	if (log->join_uids && log->email)
+		qsort(log->emails.items, log->emails.nr,
+		      sizeof(struct string_list_item), compare_by_idx_and_count);
+
+	for (i = 0, j = 0; ; i++) {
+		static int idx = -1;
+		static const struct idinfo *info = NULL;
+		while (i < log->names.nr &&
+		       (info = log->names.items[i].util)->idx == idx)
+			i++;
+		if (!info || info->idx == idx)
+			break;
+		idx = info->idx;
+		if (log->join_uids && log->email) {
+			int len;
+			char *name, *email, *newname;
+			while (j < log->emails.nr &&
+			       (info = log->emails.items[j].util)->idx != idx)
+				j++;
+			if (j == log->emails.nr)
+				die("Could not find email address for '%s'",
+				    log->names.items[i].string);
+			name = log->names.items[i].string;
+			email = log->emails.items[j].string;
+			len = strlen(name) + strlen(email) + 4;
+			newname = xmalloc(len);
+			snprintf(newname, len, "%s <%s>", name, email);
+			free(name);
+			log->names.items[i].string = newname;
+		}
+		log->all_lines[idx].items[0].util = log->names.items[i].string;
+	}
+
+	qsort(log->all_lines, log->nr, sizeof(struct string_list),
+	      log->sort_by_number ? compare_by_nr : compare_by_first_util_str);
+
+	for (i = 0; i < log->nr; i++) {
+		const char *name;
+		struct string_list *onelines = &log->all_lines[i];
+
+		if (onelines->nr == 0)
+			/* There can be empty lists for merged names. */
+			continue;
+		else
+			name = onelines->items[0].util;
+
 		if (log->summary) {
-			printf("%6d\t%s\n", onelines->nr, log->list.items[i].string);
+			printf("%6d\t%s\n", onelines->nr, name);
 		} else {
-			printf("%s (%d):\n", log->list.items[i].string, onelines->nr);
+			printf("%s (%d):\n", name, onelines->nr);
 			for (j = onelines->nr - 1; j >= 0; j--) {
 				const char *msg = onelines->items[j].string;
 
@@ -308,15 +520,13 @@ void shortlog_output(struct shortlog *log)
 			}
 			putchar('\n');
 		}
-
 		onelines->strdup_strings = 1;
 		string_list_clear(onelines, 0);
-		free(onelines);
-		log->list.items[i].util = NULL;
-	}
-
-	log->list.strdup_strings = 1;
-	string_list_clear(&log->list, 1);
+	}
+
+	free(log->all_lines);
+	string_list_clear(&log->names, 1);
+	string_list_clear(&log->emails, 1);
 	log->mailmap.strdup_strings = 1;
 	string_list_clear(&log->mailmap, 1);
 }
diff --git a/shortlog.h b/shortlog.h
index bc02cc2..f539bf0 100644
--- a/shortlog.h
+++ b/shortlog.h
@@ -4,18 +4,24 @@
 #include "string-list.h"
 
 struct shortlog {
-	struct string_list list;
 	int summary;
 	int wrap_lines;
 	int sort_by_number;
 	int wrap;
 	int in1;
 	int in2;
-	int user_format;
-
+	int email;
+	int join_uids;
+
+	int user_format;
 	char *common_repo_prefix;
-	int email;
 	struct string_list mailmap;
+
+	int nr, alloc;
+	int commit_count;
+	struct string_list names;
+	struct string_list emails;
+	struct string_list *all_lines;
 };
 
 void shortlog_init(struct shortlog *log);
-- 
1.6.1.134.g55c35

^ 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