Git development
 help / color / mirror / Atom feed
* Re: [PATCH] better git-submodule status output
From: Johannes Schindelin @ 2008-07-09 13:54 UTC (permalink / raw)
  To: Sylvain Joyeux; +Cc: git
In-Reply-To: <20080709134629.GA3848@joyeux>

Hi,

On Wed, 9 Jul 2008, Sylvain Joyeux wrote:

> [Sylvain quoted somebody, but found it funny to let the reader guess]
>
> > On a related note, the long commit name has been a constant nuisance 
> > for me.  A short commit name is perfectly enough, methinks.
>
> I also think so. The commit ID is completely useless for humans. 
> Nonetheless, that would change the git-submodules output which 
> (according to someone-that-I-dont-remember) would be a Bad Thing(tm).

Is git-submodule supposed to be plumbing?  I don't think so.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] better git-submodule status output
From: Sylvain Joyeux @ 2008-07-09 13:46 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <alpine.DEB.1.00.0807091427270.5277@eeepc-johanness>

> >   On the symbols side, I propose:
> >     > submodule commit is a direct descendant of the commit in the
> >       superproject
> >     < submodule commit is a direct ancestor of the commit in the
> >       superproject
> >     + no direct relation between submodule commit and commit in the
> >       superproject
> >     ? the commit in the superproject cannot be found in the submodule
> >       (replaces the initial '!' in my patch)
> 
> I like the output of "git checkout" when your local branch is not agreeing 
> with the tracked branch.  Maybe it would be more user-friendly to have 
> submodule use that type of message, instead of cryptic single letter?
Mmmm ... Yes that could be a great idea. I'll try it.
 
> On a related note, the long commit name has been a constant nuisance for 
> me.  A short commit name is perfectly enough, methinks.
I also think so. The commit ID is completely useless for humans.
Nonetheless, that would change the git-submodules output which
(according to someone-that-I-dont-remember) would be a Bad Thing(tm).

> > - define a git-submodule 'fetch' subcommand which call fetch in each
> >   submodule and runs the verbose mode of git-status (can be disabled by
> >   a --quiet option).
> 
> I like it.
> 
> You probably can refactor the code for "update" to do that nicely.
OK

Sylvain

^ permalink raw reply

* Re: GiT and CentOS 5.2
From: David Voit @ 2008-07-09 13:25 UTC (permalink / raw)
  To: git
In-Reply-To: <49523.216.185.71.22.1215539200.squirrel@webmail.harte-lyne.ca>

Hi all,

Just use the EPEL packages:
http://wiki.centos.org/AdditionalResources/Repositories?action=show&redirect=Repositories

Maybe not alwaya the newest version, but it works.

David

^ permalink raw reply

* clever(er) file merging tools?
From: Nigel Magnay @ 2008-07-09 13:16 UTC (permalink / raw)
  To: Git Mailing List

This isn't git-specific, but since git users tend to merge more often
there might be some good knowledge here.

My usual practice in merging is to issue the merge, then use 'git
mergetool' to cope with conflicts. On my mac, that fires up opendiff,
which does a reasonable (I guess) attempt at unpicking the changes and
allowing me to choose.

Sometimes, however, probably due to complexity, it spits out a huge
section of the file as being in conflict. Examining it, I always end
up getting annoyed, since for a lot of the file, the changes aren't in
conflict at all - whole (java) functions are in the conflict area
where they're actually the same.

I have wondered if I could pre-filter the sourcecode through something
like Jalopy to re-order all the blocks into a common ordering. But I
also wondered if there were any merge tools that (say) understand the
structure of Java and use it to compare at a method declaration level
rather than as a big text document? Maybe just opendiff isn't the best
(though I quite like the interface as it's easy to understand and the
price is right...)

^ permalink raw reply

* submodules and interaction with GIT
From: Pierre Habouzit @ 2008-07-09 13:07 UTC (permalink / raw)
  To: Git ML; +Cc: Junio C Hamano, Lars Hjemli

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

Let's start with a bit of context. We have this __huge__ "put everything
in it"-repository at work, and we want to strip out core modules and
integrate them in our different projects through submodules.  We moved
away one of our core libraries into its separate git repository, and it
became a submodule in our big fat repository. I believe it's the kind of
things we said people should do when they need partial checkouts
(tree-wise) so I assume the workflow I describe here is decent.

Just to make things clearer, we have two branches in this repository,
'maint' and 'master'. Maint is the branch for the production product,
master is the one where devel happens. 'maint' is obviously merged into
'master' on a regular basis.


Problem 1: directory/submodule conflicts (aka D/S)
---------

Our first problem was that git doesn't deal with D/S conflicts well.  To
migrate our repository, I went into 'maint' and did:

  $ git rm -rf corelib
  $ git submodule add -b corelib/master -- <our-repo> corelib
  $ git commit -asm'replace corelib with a submodule'

  Then I went into 'master' and did:

  $ git merge maint

Here it failed horribly because it claimed that the merge would clobber
untracked files like corelib/.gitignore which was a previously tracked
file in the huge repository and is now tracked in the submodule.

I worked that around by having an intermediate commit that removes
'corelib' in 'master'. Unpretty, but works.  Later, when other
developers updated their trees, they had all kinds of really distateful
issues related to D/S conflicts.



Problem 2: integration with git-checkout
---------

When using submodules, when I do updates to the corelib, like fixing a
bug, hence I want it to appear in 'maint', I go to maint and basically
do:

  $ cd corelib
  $ git fetch
  $ git reset --hard origin/corelib/master # so that I have the fix
  $ cd ..
  $ git commit -asm'update corelib for bug#nnn'

When then I `git checkout master`, the corelib submodule had no
modifications in 'maint' but remains in its 'maint' state when I go to
master instead of what I would like: see it be checkout to its 'master'
state, and refuse to checkout if the submodule cannot perform the
checkout.

I'd really like git checkout -m to also perform a git checkout -m in
submodules.

And along the road, one has a lot of frightening errors:
    fatal: cannot read object b8f1177da31281682feb79c9d4290a88edf067ae 'corelib~Updated upstream': It is a submodule!


I quite understand that in presence of submodules git checkout works
becomes quite harder as you have to check for every submodule plus
yourself to know if you can perform the checkout, but I don't really see
why it can't be done.


Problem 3: similar problem with git-reset
---------

Really, I type git reset --hard all the time to undo my local changes.
And I know while typing that it destroys local changes. Really, it
should reset the submodules to their supposed state as well.



Problem 4: merging
---------

When merging two branches, there is a strategy that I believe is
applicable for submodules. If one of the two submodules states is a
direct ancestor from the other, then the merge result shall be the
descendant.

When revisions are not in direct line, then it shall be a conflict.


Problem 5: fetching
---------

`git fetch` should fetch submodules too. Arguably, if you type `git
fetch REMOTE` then any submodule that has a corresponding "REMOTE"
configured should fetch it.


Notes:
-----

When you cannot know something required for conflicts handling e.g.,
(because you haven't enough history for the submodules) the command
shall fail asking the user to fetch the incriminated submodules. IOW
when you perform any action that involves submodules, each submodules
must be queried to know if it can performs the action, and git shall
fail if it's not the case and do nothing.

Wrt most of the behaviours I described, I would be fine if those were
enabled only by a configuration flag in the .gitmodules, and that user
can override in their .git/config. We could have a
submodule.<module>.commandsMustRecurse setting to tell
fetch/reset/checkout/... to behave like I said with this module. I
believe that true should be the default.

Non initialized submodules should be considered as always up to date for
all of this, so that people that don't want to waste bandwidth for this
or this submodule can work peacefully.



Okay, I'm sure there are tons of other uses of submodules out there for
which this is an overkill, but if we really intend seriously to tell
people "do use submodules to avoid having incredibly huge repositories"
like we did in the past, we should really improve the overall usability.
-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

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

^ permalink raw reply

* [PATCHv2] rerere: Separate libgit and builtin functions
From: Stephan Beyer @ 2008-07-09 12:58 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, git, Stephan Beyer
In-Reply-To: <20080709120030.GA10528@leksak.fem-net>

This patch moves rerere()-related functions into a newly created
rerere.c file.
The setup_rerere() function is needed by both rerere() and cmd_rerere(),
so this function is moved to rerere.c and declared non-static (and "extern")
in newly created rerere.h file.

Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---
Hi again,

so now it should apply cleanly.
Seems that I had not built the topic branch of the former patch
on top of the latest master by accident.

Regards,
  Stephan

 Makefile                     |    2 +
 builtin-commit.c             |    1 +
 builtin-rerere.c             |  362 ++---------------------------------------
 commit.h                     |    1 -
 builtin-rerere.c => rerere.c |  129 +---------------
 rerere.h                     |    9 +
 6 files changed, 33 insertions(+), 471 deletions(-)
 copy builtin-rerere.c => rerere.c (71%)
 create mode 100644 rerere.h

diff --git a/Makefile b/Makefile
index 4796565..de15163 100644
--- a/Makefile
+++ b/Makefile
@@ -363,6 +363,7 @@ LIB_H += quote.h
 LIB_H += reflog-walk.h
 LIB_H += refs.h
 LIB_H += remote.h
+LIB_H += rerere.h
 LIB_H += revision.h
 LIB_H += run-command.h
 LIB_H += sha1-lookup.h
@@ -447,6 +448,7 @@ LIB_OBJS += read-cache.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += remote.o
+LIB_OBJS += rerere.o
 LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
 LIB_OBJS += server-info.o
diff --git a/builtin-commit.c b/builtin-commit.c
index 745c11e..bdc83df 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -22,6 +22,7 @@
 #include "utf8.h"
 #include "parse-options.h"
 #include "path-list.h"
+#include "rerere.h"
 #include "unpack-trees.h"
 
 static const char * const builtin_commit_usage[] = {
diff --git a/builtin-rerere.c b/builtin-rerere.c
index 839b26e..5d40e16 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -1,11 +1,10 @@
 #include "builtin.h"
 #include "cache.h"
 #include "path-list.h"
+#include "rerere.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 
-#include <time.h>
-
 static const char git_rerere_usage[] =
 "git-rerere [clear | status | diff | gc]";
 
@@ -13,14 +12,6 @@ static const char git_rerere_usage[] =
 static int cutoff_noresolve = 15;
 static int cutoff_resolve = 60;
 
-/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
-static int rerere_enabled = -1;
-
-/* automatically update cleanly resolved paths to the index */
-static int rerere_autoupdate;
-
-static char *merge_rr_path;
-
 static const char *rr_path(const char *name, const char *file)
 {
 	return git_path("rr-cache/%s/%s", name, file);
@@ -38,179 +29,6 @@ static int has_resolution(const char *name)
 	return !stat(rr_path(name, "postimage"), &st);
 }
 
-static void read_rr(struct path_list *rr)
-{
-	unsigned char sha1[20];
-	char buf[PATH_MAX];
-	FILE *in = fopen(merge_rr_path, "r");
-	if (!in)
-		return;
-	while (fread(buf, 40, 1, in) == 1) {
-		int i;
-		char *name;
-		if (get_sha1_hex(buf, sha1))
-			die("corrupt MERGE_RR");
-		buf[40] = '\0';
-		name = xstrdup(buf);
-		if (fgetc(in) != '\t')
-			die("corrupt MERGE_RR");
-		for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
-			; /* do nothing */
-		if (i == sizeof(buf))
-			die("filename too long");
-		path_list_insert(buf, rr)->util = name;
-	}
-	fclose(in);
-}
-
-static struct lock_file write_lock;
-
-static int write_rr(struct path_list *rr, int out_fd)
-{
-	int i;
-	for (i = 0; i < rr->nr; i++) {
-		const char *path;
-		int length;
-		if (!rr->items[i].util)
-			continue;
-		path = rr->items[i].path;
-		length = strlen(path) + 1;
-		if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
-		    write_in_full(out_fd, "\t", 1) != 1 ||
-		    write_in_full(out_fd, path, length) != length)
-			die("unable to write rerere record");
-	}
-	if (commit_lock_file(&write_lock) != 0)
-		die("unable to write rerere record");
-	return 0;
-}
-
-static int handle_file(const char *path,
-	 unsigned char *sha1, const char *output)
-{
-	SHA_CTX ctx;
-	char buf[1024];
-	int hunk = 0, hunk_no = 0;
-	struct strbuf one, two;
-	FILE *f = fopen(path, "r");
-	FILE *out = NULL;
-
-	if (!f)
-		return error("Could not open %s", path);
-
-	if (output) {
-		out = fopen(output, "w");
-		if (!out) {
-			fclose(f);
-			return error("Could not write %s", output);
-		}
-	}
-
-	if (sha1)
-		SHA1_Init(&ctx);
-
-	strbuf_init(&one, 0);
-	strbuf_init(&two,  0);
-	while (fgets(buf, sizeof(buf), f)) {
-		if (!prefixcmp(buf, "<<<<<<< "))
-			hunk = 1;
-		else if (!prefixcmp(buf, "======="))
-			hunk = 2;
-		else if (!prefixcmp(buf, ">>>>>>> ")) {
-			if (strbuf_cmp(&one, &two) > 0)
-				strbuf_swap(&one, &two);
-			hunk_no++;
-			hunk = 0;
-			if (out) {
-				fputs("<<<<<<<\n", out);
-				fwrite(one.buf, one.len, 1, out);
-				fputs("=======\n", out);
-				fwrite(two.buf, two.len, 1, out);
-				fputs(">>>>>>>\n", out);
-			}
-			if (sha1) {
-				SHA1_Update(&ctx, one.buf ? one.buf : "",
-					    one.len + 1);
-				SHA1_Update(&ctx, two.buf ? two.buf : "",
-					    two.len + 1);
-			}
-			strbuf_reset(&one);
-			strbuf_reset(&two);
-		} else if (hunk == 1)
-			strbuf_addstr(&one, buf);
-		else if (hunk == 2)
-			strbuf_addstr(&two, buf);
-		else if (out)
-			fputs(buf, out);
-	}
-	strbuf_release(&one);
-	strbuf_release(&two);
-
-	fclose(f);
-	if (out)
-		fclose(out);
-	if (sha1)
-		SHA1_Final(sha1, &ctx);
-	if (hunk) {
-		if (output)
-			unlink(output);
-		return error("Could not parse conflict hunks in %s", path);
-	}
-	return hunk_no;
-}
-
-static int find_conflict(struct path_list *conflict)
-{
-	int i;
-	if (read_cache() < 0)
-		return error("Could not read index");
-	for (i = 0; i+1 < active_nr; i++) {
-		struct cache_entry *e2 = active_cache[i];
-		struct cache_entry *e3 = active_cache[i+1];
-		if (ce_stage(e2) == 2 &&
-		    ce_stage(e3) == 3 &&
-		    ce_same_name(e2, e3) &&
-		    S_ISREG(e2->ce_mode) &&
-		    S_ISREG(e3->ce_mode)) {
-			path_list_insert((const char *)e2->name, conflict);
-			i++; /* skip over both #2 and #3 */
-		}
-	}
-	return 0;
-}
-
-static int merge(const char *name, const char *path)
-{
-	int ret;
-	mmfile_t cur, base, other;
-	mmbuffer_t result = {NULL, 0};
-	xpparam_t xpp = {XDF_NEED_MINIMAL};
-
-	if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
-		return 1;
-
-	if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
-			read_mmfile(&base, rr_path(name, "preimage")) ||
-			read_mmfile(&other, rr_path(name, "postimage")))
-		return 1;
-	ret = xdl_merge(&base, &cur, "", &other, "",
-			&xpp, XDL_MERGE_ZEALOUS, &result);
-	if (!ret) {
-		FILE *f = fopen(path, "w");
-		if (!f)
-			return error("Could not write to %s", path);
-		fwrite(result.ptr, result.size, 1, f);
-		fclose(f);
-	}
-
-	free(cur.ptr);
-	free(base.ptr);
-	free(other.ptr);
-	free(result.ptr);
-
-	return ret;
-}
-
 static void unlink_rr_item(const char *name)
 {
 	unlink(rr_path(name, "thisimage"));
@@ -219,6 +37,17 @@ static void unlink_rr_item(const char *name)
 	rmdir(git_path("rr-cache/%s", name));
 }
 
+static int git_rerere_gc_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "gc.rerereresolved"))
+		cutoff_resolve = git_config_int(var, value);
+	else if (!strcmp(var, "gc.rerereunresolved"))
+		cutoff_noresolve = git_config_int(var, value);
+	else
+		return git_default_config(var, value, cb);
+	return 0;
+}
+
 static void garbage_collect(struct path_list *rr)
 {
 	struct path_list to_remove = { NULL, 0, 0, 1 };
@@ -227,6 +56,7 @@ static void garbage_collect(struct path_list *rr)
 	int i, cutoff;
 	time_t now = time(NULL), then;
 
+	git_config(git_rerere_gc_config, NULL);
 	dir = opendir(git_path("rr-cache"));
 	while ((e = readdir(dir))) {
 		const char *name = e->d_name;
@@ -279,181 +109,25 @@ static int diff_two(const char *file1, const char *label1,
 	return 0;
 }
 
-static struct lock_file index_lock;
-
-static int update_paths(struct path_list *update)
-{
-	int i;
-	int fd = hold_locked_index(&index_lock, 0);
-	int status = 0;
-
-	if (fd < 0)
-		return -1;
-
-	for (i = 0; i < update->nr; i++) {
-		struct path_list_item *item = &update->items[i];
-		if (add_file_to_cache(item->path, ADD_CACHE_IGNORE_ERRORS))
-			status = -1;
-	}
-
-	if (!status && active_cache_changed) {
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(&index_lock))
-			die("Unable to write new index file");
-	} else if (fd >= 0)
-		rollback_lock_file(&index_lock);
-	return status;
-}
-
-static int do_plain_rerere(struct path_list *rr, int fd)
-{
-	struct path_list conflict = { NULL, 0, 0, 1 };
-	struct path_list update = { NULL, 0, 0, 1 };
-	int i;
-
-	find_conflict(&conflict);
-
-	/*
-	 * MERGE_RR records paths with conflicts immediately after merge
-	 * failed.  Some of the conflicted paths might have been hand resolved
-	 * in the working tree since then, but the initial run would catch all
-	 * and register their preimages.
-	 */
-
-	for (i = 0; i < conflict.nr; i++) {
-		const char *path = conflict.items[i].path;
-		if (!path_list_has_path(rr, path)) {
-			unsigned char sha1[20];
-			char *hex;
-			int ret;
-			ret = handle_file(path, sha1, NULL);
-			if (ret < 1)
-				continue;
-			hex = xstrdup(sha1_to_hex(sha1));
-			path_list_insert(path, rr)->util = hex;
-			if (mkdir(git_path("rr-cache/%s", hex), 0755))
-				continue;;
-			handle_file(path, NULL, rr_path(hex, "preimage"));
-			fprintf(stderr, "Recorded preimage for '%s'\n", path);
-		}
-	}
-
-	/*
-	 * Now some of the paths that had conflicts earlier might have been
-	 * hand resolved.  Others may be similar to a conflict already that
-	 * was resolved before.
-	 */
-
-	for (i = 0; i < rr->nr; i++) {
-		int ret;
-		const char *path = rr->items[i].path;
-		const char *name = (const char *)rr->items[i].util;
-
-		if (has_resolution(name)) {
-			if (!merge(name, path)) {
-				fprintf(stderr, "Resolved '%s' using "
-						"previous resolution.\n", path);
-				if (rerere_autoupdate)
-					path_list_insert(path, &update);
-				goto mark_resolved;
-			}
-		}
-
-		/* Let's see if we have resolved it. */
-		ret = handle_file(path, NULL, NULL);
-		if (ret)
-			continue;
-
-		fprintf(stderr, "Recorded resolution for '%s'.\n", path);
-		copy_file(rr_path(name, "postimage"), path, 0666);
-	mark_resolved:
-		rr->items[i].util = NULL;
-	}
-
-	if (update.nr)
-		update_paths(&update);
-
-	return write_rr(rr, fd);
-}
-
-static int git_rerere_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "gc.rerereresolved"))
-		cutoff_resolve = git_config_int(var, value);
-	else if (!strcmp(var, "gc.rerereunresolved"))
-		cutoff_noresolve = git_config_int(var, value);
-	else if (!strcmp(var, "rerere.enabled"))
-		rerere_enabled = git_config_bool(var, value);
-	else if (!strcmp(var, "rerere.autoupdate"))
-		rerere_autoupdate = git_config_bool(var, value);
-	else
-		return git_default_config(var, value, cb);
-	return 0;
-}
-
-static int is_rerere_enabled(void)
-{
-	struct stat st;
-	const char *rr_cache;
-	int rr_cache_exists;
-
-	if (!rerere_enabled)
-		return 0;
-
-	rr_cache = git_path("rr-cache");
-	rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
-	if (rerere_enabled < 0)
-		return rr_cache_exists;
-
-	if (!rr_cache_exists &&
-	    (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
-		die("Could not create directory %s", rr_cache);
-	return 1;
-}
-
-static int setup_rerere(struct path_list *merge_rr)
-{
-	int fd;
-
-	git_config(git_rerere_config, NULL);
-	if (!is_rerere_enabled())
-		return -1;
-
-	merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
-	fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
-	read_rr(merge_rr);
-	return fd;
-}
-
-int rerere(void)
-{
-	struct path_list merge_rr = { NULL, 0, 0, 1 };
-	int fd;
-
-	fd = setup_rerere(&merge_rr);
-	if (fd < 0)
-		return 0;
-	return do_plain_rerere(&merge_rr, fd);
-}
-
 int cmd_rerere(int argc, const char **argv, const char *prefix)
 {
 	struct path_list merge_rr = { NULL, 0, 0, 1 };
 	int i, fd;
 
+	if (argc < 2)
+		return rerere();
+
 	fd = setup_rerere(&merge_rr);
 	if (fd < 0)
 		return 0;
 
-	if (argc < 2)
-		return do_plain_rerere(&merge_rr, fd);
-	else if (!strcmp(argv[1], "clear")) {
+	if (!strcmp(argv[1], "clear")) {
 		for (i = 0; i < merge_rr.nr; i++) {
 			const char *name = (const char *)merge_rr.items[i].util;
 			if (!has_resolution(name))
 				unlink_rr_item(name);
 		}
-		unlink(merge_rr_path);
+		unlink(git_path("rr-cache/MERGE_RR"));
 	} else if (!strcmp(argv[1], "gc"))
 		garbage_collect(&merge_rr);
 	else if (!strcmp(argv[1], "status"))
diff --git a/commit.h b/commit.h
index 2d94d41..fda7ad0 100644
--- a/commit.h
+++ b/commit.h
@@ -131,7 +131,6 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
 int in_merge_bases(struct commit *, struct commit **, int);
 
 extern int interactive_add(int argc, const char **argv, const char *prefix);
-extern int rerere(void);
 
 static inline int single_parent(struct commit *commit)
 {
diff --git a/builtin-rerere.c b/rerere.c
similarity index 71%
copy from builtin-rerere.c
copy to rerere.c
index 839b26e..bbd8c00 100644
--- a/builtin-rerere.c
+++ b/rerere.c
@@ -1,18 +1,9 @@
-#include "builtin.h"
 #include "cache.h"
 #include "path-list.h"
+#include "rerere.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 
-#include <time.h>
-
-static const char git_rerere_usage[] =
-"git-rerere [clear | status | diff | gc]";
-
-/* these values are days */
-static int cutoff_noresolve = 15;
-static int cutoff_resolve = 60;
-
 /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
 static int rerere_enabled = -1;
 
@@ -26,12 +17,6 @@ static const char *rr_path(const char *name, const char *file)
 	return git_path("rr-cache/%s/%s", name, file);
 }
 
-static time_t rerere_created_at(const char *name)
-{
-	struct stat st;
-	return stat(rr_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
-}
-
 static int has_resolution(const char *name)
 {
 	struct stat st;
@@ -211,74 +196,6 @@ static int merge(const char *name, const char *path)
 	return ret;
 }
 
-static void unlink_rr_item(const char *name)
-{
-	unlink(rr_path(name, "thisimage"));
-	unlink(rr_path(name, "preimage"));
-	unlink(rr_path(name, "postimage"));
-	rmdir(git_path("rr-cache/%s", name));
-}
-
-static void garbage_collect(struct path_list *rr)
-{
-	struct path_list to_remove = { NULL, 0, 0, 1 };
-	DIR *dir;
-	struct dirent *e;
-	int i, cutoff;
-	time_t now = time(NULL), then;
-
-	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')))
-			continue;
-		then = rerere_created_at(name);
-		if (!then)
-			continue;
-		cutoff = (has_resolution(name)
-			  ? cutoff_resolve : cutoff_noresolve);
-		if (then < now - cutoff * 86400)
-			path_list_append(name, &to_remove);
-	}
-	for (i = 0; i < to_remove.nr; i++)
-		unlink_rr_item(to_remove.items[i].path);
-	path_list_clear(&to_remove, 0);
-}
-
-static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
-{
-	int i;
-	for (i = 0; i < nbuf; i++)
-		if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
-			return -1;
-	return 0;
-}
-
-static int diff_two(const char *file1, const char *label1,
-		const char *file2, const char *label2)
-{
-	xpparam_t xpp;
-	xdemitconf_t xecfg;
-	xdemitcb_t ecb;
-	mmfile_t minus, plus;
-
-	if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
-		return 1;
-
-	printf("--- a/%s\n+++ b/%s\n", label1, label2);
-	fflush(stdout);
-	xpp.flags = XDF_NEED_MINIMAL;
-	memset(&xecfg, 0, sizeof(xecfg));
-	xecfg.ctxlen = 3;
-	ecb.outf = outf;
-	xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
-
-	free(minus.ptr);
-	free(plus.ptr);
-	return 0;
-}
-
 static struct lock_file index_lock;
 
 static int update_paths(struct path_list *update)
@@ -378,11 +295,7 @@ static int do_plain_rerere(struct path_list *rr, int fd)
 
 static int git_rerere_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "gc.rerereresolved"))
-		cutoff_resolve = git_config_int(var, value);
-	else if (!strcmp(var, "gc.rerereunresolved"))
-		cutoff_noresolve = git_config_int(var, value);
-	else if (!strcmp(var, "rerere.enabled"))
+	if (!strcmp(var, "rerere.enabled"))
 		rerere_enabled = git_config_bool(var, value);
 	else if (!strcmp(var, "rerere.autoupdate"))
 		rerere_autoupdate = git_config_bool(var, value);
@@ -411,7 +324,7 @@ static int is_rerere_enabled(void)
 	return 1;
 }
 
-static int setup_rerere(struct path_list *merge_rr)
+int setup_rerere(struct path_list *merge_rr)
 {
 	int fd;
 
@@ -435,39 +348,3 @@ int rerere(void)
 		return 0;
 	return do_plain_rerere(&merge_rr, fd);
 }
-
-int cmd_rerere(int argc, const char **argv, const char *prefix)
-{
-	struct path_list merge_rr = { NULL, 0, 0, 1 };
-	int i, fd;
-
-	fd = setup_rerere(&merge_rr);
-	if (fd < 0)
-		return 0;
-
-	if (argc < 2)
-		return do_plain_rerere(&merge_rr, fd);
-	else if (!strcmp(argv[1], "clear")) {
-		for (i = 0; i < merge_rr.nr; i++) {
-			const char *name = (const char *)merge_rr.items[i].util;
-			if (!has_resolution(name))
-				unlink_rr_item(name);
-		}
-		unlink(merge_rr_path);
-	} else if (!strcmp(argv[1], "gc"))
-		garbage_collect(&merge_rr);
-	else if (!strcmp(argv[1], "status"))
-		for (i = 0; i < merge_rr.nr; i++)
-			printf("%s\n", merge_rr.items[i].path);
-	else if (!strcmp(argv[1], "diff"))
-		for (i = 0; i < merge_rr.nr; i++) {
-			const char *path = merge_rr.items[i].path;
-			const char *name = (const char *)merge_rr.items[i].util;
-			diff_two(rr_path(name, "preimage"), path, path, path);
-		}
-	else
-		usage(git_rerere_usage);
-
-	path_list_clear(&merge_rr, 1);
-	return 0;
-}
diff --git a/rerere.h b/rerere.h
new file mode 100644
index 0000000..35b0fa8
--- /dev/null
+++ b/rerere.h
@@ -0,0 +1,9 @@
+#ifndef RERERE_H
+#define RERERE_H
+
+#include "path-list.h"
+
+extern int setup_rerere(struct path_list *);
+extern int rerere(void);
+
+#endif
-- 
1.5.6

^ permalink raw reply related

* Re: [PATCH] better git-submodule status output
From: Johannes Schindelin @ 2008-07-09 12:31 UTC (permalink / raw)
  To: Sylvain Joyeux; +Cc: git
In-Reply-To: <20080709101330.GA3525@joyeux>

Hi,

On Wed, 9 Jul 2008, Sylvain Joyeux wrote:

>   On the symbols side, I propose:
>     > submodule commit is a direct descendant of the commit in the
>       superproject
>     < submodule commit is a direct ancestor of the commit in the
>       superproject
>     + no direct relation between submodule commit and commit in the
>       superproject
>     ? the commit in the superproject cannot be found in the submodule
>       (replaces the initial '!' in my patch)

I like the output of "git checkout" when your local branch is not agreeing 
with the tracked branch.  Maybe it would be more user-friendly to have 
submodule use that type of message, instead of cryptic single letter?

On a related note, the long commit name has been a constant nuisance for 
me.  A short commit name is perfectly enough, methinks.

> - define a git-submodule 'fetch' subcommand which call fetch in each
>   submodule and runs the verbose mode of git-status (can be disabled by
>   a --quiet option).

I like it.

You probably can refactor the code for "update" to do that nicely.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH 1/3] cherry: cache patch-ids to avoid repeating work
From: Johannes Schindelin @ 2008-07-09 12:18 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Geoffrey Irving, git@vger.kernel.org
In-Reply-To: <7vprpnlglh.fsf@gitster.siamese.dyndns.org>

Hi,

On Tue, 8 Jul 2008, Junio C Hamano wrote:

> Think of this procedure as giving a chance for you to hide early 
> embarrassment under the rug ;-)

Further, as Shawn pointed out to me (and you will all be able to hear it 
for yourselves soon), these patch iterations give you the chance to apply 
all the wisdom of the combined developers on this list to your patch, and 
in the end put your name on it :-)

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH 1/4] git-imap-send: Allow the program to be run from subdirectories of a git tree.
From: Rob Shearman @ 2008-07-09 12:08 UTC (permalink / raw)
  To: Jay Soffian; +Cc: Junio C Hamano, git
In-Reply-To: <76718490807081828k7640d07bp547a69d05a6e07c4@mail.gmail.com>

2008/7/9 Jay Soffian <jaysoffian@gmail.com>:
> On Tue, Jul 8, 2008 at 9:15 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> I thought Jeff already explained why this NULL was a bad idea, but perhaps
>> I was dreaming...
>
> http://article.gmane.org/gmane.comp.version-control.git/87701

Sorry, I missed that message. I'll resend the patch with his suggestion.

-- 
Rob Shearman

^ permalink raw reply

* Re: [PATCH 4/4] Documentation: Improve documentation for git-imap-send(1).
From: Rob Shearman @ 2008-07-09 12:05 UTC (permalink / raw)
  To: Brian Gernhardt; +Cc: git
In-Reply-To: <19AD9080-74BE-4147-8DF5-8A2937129B6D@silverinsanity.com>

2008/7/9 Brian Gernhardt <benji@silverinsanity.com>:
>
> On Jul 8, 2008, at 6:18 PM, Robert Shearman wrote:
>
>> +Using direct mode:
>>
>> +.........................
>> [imap]
>> -    Host = imaps://imap.example.com
>> -    User = bob
>> -    Pass = pwd
>> -    Port = 993
>> +    folder = "[Gmail]/Drafts"
>> +    host = imaps://imap.example.com
>> +    user = bob
>> +    pass = p4ssw0rd
>> +    port = 123
>>    sslverify = false
>> ..........................
>
> If you're going to use [Gmail]/Drafts as the example folder, shouldn't you
> just use mail.google.com as your example?  Google themselves use
> username@gmail.com as an example[1], so that should be safe. So:

I'll fix it not to use "[Gmail]/Drafts" in the example as that is the
exception rather than the norm.

> And I also assume that someone has tried this with Gmail and it doesn't
> mangle the Drafts.

Yes, as specified in my previous email I tested with two different
IMAP servers, one of which was Gmail.

>  I'd also move the sslverify option into the tunneling
> example, as it's more likely to be needed there.

Actually, the sslverify option is ignored in tunnel mode (since you're
likely to be using your own encryption with the tunnel). I'll document
this and other ignored options in a revised patch.

-- 
Rob Shearman

^ permalink raw reply

* Re: [PATCH] rerere: Separate libgit and builtin functions
From: Johannes Schindelin @ 2008-07-09 12:03 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Junio C Hamano
In-Reply-To: <1215598683-9685-1-git-send-email-s-beyer@gmx.net>

Hi,

On Wed, 9 Jul 2008, Stephan Beyer wrote:

>  Makefile                     |    2 +
>  builtin-rerere.c             |  324 +++---------------------------------------
>  builtin-rerere.c => rerere.c |  133 +-----------------
>  rerere.h                     |    9 ++
>  4 files changed, 32 insertions(+), 436 deletions(-)

Heh, that sounds nice!  Deleting way more lines than adding!  :-)

>  copy builtin-rerere.c => rerere.c (66%)

Oh, well :-)

> diff --git a/builtin-rerere.c b/rerere.c
> similarity index 66%
> copy from builtin-rerere.c
> copy to rerere.c
> index 85222d9..5c22bed 100644
> --- a/builtin-rerere.c
> +++ b/rerere.c
> @@ -1,18 +1,11 @@
> -#include "builtin.h"
>  #include "cache.h"
>  #include "path-list.h"
> +#include "rerere.h"
>  #include "xdiff/xdiff.h"
>  #include "xdiff-interface.h"
>  
>  #include <time.h>

No longer necessary, is it?

> diff --git a/rerere.h b/rerere.h
> new file mode 100644
> index 0000000..35b0fa8
> --- /dev/null
> +++ b/rerere.h
> @@ -0,0 +1,9 @@
> +#ifndef RERERE_H
> +#define RERERE_H
> +
> +#include "path-list.h"
> +
> +extern int setup_rerere(struct path_list *);

Why?

Ah, it is needed for "gc".  Maybe this needs documentation, since it cause 
some minutes of head scratching with yours truly.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH 2/4] git-imap-send: Add support for SSL.
From: Rob Shearman @ 2008-07-09 12:02 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vbq18q7yk.fsf@gitster.siamese.dyndns.org>

2008/7/9 Junio C Hamano <gitster@pobox.com>:
> Robert Shearman <robertshearman@gmail.com> writes:
>> diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
>> index b3d8da3..e4a5873 100644
>> --- a/Documentation/git-imap-send.txt
>> +++ b/Documentation/git-imap-send.txt
>> @@ -37,10 +37,11 @@ configuration file (shown with examples):
>>      Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null"
>>
>>  [imap]
>> -    Host = imap.server.com
>> +    Host = imaps://imap.example.com
>>      User = bob
>>      Pass = pwd
>> -    Port = 143
>> +    Port = 993
>> +    sslverify = false
>>  ..........................
>
> Don't we also want to keep a vanilla configuration in the example, or is
> imaps the norm and unencrypted imap is exception these days?

Good point. I'll fix the documentation to use imap:// instead of
imaps:// and not change the port number. However, I'm not sure the
examples should be telling the user what they should do, but rather
what they can do.

> Don't we need to support custom certificates, keys and CAs, just like our
> code that supports https does, by honoring GIT_SSL_* environment variables
> and configuration file entries?

Yes, eventually we will want that support in imap-send too. It should
be fairly trivial to do, although testing will be more difficult.

>  The patch itself looks fairly clean, and
> I'd like to queue this for wider testing, initially even without GIT_SSL_*
> support.  But I'd like to see any patch with substantial amount of changes
> properly signed off.

Great. I'll resend the series later with changes from the comments
I've received and properly signed-off.

-- 
Rob Shearman

^ permalink raw reply

* Re: [PATCH] rerere: Separate libgit and builtin functions
From: Stephan Beyer @ 2008-07-09 12:00 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Junio C Hamano
In-Reply-To: <alpine.DEB.1.00.0807091355220.5277@eeepc-johanness>

> > This patch moves rerere()-related functions into a newly created 
> > rerere.c file.  Also setup_rerere() is moved into rerere.c to decrease 
> > code duplications for rerere.c and builtin-rerere.c.
> 
> It is not moved to decrease code duplication, but because it is needed by 
> rerere().

Right.

And btw the patch seems to be broken. (At least I can't apply it
here...)   Sorry :( Usually I test applying before sending but this time
lunch was eliciting.

Well, I'm going to fix that.

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply

* Re: [PATCH] rerere: Separate libgit and builtin functions
From: Johannes Schindelin @ 2008-07-09 11:55 UTC (permalink / raw)
  To: Stephan Beyer; +Cc: git, Junio C Hamano
In-Reply-To: <1215598683-9685-1-git-send-email-s-beyer@gmx.net>

Hi,

On Wed, 9 Jul 2008, Stephan Beyer wrote:

> This patch moves rerere()-related functions into a newly created 
> rerere.c file.  Also setup_rerere() is moved into rerere.c to decrease 
> code duplications for rerere.c and builtin-rerere.c.

It is not moved to decrease code duplication, but because it is needed by 
rerere().

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] Fix problem with authentification on http repository.
From: Johannes Schindelin @ 2008-07-09 11:43 UTC (permalink / raw)
  To: Mike Hommey; +Cc: Junio C Hamano, jean.guyader, git, Jean Guyader
In-Reply-To: <20080709085651.GA23453@glandium.org>

Hi,

On Wed, 9 Jul 2008, Mike Hommey wrote:

> Note that http://user:pass@server/path/ /should/ work (but that would 
> need validation), though not a good idea on command line.

Well, now that the programs using URLs are all builtins, we can actually 
do something about it.  We can edit out the "user[:pass]@" part out of 
argv, which usually means that "ps" will not see it anymore.

Ciao,
Dscho

^ permalink raw reply

* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Jakub Narebski @ 2008-07-09 11:36 UTC (permalink / raw)
  To: Sverre Rabbelier; +Cc: git, David Symonds, Shawn O. Pearce
In-Reply-To: <bd6139dc0807090318r4cbcac93s907871c6b45c03ac@mail.gmail.com>

On Wed, 9 July 2008, Sverre Rabbelier wrote:
> On Tue, Jul 8, 2008 at 2:27 AM, Jakub Narebski <jnareb@gmail.com> wrote:
> >
> > I'd very much like to have (or perhaps to wrote) some sort of interim
> > progress report for Google Summer of Code 2008 projects on
> > http://git.or.cz/gitwiki/SoC2008Projects.  Therefore I'd like you to
> > expand and/or correct those mini-summaries below.
> 
> Would you like us to go to the wiki page and edit them ourselves, yes?

Yes, thanks in advance.

> If so, I'll see about kicking up something interesting to put up there
> soon.

I'd rather students or mentors did write short summary on mentioned
wiki page, but I'll try to come up with some short summary for each
GSoC2008 project (if it won't be there already) after July 14.


[thanks for summary]
-- 
Jakub Narebski
Poland

^ permalink raw reply

* Re: [PATCH] better git-submodule status output
From: Sylvain Joyeux @ 2008-07-09 11:01 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: git
In-Reply-To: <48749215.6070307@op5.se>

On Wed, Jul 09, 2008 at 12:25:25PM +0200, Andreas Ericsson wrote:
> Sylvain Joyeux wrote:
>> Here is a (tentative) summary of the whole discussion:
>>
>> * doing fetch in status is EVIL. Therefore, status should only report
>>   whatever information is available.
>> * nonetheless, having a "peek" mode seem to be accepted as a useful
>>   feature not only by me.
>> * changing the output format of git-submodules is not right either,
>>   because it would break existing tools which parses it at the moment.
>>
>> Proposal
>> - remove fetch from status, and make the new output enabled when
>>   --verbose is set (can also be set in the config file I guess).
>>
>>   On the symbols side, I propose:
>>     > submodule commit is a direct descendant of the commit in the
>>       superproject
>>     < submodule commit is a direct ancestor of the commit in the
>>       superproject
>>     + no direct relation between submodule commit and commit in the
>>       superproject
>>     ? the commit in the superproject cannot be found in the submodule
>>       (replaces the initial '!' in my patch)
>>
>>   A 'M' is appended if the submodule has uncommitted changes
>>
>> - define a git-submodule 'fetch' subcommand which call fetch in each
>>   submodule and runs the verbose mode of git-status (can be disabled by
>>   a --quiet option).
>>
>> Comments ? (I'm sure there are some ...)
>>
>
> Re-use the existing symbolism from fetch status output. Using '+' to
> denote "no direct relation" in a tool that shows patches half the
> time is just plain horrible.
Could you be more specific ? AFAIK, 'fetch' symbolism is based on files,
the one I need is comparing commits.

And I don't like the '+' either. I chose it because it is the current
symbol to show that there is a difference between
superproject/submodule.
-- 
=======================================================================
Dr. Ing. Sylvain Joyeux                          sylvain.joyeux@dfki.de
Researcher
DFKI Robotik Lab -- Bremen                   http://www.dfki.de/robotik
Tel: 0049 421 218 64136
=======================================================================

^ permalink raw reply

* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Miklos Vajna @ 2008-07-09 10:56 UTC (permalink / raw)
  To: sverre
  Cc: Jakub Narebski, git, Sam Vilain, Joshua Roys, David Symonds,
	Lea Wiemann, John Hawley, Marek Zawirski, Shawn O. Pearce,
	Johannes Schindelin, Stephan Beyer, Christian Couder,
	Daniel Barkalow
In-Reply-To: <bd6139dc0807090318r4cbcac93s907871c6b45c03ac@mail.gmail.com>

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

On Wed, Jul 09, 2008 at 12:18:41PM +0200, Sverre Rabbelier <alturin@gmail.com> wrote:
> I am not yet sure how I should send in my code for peer review.
> $ git rev-list master | wc -l
> 151

What I did squashing fixes regularly, but I keep a 'history' branch
where I always reference to the old HEAD, so others can still have a
look at the individual commits.

It's like:

$ cat rebase-builtin-merge.sh
#!/bin/sh -e

[ -n "$*" ]

old_head=$(git rev-parse HEAD)
git rebase $*
git update-ref refs/heads/rebase-history \
        $(echo "Rebased with 'git rebase $*'" | \
        git commit-tree HEAD^{tree} -p rebase-history -p $old_head -p HEAD)

It will not work properly if you get conflicts, but you got the idea.

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

^ permalink raw reply

* Re: [PATCH] better git-submodule status output
From: Andreas Ericsson @ 2008-07-09 10:25 UTC (permalink / raw)
  To: Sylvain Joyeux; +Cc: git
In-Reply-To: <20080709101330.GA3525@joyeux>

Sylvain Joyeux wrote:
> Here is a (tentative) summary of the whole discussion:
> 
> * doing fetch in status is EVIL. Therefore, status should only report
>   whatever information is available.
> * nonetheless, having a "peek" mode seem to be accepted as a useful
>   feature not only by me.
> * changing the output format of git-submodules is not right either,
>   because it would break existing tools which parses it at the moment.
> 
> Proposal
> - remove fetch from status, and make the new output enabled when
>   --verbose is set (can also be set in the config file I guess).
> 
>   On the symbols side, I propose:
>     > submodule commit is a direct descendant of the commit in the
>       superproject
>     < submodule commit is a direct ancestor of the commit in the
>       superproject
>     + no direct relation between submodule commit and commit in the
>       superproject
>     ? the commit in the superproject cannot be found in the submodule
>       (replaces the initial '!' in my patch)
> 
>   A 'M' is appended if the submodule has uncommitted changes
> 
> - define a git-submodule 'fetch' subcommand which call fetch in each
>   submodule and runs the verbose mode of git-status (can be disabled by
>   a --quiet option).
> 
> Comments ? (I'm sure there are some ...)
> 

Re-use the existing symbolism from fetch status output. Using '+' to
denote "no direct relation" in a tool that shows patches half the
time is just plain horrible.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ permalink raw reply

* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Sverre Rabbelier @ 2008-07-09 10:18 UTC (permalink / raw)
  To: Jakub Narebski
  Cc: git, Sam Vilain, Joshua Roys, David Symonds, Lea Wiemann,
	John Hawley, Marek Zawirski, Shawn O. Pearce, Miklos Vajna,
	Johannes Schindelin, Stephan Beyer, Christian Couder,
	Daniel Barkalow
In-Reply-To: <200807080227.43515.jnareb@gmail.com>

[Once again I forgot to "reply to all", sorry Jakub ;)]

Heya,

On Tue, Jul 8, 2008 at 2:27 AM, Jakub Narebski <jnareb@gmail.com> wrote:
> I'd very much like to have (or perhaps to wrote) some sort of interim
> progress report for Google Summer of Code 2008 projects on
> http://git.or.cz/gitwiki/SoC2008Projects.  Therefore I'd like you to
> expand and/or correct those mini-summaries below.

Would you like us to go to the wiki page and edit them ourselves, yes?
If so, I'll see about kicking up something interesting to put up there
soon.

> (It would be, I guess, good preparation for GSoC 2008 mid-term
> evaluations, which according to GSoC 2008 timeline
>  http://code.google.com/opensource/gsoc/2008/faqs.html#0.1_timeline
> are to take place July 7 -- July 14.)

Agreed.

> 2. git-statistics (?)
>
> Student: Sverre Rabbelier
> Mentor: David Symonds
>
> There were some posts about how git-statistics can be used:
>  http://thread.gmane.org/gmane.comp.version-control.git/81534
>  http://thread.gmane.org/gmane.comp.version-control.git/82027
> but it was mainly theoretical thread
>
> The git-stats repository at http://repo.or.cz/w/git-stats.git (gitweb)
> has some commits, but I don't remember any of them on git mailing list.
> Not ready?

I am not yet sure how I should send in my code for peer review.
$ git rev-list master | wc -l
151
A bunch of these patches introduce significant changes, a few are
fixes, and some are only changes to the changelog [0] which is
actually a pretty good way to see what I've been up to. Credit here
goes to David for suggesting I keep one. I am contemplating sending in
one patch per module since I've structured my code in such a way that
each module is mostly a stand-alone file that can be reviewed in
itself. I am not sure if there is any way to get such code reviewed,
most other projects had several distinct steps (e.g., modifying parts
of existing code first) they had to go through before they could write
new code. My project has been "new code" from the get go, so any
advice as to how to send this in for peer review is very welcome.

I have been working on GitStats, an attempt to gather metrics on a git
repository. For those who would like to know more about my goals,
please have a look at [1] or the more general [2]. Currently I have
quite a few metrics done, most of them are aimed at detecting whether
a commit is a bugfix. A short listing of metrics done:
* stats.py author -d: Shows file activity of a specific developer
measured in how often they made a modification to a file and total
lines added/removed (much like a diffstat, but now for a specific
developer instead of one commit).
* stats.py author -f: Shows file activity of a specific file meaured
in how often they made a modification to a file, could be extended to
also count changes like "author -d"
* stats.py branch -b: Shows which branch a specific file belongs to,
for more information on this metric see below
* stats.py commit -v: Shows all commits that are reverted by the
specified commit, will be extended to allow detection of partial
reverts
* stats.py diff -e: Shows whether two specific commits introduce the
same changes
* stats.py diff -e -n: ditto, but ignores what changes were made, only
looks at the changed lines
* stats.py diff -e -i: ditto, but inverts the comparison, instead of
comparing addition with addition and deletions with deletions, the
additions of the first diff are compared with the deletions of the
second diff, and vise versa. This way a revert is easily detected.
* stats.py index -t: Shows which commits touched the same paths as the
staged changes

I am working now on getting the 'is a bugfix' metric going, it's
sub-metrics are mostly done ('branch contains' , 'is revert', 'commit
msg matches', 'commit diff matches') with the exception of a few
simple ones ('is partial revert', 'modifies same lines') that are
already almost done. As a sneak peek into what I've been up to I'll
zoom in a bit on the development of the 'belongs to' metric.

One of the hardest things to tackle was the 'belongs to'
metric. It's goal is to determine how much a certain commit
'belongs to' a specific branch. When aggregating this
metric one can pick the best fit. This is useful when
trying to figure out if a commit was made on a maintenance
branch, and as such whether it should be marked as a 'fix'.
The definition of "belonging to" a branch was made as follows: "Each
branch that contains the target commit begins with a 'dilution' of 0,
for each merge on the way between it and the commit, increase the
dilution by one if it was not the primary parent of the merge. This
means that if a commit was "merged into" a branch instead of having
been made on that branch, it will have a higher dilution than if the
commit was made on that branch. This way, the lower the dilution, the
more a commit belongs on a branch.

The problem with this metric was not in writing it, but in
getting it to not be terribly slow on large repositories.
A few of the major problems include:
* Do not call git-rev-list on each commit, but instead
 gather all 'parentage' information beforehand. This
 avoids a lot of calls to the git binary, which is
 expensive. This simple optimization resulted in a huge
 speed increase, even on the small tests.
 With this in place it runs in under a second for the test
 repository, but the git.git repo still takes ages.
* Do not revisit commits that have already been checked
 _and had a lower or equal dilution_ in the previous
 check.
 Especially in the git.git repository this
 optimization made the algorithm go from 'unusable' to
 useful.
 With this in place it runs in under a second for recent
 commits. It takes under 20 seconds for the first commit
 in the repository.
* Keep a global memory too, that is, when multiple branches
 contain the commit, remember that we have already visited
 a commit in a global memory as well. When visiting
 a commit we check if we already visited it before but
 with a lower dilution, if so, this means that another
 branch is always going to be better than the current
 branch at this point, as such, we stop walking there.
 With this in place it still runs in under a second for
 recent commits. It takes about 7 seconds for the first
 commit in the repository.
* Do a rev-list on all branches we are interested in once,
 instead of once for each branch. In order to cut down the
 output by not listing 'subsets', the rev-lists of each
 individual branch was needed. By not doing this filtering
 a lot of time is saved.
 With this in place it still runs in under a second for
 recent commits. It takes a little over 5 seconds for the
 first commit in the repository.

The above timings were made on a rather outdated version of git.git,
after updating to today's latest the timings are as follows:

$ time stats.py branch -b e83c516331
Matching branches:
pu
next
master
maint
offcuts

real    0m6.360s
user    0m6.228s
sys     0m0.244s

It is plausible that there is no way to do this any faster
with the current approach. Walking all commits, examining
them, and calculating the dilution, all the way to the root
commit (on the git.git repo) just takes that long. The
algorithm itself is almost instant, most of the time is
spent waiting for git rev-list to return.

For a recent commit on the maint branch we can find the following
information (the -d flag was included to include 'debug' information,
so that we can see what other information was found, but left out in
the regular report):

sverre@Laptop-Sverre:~/code/git$ time stats.py branch -b 2b2828b -d
Retreiving branches that contain the commit...
Gathering information on the history...
Done.

Checking branches now:
origin/next
master
origin/maint
origin/pu
Done.

Listing found metrics:
Branch next, dilution: 2.
Branch master, dilution: 1.
Branch maint, dilution: 0.
Done.

Matching branches:
The minimal dilution is: 0
maint

real    0m6.431s
user    0m6.164s
sys     0m0.260s

>From the above one can see that master has recently merged in that
commit, but that next did not include it until after it merged in
master. In this way a commit can cascade through multiple merge, earch
merge increasing it's dilution by one.

I am very interested to hear comments on my progress so far, but also
on what is thought to be "important to work on next". It would be
awesome if a few people could give it a test drive. I recommend using
the setupRepo.py script in 'src/scripts' which will create a
metricsrepo in /tmp/ that is very well suited to experiment with the
'belongs to' metric. The testrepo also created in /tmp is better
suited to test some of the other metrics. Both repositories are used
by the testcases under 'src/t', which should all pass :). My
repository can be found at [3].

Thank you for reading, and I'm looking forward to review/comments.

[0] http://repo.or.cz/w/git-stats.git?a=blob;f=doc/changelog.txt
[1] http://alturin.googlepages.com/Use_cases.html
[2] http://alturin.googlepages.com/gsoc2008
[3] http://repo.or.cz/w/git-stats.git

--
Cheers,

Sverre Rabbelier

^ permalink raw reply

* [PATCH] rerere: Separate libgit and builtin functions
From: Stephan Beyer @ 2008-07-09 10:18 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Junio C Hamano, Stephan Beyer
In-Reply-To: <alpine.DEB.1.00.0807090230560.5277@eeepc-johanness>

This patch moves rerere()-related functions into a newly created
rerere.c file.  Also setup_rerere() is moved into rerere.c to decrease
code duplications for rerere.c and builtin-rerere.c.

Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
---

> BTW I think it is a horrible thing that rerere() is declared in commit.h
> (a libgit header), but implemented in builtin-rerere.c, which is not part
> of libgit.a.

Ok, I give it a try.

 Makefile                     |    2 +
 builtin-rerere.c             |  324 +++---------------------------------------
 builtin-rerere.c => rerere.c |  133 +-----------------
 rerere.h                     |    9 ++
 4 files changed, 32 insertions(+), 436 deletions(-)
 copy builtin-rerere.c => rerere.c (66%)
 create mode 100644 rerere.h

diff --git a/Makefile b/Makefile
index bddd1a7..4e23c29 100644
--- a/Makefile
+++ b/Makefile
@@ -363,6 +363,7 @@ LIB_H += quote.h
 LIB_H += reflog-walk.h
 LIB_H += refs.h
 LIB_H += remote.h
+LIB_H += rerere.h
 LIB_H += revision.h
 LIB_H += run-command.h
 LIB_H += sha1-lookup.h
@@ -447,6 +448,7 @@ LIB_OBJS += read-cache.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += remote.o
+LIB_OBJS += rerere.o
 LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
 LIB_OBJS += server-info.o
diff --git a/builtin-rerere.c b/builtin-rerere.c
index 85222d9..669a963 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "cache.h"
 #include "path-list.h"
+#include "rerere.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 
@@ -13,183 +14,11 @@ static const char git_rerere_usage[] =
 static int cutoff_noresolve = 15;
 static int cutoff_resolve = 60;
 
-/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
-static int rerere_enabled = -1;
-
-static char *merge_rr_path;
-
 static const char *rr_path(const char *name, const char *file)
 {
 	return git_path("rr-cache/%s/%s", name, file);
 }
 
-static void read_rr(struct path_list *rr)
-{
-	unsigned char sha1[20];
-	char buf[PATH_MAX];
-	FILE *in = fopen(merge_rr_path, "r");
-	if (!in)
-		return;
-	while (fread(buf, 40, 1, in) == 1) {
-		int i;
-		char *name;
-		if (get_sha1_hex(buf, sha1))
-			die("corrupt MERGE_RR");
-		buf[40] = '\0';
-		name = xstrdup(buf);
-		if (fgetc(in) != '\t')
-			die("corrupt MERGE_RR");
-		for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
-			; /* do nothing */
-		if (i == sizeof(buf))
-			die("filename too long");
-		path_list_insert(buf, rr)->util = name;
-	}
-	fclose(in);
-}
-
-static struct lock_file write_lock;
-
-static int write_rr(struct path_list *rr, int out_fd)
-{
-	int i;
-	for (i = 0; i < rr->nr; i++) {
-		const char *path = rr->items[i].path;
-		int length = strlen(path) + 1;
-		if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
-		    write_in_full(out_fd, "\t", 1) != 1 ||
-		    write_in_full(out_fd, path, length) != length)
-			die("unable to write rerere record");
-	}
-	if (commit_lock_file(&write_lock) != 0)
-		die("unable to write rerere record");
-	return 0;
-}
-
-static int handle_file(const char *path,
-	 unsigned char *sha1, const char *output)
-{
-	SHA_CTX ctx;
-	char buf[1024];
-	int hunk = 0, hunk_no = 0;
-	struct strbuf one, two;
-	FILE *f = fopen(path, "r");
-	FILE *out = NULL;
-
-	if (!f)
-		return error("Could not open %s", path);
-
-	if (output) {
-		out = fopen(output, "w");
-		if (!out) {
-			fclose(f);
-			return error("Could not write %s", output);
-		}
-	}
-
-	if (sha1)
-		SHA1_Init(&ctx);
-
-	strbuf_init(&one, 0);
-	strbuf_init(&two,  0);
-	while (fgets(buf, sizeof(buf), f)) {
-		if (!prefixcmp(buf, "<<<<<<< "))
-			hunk = 1;
-		else if (!prefixcmp(buf, "======="))
-			hunk = 2;
-		else if (!prefixcmp(buf, ">>>>>>> ")) {
-			int cmp = strbuf_cmp(&one, &two);
-
-			hunk_no++;
-			hunk = 0;
-			if (cmp > 0) {
-				strbuf_swap(&one, &two);
-			}
-			if (out) {
-				fputs("<<<<<<<\n", out);
-				fwrite(one.buf, one.len, 1, out);
-				fputs("=======\n", out);
-				fwrite(two.buf, two.len, 1, out);
-				fputs(">>>>>>>\n", out);
-			}
-			if (sha1) {
-				SHA1_Update(&ctx, one.buf ? one.buf : "",
-					    one.len + 1);
-				SHA1_Update(&ctx, two.buf ? two.buf : "",
-					    two.len + 1);
-			}
-			strbuf_reset(&one);
-			strbuf_reset(&two);
-		} else if (hunk == 1)
-			strbuf_addstr(&one, buf);
-		else if (hunk == 2)
-			strbuf_addstr(&two, buf);
-		else if (out)
-			fputs(buf, out);
-	}
-	strbuf_release(&one);
-	strbuf_release(&two);
-
-	fclose(f);
-	if (out)
-		fclose(out);
-	if (sha1)
-		SHA1_Final(sha1, &ctx);
-	return hunk_no;
-}
-
-static int find_conflict(struct path_list *conflict)
-{
-	int i;
-	if (read_cache() < 0)
-		return error("Could not read index");
-	for (i = 0; i+1 < active_nr; i++) {
-		struct cache_entry *e2 = active_cache[i];
-		struct cache_entry *e3 = active_cache[i+1];
-		if (ce_stage(e2) == 2 &&
-		    ce_stage(e3) == 3 &&
-		    ce_same_name(e2, e3) &&
-		    S_ISREG(e2->ce_mode) &&
-		    S_ISREG(e3->ce_mode)) {
-			path_list_insert((const char *)e2->name, conflict);
-			i++; /* skip over both #2 and #3 */
-		}
-	}
-	return 0;
-}
-
-static int merge(const char *name, const char *path)
-{
-	int ret;
-	mmfile_t cur, base, other;
-	mmbuffer_t result = {NULL, 0};
-	xpparam_t xpp = {XDF_NEED_MINIMAL};
-
-	if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
-		return 1;
-
-	if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
-			read_mmfile(&base, rr_path(name, "preimage")) ||
-			read_mmfile(&other, rr_path(name, "postimage")))
-		return 1;
-	ret = xdl_merge(&base, &cur, "", &other, "",
-			&xpp, XDL_MERGE_ZEALOUS, &result);
-	if (!ret) {
-		FILE *f = fopen(path, "w");
-		if (!f)
-			return error("Could not write to %s", path);
-		fwrite(result.ptr, result.size, 1, f);
-		fclose(f);
-	}
-
-	free(cur.ptr);
-	free(base.ptr);
-	free(other.ptr);
-	free(result.ptr);
-
-	return ret;
-}
-
 static void unlink_rr_item(const char *name)
 {
 	unlink(rr_path(name, "thisimage"));
@@ -198,6 +27,17 @@ static void unlink_rr_item(const char *name)
 	rmdir(git_path("rr-cache/%s", name));
 }
 
+static int git_rerere_gc_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "gc.rerereresolved"))
+		cutoff_resolve = git_config_int(var, value);
+	else if (!strcmp(var, "gc.rerereunresolved"))
+		cutoff_noresolve = git_config_int(var, value);
+	else
+		return git_default_config(var, value, cb);
+	return 0;
+}
+
 static void garbage_collect(struct path_list *rr)
 {
 	struct path_list to_remove = { NULL, 0, 0, 1 };
@@ -207,6 +47,7 @@ static void garbage_collect(struct path_list *rr)
 	int len, i, cutoff;
 	time_t now = time(NULL), then;
 
+	git_config(git_rerere_gc_config, NULL);
 	strlcpy(buf, git_path("rr-cache"), sizeof(buf));
 	len = strlen(buf);
 	dir = opendir(buf);
@@ -267,148 +108,19 @@ static int diff_two(const char *file1, const char *label1,
 	return 0;
 }
 
-static int do_plain_rerere(struct path_list *rr, int fd)
-{
-	struct path_list conflict = { NULL, 0, 0, 1 };
-	int i;
-
-	find_conflict(&conflict);
-
-	/*
-	 * MERGE_RR records paths with conflicts immediately after merge
-	 * failed.  Some of the conflicted paths might have been hand resolved
-	 * in the working tree since then, but the initial run would catch all
-	 * and register their preimages.
-	 */
-
-	for (i = 0; i < conflict.nr; i++) {
-		const char *path = conflict.items[i].path;
-		if (!path_list_has_path(rr, path)) {
-			unsigned char sha1[20];
-			char *hex;
-			int ret;
-			ret = handle_file(path, sha1, NULL);
-			if (ret < 1)
-				continue;
-			hex = xstrdup(sha1_to_hex(sha1));
-			path_list_insert(path, rr)->util = hex;
-			if (mkdir(git_path("rr-cache/%s", hex), 0755))
-				continue;;
-			handle_file(path, NULL, rr_path(hex, "preimage"));
-			fprintf(stderr, "Recorded preimage for '%s'\n", path);
-		}
-	}
-
-	/*
-	 * Now some of the paths that had conflicts earlier might have been
-	 * hand resolved.  Others may be similar to a conflict already that
-	 * was resolved before.
-	 */
-
-	for (i = 0; i < rr->nr; i++) {
-		struct stat st;
-		int ret;
-		const char *path = rr->items[i].path;
-		const char *name = (const char *)rr->items[i].util;
-
-		if (!stat(rr_path(name, "preimage"), &st) &&
-				!stat(rr_path(name, "postimage"), &st)) {
-			if (!merge(name, path)) {
-				fprintf(stderr, "Resolved '%s' using "
-						"previous resolution.\n", path);
-				goto tail_optimization;
-			}
-		}
-
-		/* Let's see if we have resolved it. */
-		ret = handle_file(path, NULL, NULL);
-		if (ret)
-			continue;
-
-		fprintf(stderr, "Recorded resolution for '%s'.\n", path);
-		copy_file(rr_path(name, "postimage"), path, 0666);
-tail_optimization:
-		if (i < rr->nr - 1)
-			memmove(rr->items + i,
-				rr->items + i + 1,
-				sizeof(rr->items[0]) * (rr->nr - i - 1));
-		rr->nr--;
-		i--;
-	}
-
-	return write_rr(rr, fd);
-}
-
-static int git_rerere_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "gc.rerereresolved"))
-		cutoff_resolve = git_config_int(var, value);
-	else if (!strcmp(var, "gc.rerereunresolved"))
-		cutoff_noresolve = git_config_int(var, value);
-	else if (!strcmp(var, "rerere.enabled"))
-		rerere_enabled = git_config_bool(var, value);
-	else
-		return git_default_config(var, value, cb);
-	return 0;
-}
-
-static int is_rerere_enabled(void)
-{
-	struct stat st;
-	const char *rr_cache;
-	int rr_cache_exists;
-
-	if (!rerere_enabled)
-		return 0;
-
-	rr_cache = git_path("rr-cache");
-	rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
-	if (rerere_enabled < 0)
-		return rr_cache_exists;
-
-	if (!rr_cache_exists &&
-	    (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
-		die("Could not create directory %s", rr_cache);
-	return 1;
-}
-
-static int setup_rerere(struct path_list *merge_rr)
-{
-	int fd;
-
-	git_config(git_rerere_config, NULL);
-	if (!is_rerere_enabled())
-		return -1;
-
-	merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
-	fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
-	read_rr(merge_rr);
-	return fd;
-}
-
-int rerere(void)
-{
-	struct path_list merge_rr = { NULL, 0, 0, 1 };
-	int fd;
-
-	fd = setup_rerere(&merge_rr);
-	if (fd < 0)
-		return 0;
-	return do_plain_rerere(&merge_rr, fd);
-}
-
 int cmd_rerere(int argc, const char **argv, const char *prefix)
 {
 	struct path_list merge_rr = { NULL, 0, 0, 1 };
 	int i, fd;
 
+	if (argc < 2)
+		return rerere();
+
 	fd = setup_rerere(&merge_rr);
 	if (fd < 0)
 		return 0;
 
-	if (argc < 2)
-		return do_plain_rerere(&merge_rr, fd);
-	else if (!strcmp(argv[1], "clear")) {
+	if (!strcmp(argv[1], "clear")) {
 		for (i = 0; i < merge_rr.nr; i++) {
 			struct stat st;
 			const char *name = (const char *)merge_rr.items[i].util;
@@ -417,7 +129,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
 					stat(rr_path(name, "postimage"), &st))
 				unlink_rr_item(name);
 		}
-		unlink(merge_rr_path);
+		unlink(git_path("rr-cache/MERGE_RR"));
 	} else if (!strcmp(argv[1], "gc"))
 		garbage_collect(&merge_rr);
 	else if (!strcmp(argv[1], "status"))
diff --git a/builtin-rerere.c b/rerere.c
similarity index 66%
copy from builtin-rerere.c
copy to rerere.c
index 85222d9..5c22bed 100644
--- a/builtin-rerere.c
+++ b/rerere.c
@@ -1,18 +1,11 @@
-#include "builtin.h"
 #include "cache.h"
 #include "path-list.h"
+#include "rerere.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 
 #include <time.h>
 
-static const char git_rerere_usage[] =
-"git-rerere [clear | status | diff | gc]";
-
-/* these values are days */
-static int cutoff_noresolve = 15;
-static int cutoff_resolve = 60;
-
 /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
 static int rerere_enabled = -1;
 
@@ -190,83 +183,6 @@ static int merge(const char *name, const char *path)
 	return ret;
 }
 
-static void unlink_rr_item(const char *name)
-{
-	unlink(rr_path(name, "thisimage"));
-	unlink(rr_path(name, "preimage"));
-	unlink(rr_path(name, "postimage"));
-	rmdir(git_path("rr-cache/%s", name));
-}
-
-static void garbage_collect(struct path_list *rr)
-{
-	struct path_list to_remove = { NULL, 0, 0, 1 };
-	char buf[1024];
-	DIR *dir;
-	struct dirent *e;
-	int len, i, cutoff;
-	time_t now = time(NULL), then;
-
-	strlcpy(buf, git_path("rr-cache"), sizeof(buf));
-	len = strlen(buf);
-	dir = opendir(buf);
-	strcpy(buf + len++, "/");
-	while ((e = readdir(dir))) {
-		const char *name = e->d_name;
-		struct stat st;
-		if (name[0] == '.' && (name[1] == '\0' ||
-					(name[1] == '.' && name[2] == '\0')))
-			continue;
-		i = snprintf(buf + len, sizeof(buf) - len, "%s", name);
-		strlcpy(buf + len + i, "/preimage", sizeof(buf) - len - i);
-		if (stat(buf, &st))
-			continue;
-		then = st.st_mtime;
-		strlcpy(buf + len + i, "/postimage", sizeof(buf) - len - i);
-		cutoff = stat(buf, &st) ? cutoff_noresolve : cutoff_resolve;
-		if (then < now - cutoff * 86400) {
-			buf[len + i] = '\0';
-			path_list_insert(xstrdup(name), &to_remove);
-		}
-	}
-	for (i = 0; i < to_remove.nr; i++)
-		unlink_rr_item(to_remove.items[i].path);
-	path_list_clear(&to_remove, 0);
-}
-
-static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
-{
-	int i;
-	for (i = 0; i < nbuf; i++)
-		if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
-			return -1;
-	return 0;
-}
-
-static int diff_two(const char *file1, const char *label1,
-		const char *file2, const char *label2)
-{
-	xpparam_t xpp;
-	xdemitconf_t xecfg;
-	xdemitcb_t ecb;
-	mmfile_t minus, plus;
-
-	if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
-		return 1;
-
-	printf("--- a/%s\n+++ b/%s\n", label1, label2);
-	fflush(stdout);
-	xpp.flags = XDF_NEED_MINIMAL;
-	memset(&xecfg, 0, sizeof(xecfg));
-	xecfg.ctxlen = 3;
-	ecb.outf = outf;
-	xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
-
-	free(minus.ptr);
-	free(plus.ptr);
-	return 0;
-}
-
 static int do_plain_rerere(struct path_list *rr, int fd)
 {
 	struct path_list conflict = { NULL, 0, 0, 1 };
@@ -341,11 +257,7 @@ tail_optimization:
 
 static int git_rerere_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "gc.rerereresolved"))
-		cutoff_resolve = git_config_int(var, value);
-	else if (!strcmp(var, "gc.rerereunresolved"))
-		cutoff_noresolve = git_config_int(var, value);
-	else if (!strcmp(var, "rerere.enabled"))
+	if (!strcmp(var, "rerere.enabled"))
 		rerere_enabled = git_config_bool(var, value);
 	else
 		return git_default_config(var, value, cb);
@@ -372,7 +284,7 @@ static int is_rerere_enabled(void)
 	return 1;
 }
 
-static int setup_rerere(struct path_list *merge_rr)
+int setup_rerere(struct path_list *merge_rr)
 {
 	int fd;
 
@@ -396,42 +308,3 @@ int rerere(void)
 		return 0;
 	return do_plain_rerere(&merge_rr, fd);
 }
-
-int cmd_rerere(int argc, const char **argv, const char *prefix)
-{
-	struct path_list merge_rr = { NULL, 0, 0, 1 };
-	int i, fd;
-
-	fd = setup_rerere(&merge_rr);
-	if (fd < 0)
-		return 0;
-
-	if (argc < 2)
-		return do_plain_rerere(&merge_rr, fd);
-	else if (!strcmp(argv[1], "clear")) {
-		for (i = 0; i < merge_rr.nr; i++) {
-			struct stat st;
-			const char *name = (const char *)merge_rr.items[i].util;
-			if (!stat(git_path("rr-cache/%s", name), &st) &&
-					S_ISDIR(st.st_mode) &&
-					stat(rr_path(name, "postimage"), &st))
-				unlink_rr_item(name);
-		}
-		unlink(merge_rr_path);
-	} else if (!strcmp(argv[1], "gc"))
-		garbage_collect(&merge_rr);
-	else if (!strcmp(argv[1], "status"))
-		for (i = 0; i < merge_rr.nr; i++)
-			printf("%s\n", merge_rr.items[i].path);
-	else if (!strcmp(argv[1], "diff"))
-		for (i = 0; i < merge_rr.nr; i++) {
-			const char *path = merge_rr.items[i].path;
-			const char *name = (const char *)merge_rr.items[i].util;
-			diff_two(rr_path(name, "preimage"), path, path, path);
-		}
-	else
-		usage(git_rerere_usage);
-
-	path_list_clear(&merge_rr, 1);
-	return 0;
-}
diff --git a/rerere.h b/rerere.h
new file mode 100644
index 0000000..35b0fa8
--- /dev/null
+++ b/rerere.h
@@ -0,0 +1,9 @@
+#ifndef RERERE_H
+#define RERERE_H
+
+#include "path-list.h"
+
+extern int setup_rerere(struct path_list *);
+extern int rerere(void);
+
+#endif
-- 
1.5.6.2.574.gd4568

^ permalink raw reply related

* Re: [PATCH] better git-submodule status output
From: Sylvain Joyeux @ 2008-07-09 10:13 UTC (permalink / raw)
  To: git
In-Reply-To: <20080701150119.GE5852@joyeux>

Here is a (tentative) summary of the whole discussion:

* doing fetch in status is EVIL. Therefore, status should only report
  whatever information is available.
* nonetheless, having a "peek" mode seem to be accepted as a useful
  feature not only by me.
* changing the output format of git-submodules is not right either,
  because it would break existing tools which parses it at the moment.

Proposal
- remove fetch from status, and make the new output enabled when
  --verbose is set (can also be set in the config file I guess).

  On the symbols side, I propose:
    > submodule commit is a direct descendant of the commit in the
      superproject
    < submodule commit is a direct ancestor of the commit in the
      superproject
    + no direct relation between submodule commit and commit in the
      superproject
    ? the commit in the superproject cannot be found in the submodule
      (replaces the initial '!' in my patch)

  A 'M' is appended if the submodule has uncommitted changes

- define a git-submodule 'fetch' subcommand which call fetch in each
  submodule and runs the verbose mode of git-status (can be disabled by
  a --quiet option).

Comments ? (I'm sure there are some ...)

Sylvain

^ permalink raw reply

* Re: [PATCH] git-rerere.txt: Mention rr-cache directory
From: Stephan Beyer @ 2008-07-09  9:21 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, git
In-Reply-To: <alpine.DEB.1.00.0807090230560.5277@eeepc-johanness>

Hi,

On Wed, Jul 09, Johannes Schindelin wrote:
> On Wed, 9 Jul 2008, Johannes Schindelin wrote:
> > On Wed, 9 Jul 2008, Stephan Beyer wrote:
> > 
> > > If a user reads the rerere documentation, he or she is not told to 
> > > create the $GIT_DIR/rr-cache directory to be able to use git-rerere. 
> > 
> > Is it?  The config setting is not enough?  Then that is a bug, and should 
> > not be blessed by a bug in the documentation.

I don't know if this is exactly a bug, at least config.txt says:

rerere.enabled::
	Activate recording of resolved conflicts, so that identical
	conflict hunks can be resolved automatically, should they
	be encountered again.  linkgit:git-rerere[1] command is by
	default enabled if you create `rr-cache` directory under
	`$GIT_DIR`, but can be disabled by setting this option to false.

So according to *that* documentation it is right, according to the
git-rerere.txt documentation it was wrong, and according to the
source it is...
> -- snip --
> static int is_rerere_enabled(void)
> {
>         struct stat st;
>         const char *rr_cache;
>         int rr_cache_exists;
> 
>         if (!rerere_enabled)
>                 return 0;
> 
>         rr_cache = git_path("rr-cache");
>         rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
>         if (rerere_enabled < 0)
>                 return rr_cache_exists;
> 
>         if (!rr_cache_exists &&
>             (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
>                 die("Could not create directory %s", rr_cache);
>         return 1;
> }
> -- snap --
> 
> As you can see, in the case rerere_enabled < 0 (i.e. the config did not 
> say anything about rerere), it is assumed enabled _exactly_ when 
> .git/rr_cache/ exists.
> 
> But if it is > 0, the directory is created.

Yes.
There is also an important part in the file:
-- snip --
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
static int rerere_enabled = -1;
-- snap --

So this means it's the following behavior:
 - rerere_enabled < 0: enable, iff rr-cache exists
 - rerere_enabled ==0: disable
 - rerere_enabled > 0: enable and create rr-cache if it does not exist

So this is my fault, sorry :) Both documentations (config.txt and
git-rerere.txt) are right.
It was not explicitly enabled on this one machine which lead me to the
wrong assumption. I should sync my ~/.gitconfig more often ;)

Sorry for that,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply

* Re: [PATCH 2/2] branch --merged/--not-merged: allow specifying arbitrary  commit
From: Junio C Hamano @ 2008-07-09  9:13 UTC (permalink / raw)
  To: Pierre Habouzit; +Cc: git, Lars Hjemli
In-Reply-To: <20080709074516.GA23482@artemis.madism.org>

Pierre Habouzit <madcoder@debian.org> writes:

>   There's a way: declare --merge as (PARSE_OPT_)NONEG to tell parse-opt
> not to generate --no-merge by itself, and declare a --no-merge option as
> well.  I think it works, and if not, we should make parse-opt work with
> that. This is a quite disgusting hack, and no *new* options should be
> written this way, but we have to be flexible enough for backward
> compatibility's sake.

Nice, thanks.

With the obvious s/_FAKELASTARG/_LASTARG_DEFAULT/g to the first one, this
replaces the second one (with documentation updates for a change ;-), and
it seems to work Ok.

-- >8 --
Subject: branch --merged/--no-merged: allow specifying arbitrary commit

"git-branch --merged" is a handy way to list all the branches that have
already been merged to the current branch, but it did not allow checking
against anything but the current branch.  Having to switch branches only
to list the branches that are merged with another branch made the feature
practically useless.

This updates the option parser so that "git branch --merged next" is
accepted when you are on 'master' branch.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/git-branch.txt |   27 ++++++++++++----------
 builtin-branch.c             |   50 +++++++++++++++++++++++++++++++++++------
 2 files changed, 57 insertions(+), 20 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0fd5808..450f903 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -8,24 +8,27 @@ git-branch - List, create, or delete branches
 SYNOPSIS
 --------
 [verse]
-'git-branch' [--color | --no-color] [-r | -a] [--merged | --no-merged]
-	   [-v [--abbrev=<length> | --no-abbrev]]
-	   [--contains <commit>]
+'git-branch' [--color | --no-color] [-r | -a]
+	[-v [--abbrev=<length> | --no-abbrev]]
+	[(--merged | --no-merged | --contains) [<commit>]]
 'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git-branch' (-m | -M) [<oldbranch>] <newbranch>
 'git-branch' (-d | -D) [-r] <branchname>...
 
 DESCRIPTION
 -----------
-With no arguments given a list of existing branches
-will be shown, the current branch will be highlighted with an asterisk.
-Option `-r` causes the remote-tracking branches to be listed,
-and option `-a` shows both.
-With `--contains <commit>`, shows only the branches that
-contains the named commit (in other words, the branches whose
-tip commits are descendant of the named commit).
-With `--merged`, only branches merged into HEAD will be listed, and
-with `--no-merged` only branches not merged into HEAD will be listed.
+
+With no arguments, existing branches are listed, the current branch will
+be highlighted with an asterisk.  Option `-r` causes the remote-tracking
+branches to be listed, and option `-a` shows both.
+
+With `--contains`, shows only the branches that contains the named commit
+(in other words, the branches whose tip commits are descendant of the
+named commit).  With `--merged`, only branches merged into the named
+commit (i.e. the branches whose tip commits are reachable from the named
+commit) will be listed.  With `--no-merged` only branches not merged into
+the named commit will be listed.  Missing <commit> argument defaults to
+'HEAD' (i.e. the tip of the current branch).
 
 In its second form, a new branch named <branchname> will be created.
 It will start out with a head equal to the one given as <start-point>.
diff --git a/builtin-branch.c b/builtin-branch.c
index 50cbc9c..1926c47 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -46,7 +46,12 @@ enum color_branch {
 	COLOR_BRANCH_CURRENT = 4,
 };
 
-static int mergefilter = -1;
+static enum merge_filter {
+	NO_FILTER = 0,
+	SHOW_NOT_MERGED,
+	SHOW_MERGED,
+} merge_filter;
+static unsigned char merge_filter_ref[20];
 
 static int parse_branch_color_slot(const char *var, int ofs)
 {
@@ -234,13 +239,15 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
 	if ((kind & ref_list->kinds) == 0)
 		return 0;
 
-	if (mergefilter > -1) {
+	if (merge_filter != NO_FILTER) {
 		branch.item = lookup_commit_reference_gently(sha1, 1);
 		if (!branch.item)
 			die("Unable to lookup tip of branch %s", refname);
-		if (mergefilter == 0 && has_commit(head_sha1, &branch))
+		if (merge_filter == SHOW_NOT_MERGED &&
+		    has_commit(merge_filter_ref, &branch))
 			return 0;
-		if (mergefilter == 1 && !has_commit(head_sha1, &branch))
+		if (merge_filter == SHOW_MERGED &&
+		    !has_commit(merge_filter_ref, &branch))
 			return 0;
 	}
 
@@ -421,6 +428,20 @@ static int opt_parse_with_commit(const struct option *opt, const char *arg, int
 	return 0;
 }
 
+static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
+{
+	merge_filter = ((opt->long_name[0] == 'n')
+			? SHOW_NOT_MERGED
+			: SHOW_MERGED);
+	if (unset)
+		merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */
+	if (!arg)
+		arg = "HEAD";
+	if (get_sha1(arg, merge_filter_ref))
+		die("malformed object name %s", arg);
+	return 0;
+}
+
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
 	int delete = 0, rename = 0, force_create = 0;
@@ -461,7 +482,18 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
 		OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
 		OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
-		OPT_SET_INT(0, "merged", &mergefilter, "list only merged branches", 1),
+		{
+			OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
+			"commit", "print only not merged branches",
+			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
+			opt_parse_merge_filter, (intptr_t) "HEAD",
+		},
+		{
+			OPTION_CALLBACK, 0, "merged", &merge_filter_ref,
+			"commit", "print only merged branches",
+			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
+			opt_parse_merge_filter, (intptr_t) "HEAD",
+		},
 		OPT_END(),
 	};
 
@@ -471,9 +503,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		branch_use_color = git_use_color_default;
 
 	track = git_branch_track;
-	argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
-	if (!!delete + !!rename + !!force_create > 1)
-		usage_with_options(builtin_branch_usage, options);
 
 	head = resolve_ref("HEAD", head_sha1, 0, NULL);
 	if (!head)
@@ -486,6 +515,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			die("HEAD not found below refs/heads!");
 		head += 11;
 	}
+	hashcpy(merge_filter_ref, head_sha1);
+
+	argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
+	if (!!delete + !!rename + !!force_create > 1)
+		usage_with_options(builtin_branch_usage, options);
 
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
-- 
1.5.6.2.276.gbb293

^ permalink raw reply related

* Re: [PATCH] Fix problem with authentification on http repository.
From: Mike Hommey @ 2008-07-09  8:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, jean.guyader, git, Jean Guyader
In-Reply-To: <7vhcazlbch.fsf@gitster.siamese.dyndns.org>

On Wed, Jul 09, 2008 at 01:18:22AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> Mike Hommey <mh@glandium.org> writes:
> 
> > On Tue, Jul 08, 2008 at 11:28:15PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> > ...
> >> >> Actually Documentation/howto/setup-git-server-over-http.txt
> >> >> talks about http://user@server/path/ format.  How well does this
> >> >> work in practice?  If it does, we should update Documentation/urls.txt
> >> >> to allow optional user@ there like...
> >> >
> >> > AFAIK, there is nothing to deal with asking a password to the user in git
> >> > in this case, so that doesn't work.
> >> 
> >> Then perhaps the howto needs updating?
> >
> > Actually, the howto tells to setup a ~/.netrc file, which should make
> > the http://user@server/path/ format work. I don't know if dropping user@
> > would still work, that depends how libcurl works with it.
> 
> Ok, so the documentation update I suggested earlier may actually be
> valid, right?

The problem is that these urls won't work out of the box if the user
doesn't setup a .netrc file. I'd say it would be better not to add these
forms in Documentation/urls.txt until git prompts for the password
when it is missing.

Note that http://user:pass@server/path/ /should/ work (but that would
need validation), though not a good idea on command line.

Mike

^ permalink raw reply


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