git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* Broken directory pathname pruning..
@ 2005-05-27  0:41 Linus Torvalds
  2005-05-27  0:42 ` Junio C Hamano
                   ` (2 more replies)
  0 siblings, 3 replies; 45+ messages in thread
From: Linus Torvalds @ 2005-05-27  0:41 UTC (permalink / raw)
  To: Junio C Hamano, Git Mailing List


The directory-based pathname pruning doesn't work any more.

I used to just say

	git-whatchanged -v -p drivers/usb/

and I just noticed that it doesn't work any more.

It _does_ work if I leave the final '/' off the thing.

diff-tree itself does this right: that's shown by the fact that with the
slash in place, we still do get the right _changelog_ that is restricted
to drivers/usb changes, but the diffs themselves are missing.

So it seems to be purely "diffcore_pathspec()" that is broken.

Btw, I don't think we should call "diffcore_pathspec()" at all in
diff-tree, because diff-tree already handles "interesting" paths correctly
(and has to do so for performance reasons, since it's not acceptable to
expand all the trees and diff them).

So in the "git-whatchanged"  case the fix is as simple as just removing
those two lines, but the bug then continues to exist in the other *diff* 
programs..

Junio?

		Linus

diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -268,8 +268,6 @@ static int call_diff_flush(void)
 		diff_flush(DIFF_FORMAT_NO_OUTPUT, 0);
 		return 0;
 	}
-	if (nr_paths)
-		diffcore_pathspec(paths);
 	if (header) {
 		if (diff_output_format == DIFF_FORMAT_MACHINE) {
 			const char *ep, *cp;


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: Broken directory pathname pruning..
  2005-05-27  0:41 Broken directory pathname pruning Linus Torvalds
@ 2005-05-27  0:42 ` Junio C Hamano
  2005-05-27  0:49 ` [PATCH] allow pathspec to end with a slash Junio C Hamano
  2005-05-27  6:41 ` [PATCH] Diff updates, fixing pathspec and rename/copy interaction Junio C Hamano
  2 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27  0:42 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Agreed in both counts.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] allow pathspec to end with a slash
  2005-05-27  0:41 Broken directory pathname pruning Linus Torvalds
  2005-05-27  0:42 ` Junio C Hamano
@ 2005-05-27  0:49 ` Junio C Hamano
  2005-05-27  0:52   ` [PATCH] allow pathspec to end with a slash (take #2) Junio C Hamano
  2005-05-27  6:41 ` [PATCH] Diff updates, fixing pathspec and rename/copy interaction Junio C Hamano
  2 siblings, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27  0:49 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Recent rewrite broke "git-whatchanged -v -p drivers/usb/" but 
"git-whatchanged -v -p drivers/usb" still works.  Just strip out
the trailing slashes internally to make it work again.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
# - linus: [PATCH] Make ls-* output consistent with diff-* output format.
# + (working tree)
diff --git a/diffcore-pathspec.c b/diffcore-pathspec.c
--- a/diffcore-pathspec.c
+++ b/diffcore-pathspec.c
@@ -45,8 +45,12 @@ void diffcore_pathspec(const char **path
 	speccnt = i;
 	spec = xmalloc(sizeof(*spec) * speccnt);
 	for (i = 0; pathspec[i]; i++) {
+		int l;
 		spec[i].spec = pathspec[i];
-		spec[i].len = strlen(pathspec[i]);
+		l = strlen(pathspec[i]);
+		while (pathspec[i][l-1] == '/')
+			l--;
+		spec[i].len = l;
 	}
 
 	for (i = 0; i < q->nr; i++) {

Compilation finished at Thu May 26 17:46:56


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] allow pathspec to end with a slash (take #2)
  2005-05-27  0:49 ` [PATCH] allow pathspec to end with a slash Junio C Hamano
@ 2005-05-27  0:52   ` Junio C Hamano
  0 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27  0:52 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Oops, wrong patch; I am an idiot.

------------
Recent rewrite broke "git-whatchanged -v -p drivers/usb/" but 
"git-whatchanged -v -p drivers/usb" still works.  Just strip out
the trailing slashes internally to make it work again.

It uses compare-thing-with-number comparison order instead of
visual comparison order ;-).

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
cd /opt/packrat/playpen/public/in-place/git/git.junio/
jit-diff 1: diffcore-pathspec.c
# - linus: [PATCH] Make ls-* output consistent with diff-* output format.
# + (working tree)
diff --git a/diffcore-pathspec.c b/diffcore-pathspec.c
--- a/diffcore-pathspec.c
+++ b/diffcore-pathspec.c
@@ -45,8 +45,12 @@ void diffcore_pathspec(const char **path
 	speccnt = i;
 	spec = xmalloc(sizeof(*spec) * speccnt);
 	for (i = 0; pathspec[i]; i++) {
+		int l;
 		spec[i].spec = pathspec[i];
-		spec[i].len = strlen(pathspec[i]);
+		l = strlen(pathspec[i]);
+		while (l > 0 && pathspec[i][l-1] == '/')
+			l--;
+		spec[i].len = l;
 	}
 
 	for (i = 0; i < q->nr; i++) {

Compilation finished at Thu May 26 17:50:40


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Diff updates, fixing pathspec and rename/copy interaction.
  2005-05-27  0:41 Broken directory pathname pruning Linus Torvalds
  2005-05-27  0:42 ` Junio C Hamano
  2005-05-27  0:49 ` [PATCH] allow pathspec to end with a slash Junio C Hamano
@ 2005-05-27  6:41 ` Junio C Hamano
  2005-05-27 15:56   ` Linus Torvalds
  2 siblings, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27  6:41 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

During the mailing list discussion about diff-tree omitting to
call diffcore-pathspec, I realized that the current rename/copy
differentiator has a major flaw interacting with pathspec (or
any other filepair filters, including pickaxe).

The problem was that in order to tell if the rename-copy source
still remains in the resulting tree (that is what determines if
one of the rename-copy can become a rename or everybody needs to
be a copy), diffcore-rename was sending a filepair that records
unmodified source downstream, expecting that it reaches
resolve_rename_copy() which happens as the final stage before
actual output happens.  Of course, pathspec and pickaxe can
interfere and happily remove that entry, in which case what
should be shown as a copy suddenly becomes a rename.

The problem was fixed by changing the way diffcore-rename
records whether the source file remains in the resulting tree.

This patch also introduces an optimization for diff-tree -M and
diff-tree -C to throw away implausible rename/copy source
candidate by keeping the filesize information of already seen
SHA1 object IDs in a cache.  This cache is activated only when
diff-tree is reading from --stdin (i.e. processing sequence of
tree pairs) and when rename detection is used.

Pickaxe acquired another option, --pickaxe-all, to diff-* three
brothers.  When a search string is touched, instead of showing
only the changed file that touches that searched string, it
shows the entire changeset.  This is useful to see the change in
context.

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

diff-cache.c             |   13 ++
diff-files.c             |   11 +-
diff-helper.c            |    9 +-
diff-tree.c              |   16 ++-
diff.c                   |  209 ++++++++++++++++++++++++++++++-----------------
diff.h                   |   18 ++--
diffcore-pathspec.c      |    5 -
diffcore-pickaxe.c       |   79 +++++++++++++----
diffcore-rename.c        |   91 +++++++++-----------
diffcore.h               |   14 +--
git-external-diff-script |    2 
t/t4007-rename-3.sh      |  103 +++++++++++++++++++++++
12 files changed, 392 insertions(+), 178 deletions(-)
new file (100755): t/t4007-rename-3.sh

diff --git a/diff-cache.c b/diff-cache.c
--- a/diff-cache.c
+++ b/diff-cache.c
@@ -5,9 +5,10 @@ static int cached_only = 0;
 static int diff_output_format = DIFF_FORMAT_HUMAN;
 static int match_nonexisting = 0;
 static int detect_rename = 0;
-static int reverse_diff = 0;
+static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
+static int pickaxe_opts = 0;
 
 /* A file entry went away or appeared */
 static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)
@@ -202,13 +203,17 @@ int main(int argc, const char **argv)
 			continue;
 		}
 		if (!strcmp(arg, "-R")) {
-			reverse_diff = 1;
+			diff_setup_opt |= DIFF_SETUP_REVERSE;
 			continue;
 		}
 		if (!strcmp(arg, "-S")) {
 			pickaxe = arg + 2;
 			continue;
 		}
+		if (!strcmp(arg, "--pickaxe-all")) {
+			pickaxe_opts = DIFF_PICKAXE_ALL;
+			continue;
+		}
 		if (!strcmp(arg, "-m")) {
 			match_nonexisting = 1;
 			continue;
@@ -224,7 +229,7 @@ int main(int argc, const char **argv)
 		usage(diff_cache_usage);
 
 	/* The rest is for paths restriction. */
-	diff_setup(reverse_diff);
+	diff_setup(diff_setup_opt);
 
 	mark_merge_entries();
 
@@ -238,7 +243,7 @@ int main(int argc, const char **argv)
 	if (detect_rename)
 		diffcore_rename(detect_rename, diff_score_opt);
 	if (pickaxe)
-		diffcore_pickaxe(pickaxe);
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (pathspec)
 		diffcore_pathspec(pathspec);
 	diff_flush(diff_output_format, 1);
diff --git a/diff-files.c b/diff-files.c
--- a/diff-files.c
+++ b/diff-files.c
@@ -11,9 +11,10 @@ static const char *diff_files_usage =
 
 static int diff_output_format = DIFF_FORMAT_HUMAN;
 static int detect_rename = 0;
-static int reverse_diff = 0;
+static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
+static int pickaxe_opts = 0;
 static int silent = 0;
 
 static void show_unmerge(const char *path)
@@ -51,9 +52,11 @@ int main(int argc, const char **argv)
 		else if (!strcmp(argv[1], "-z"))
 			diff_output_format = DIFF_FORMAT_MACHINE;
 		else if (!strcmp(argv[1], "-R"))
-			reverse_diff = 1;
+			diff_setup_opt |= DIFF_SETUP_REVERSE;
 		else if (!strcmp(argv[1], "-S"))
 			pickaxe = argv[1] + 2;
+		else if (!strcmp(argv[1], "--pickaxe-all"))
+			pickaxe_opts = DIFF_PICKAXE_ALL;
 		else if (!strncmp(argv[1], "-M", 2)) {
 			diff_score_opt = diff_scoreopt_parse(argv[1]);
 			detect_rename = DIFF_DETECT_RENAME;
@@ -75,7 +78,7 @@ int main(int argc, const char **argv)
 		exit(1);
 	}
 
-	diff_setup(reverse_diff);
+	diff_setup(diff_setup_opt);
 
 	for (i = 0; i < entries; i++) {
 		struct stat st;
@@ -116,7 +119,7 @@ int main(int argc, const char **argv)
 	if (detect_rename)
 		diffcore_rename(detect_rename, diff_score_opt);
 	if (pickaxe)
-		diffcore_pickaxe(pickaxe);
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (1 < argc)
 		diffcore_pathspec(argv + 1);
 	diff_flush(diff_output_format, 1);
diff --git a/diff-helper.c b/diff-helper.c
--- a/diff-helper.c
+++ b/diff-helper.c
@@ -4,9 +4,9 @@
 #include "cache.h"
 #include "strbuf.h"
 #include "diff.h"
-#include "diffcore.h" /* just for MAX_SCORE */
 
 static const char *pickaxe = NULL;
+static int pickaxe_opts = 0;
 static int line_termination = '\n';
 static int inter_name_termination = '\t';
 
@@ -24,6 +24,8 @@ int main(int ac, const char **av) {
 		else if (av[1][1] == 'S') {
 			pickaxe = av[1] + 2;
 		}
+		else if (!strcmp(av[1], "--pickaxe-all"))
+			pickaxe_opts = DIFF_PICKAXE_ALL;
 		else
 			usage(diff_helper_usage);
 		ac--; av++;
@@ -78,7 +80,6 @@ int main(int ac, const char **av) {
 			if (status == 'R' || status == 'C') {
 				two_paths = 1;
 				sscanf(cp, "%d", &score);
-				score = score * MAX_SCORE / 100;
 				if (line_termination) {
 					cp = strchr(cp,
 						    inter_name_termination);
@@ -129,14 +130,14 @@ int main(int ac, const char **av) {
 			continue;
 		}
 		if (pickaxe)
-			diffcore_pickaxe(pickaxe);
+			diffcore_pickaxe(pickaxe, pickaxe_opts);
 		if (1 < ac)
 			diffcore_pathspec(av + 1);
 		diff_flush(DIFF_FORMAT_PATCH, 0);
 		printf("%s\n", sb.buf);
 	}
 	if (pickaxe)
-		diffcore_pickaxe(pickaxe);
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (1 < ac)
 		diffcore_pathspec(av + 1);
 	diff_flush(DIFF_FORMAT_PATCH, 0);
diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -10,9 +10,10 @@ static int show_tree_entry_in_recursive 
 static int read_stdin = 0;
 static int diff_output_format = DIFF_FORMAT_HUMAN;
 static int detect_rename = 0;
-static int reverse_diff = 0;
+static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
+static int pickaxe_opts = 0;
 static const char *header = NULL;
 static const char *header_prefix = "";
 
@@ -255,7 +256,7 @@ static int diff_tree_sha1(const unsigned
 
 static void call_diff_setup(void)
 {
-	diff_setup(reverse_diff);
+	diff_setup(diff_setup_opt);
 }
 
 static int call_diff_flush(void)
@@ -263,7 +264,7 @@ static int call_diff_flush(void)
 	if (detect_rename)
 		diffcore_rename(detect_rename, diff_score_opt);
 	if (pickaxe)
-		diffcore_pickaxe(pickaxe);
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (diff_queue_is_empty()) {
 		diff_flush(DIFF_FORMAT_NO_OUTPUT, 0);
 		return 0;
@@ -504,7 +505,7 @@ int main(int argc, const char **argv)
 			continue;
 		}
 		if (!strcmp(arg, "-R")) {
-			reverse_diff = 1;
+			diff_setup_opt |= DIFF_SETUP_REVERSE;
 			continue;
 		}
 		if (!strcmp(arg, "-p")) {
@@ -516,6 +517,10 @@ int main(int argc, const char **argv)
 			pickaxe = arg + 2;
 			continue;
 		}
+		if (!strcmp(arg, "--pickaxe-all")) {
+			pickaxe_opts = DIFF_PICKAXE_ALL;
+			continue;
+		}
 		if (!strncmp(arg, "-M", 2)) {
 			detect_rename = DIFF_DETECT_RENAME;
 			diff_score_opt = diff_scoreopt_parse(arg);
@@ -580,6 +585,9 @@ int main(int argc, const char **argv)
 	if (!read_stdin)
 		return 0;
 
+	if (detect_rename)
+		diff_setup_opt |= (DIFF_SETUP_USE_SIZE_CACHE |
+				   DIFF_SETUP_USE_CACHE);
 	while (fgets(line, sizeof(line), stdin))
 		diff_tree_stdin(line);
 
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -12,6 +12,7 @@ static const char *diff_opts = "-pu";
 static unsigned char null_sha1[20] = { 0, };
 
 static int reverse_diff;
+static int use_size_cache;
 
 static const char *external_diff(void)
 {
@@ -141,7 +142,7 @@ static void builtin_diff(const char *nam
 			printf("new mode %s\n", temp[1].mode);
 		}
 		if (xfrm_msg && xfrm_msg[0])
-			fputs(xfrm_msg, stdout);
+			puts(xfrm_msg);
 
 		if (strncmp(temp[0].mode, temp[1].mode, 3))
 			/* we do not run diff between different kind
@@ -222,12 +223,60 @@ static int work_tree_matches(const char 
 	return 1;
 }
 
+static struct sha1_size_cache {
+	unsigned char sha1[20];
+	unsigned long size;
+} **sha1_size_cache;
+static int sha1_size_cache_nr, sha1_size_cache_alloc;
+
+static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
+						 unsigned long size)
+{
+	int first, last;
+	struct sha1_size_cache *e;
+
+	first = 0;
+	last = sha1_size_cache_nr;
+	while (last > first) {
+		int next = (last + first) >> 1;
+		e = sha1_size_cache[next];
+		int cmp = memcmp(e->sha1, sha1, 20);
+		if (!cmp)
+			return e;
+		if (cmp < 0) {
+			last = next;
+			continue;
+		}
+		first = next+1;
+	}
+	/* not found */
+	if (size == UINT_MAX)
+		return NULL;
+	/* insert to make it at "first" */
+	if (sha1_size_cache_alloc <= sha1_size_cache_nr) {
+		sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc);
+		sha1_size_cache = xrealloc(sha1_size_cache,
+					   sha1_size_cache_alloc *
+					   sizeof(*sha1_size_cache));
+	}
+	sha1_size_cache_nr++;
+	if (first < sha1_size_cache_nr)
+		memmove(sha1_size_cache + first + 1, sha1_size_cache + first,
+			(sha1_size_cache_nr - first - 1) *
+			sizeof(*sha1_size_cache));
+	e = xmalloc(sizeof(struct sha1_size_cache));
+	sha1_size_cache[first] = e;
+	memcpy(e->sha1, sha1, 20);
+	e->size = size;
+	return e;
+}
+
 /*
  * While doing rename detection and pickaxe operation, we may need to
  * grab the data for the blob (or file) for our own in-core comparison.
  * diff_filespec has data and size fields for this purpose.
  */
-int diff_populate_filespec(struct diff_filespec *s)
+int diff_populate_filespec(struct diff_filespec *s, int size_only)
 {
 	int err = 0;
 	if (!DIFF_FILE_VALID(s))
@@ -235,6 +284,9 @@ int diff_populate_filespec(struct diff_f
 	if (S_ISDIR(s->mode))
 		return -1;
 
+	if (!use_size_cache)
+		size_only = 0;
+
 	if (s->data)
 		return err;
 	if (!s->sha1_valid ||
@@ -254,6 +306,8 @@ int diff_populate_filespec(struct diff_f
 		s->size = st.st_size;
 		if (!s->size)
 			goto empty;
+		if (size_only)
+			return 0;
 		if (S_ISLNK(st.st_mode)) {
 			int ret;
 			s->data = xmalloc(s->size);
@@ -273,9 +327,21 @@ int diff_populate_filespec(struct diff_f
 		close(fd);
 	}
 	else {
+		/* We cannot do size only for SHA1 blobs */
 		char type[20];
+		struct sha1_size_cache *e;
+
+		if (size_only) {
+			e = locate_size_cache(s->sha1, UINT_MAX);
+			if (e) {
+				s->size = e->size;
+				return 0;
+			}
+		}
 		s->data = read_sha1_file(s->sha1, type, &s->size);
 		s->should_free = 1;
+		if (s->data && size_only)
+			locate_size_cache(s->sha1, s->size);
 	}
 	return 0;
 }
@@ -361,7 +427,7 @@ static void prepare_temp_file(const char
 		return;
 	}
 	else {
-		if (diff_populate_filespec(one))
+		if (diff_populate_filespec(one, 0))
 			die("cannot read data blob for %s", one->path);
 		prep_temp_blob(temp, one->data, one->size,
 			       one->sha1, one->mode);
@@ -492,9 +558,23 @@ static void run_diff(const char *name,
 		run_external_diff(pgm, name, other, one, two, xfrm_msg);
 }
 
-void diff_setup(int reverse_diff_)
+void diff_setup(int flags)
 {
-	reverse_diff = reverse_diff_;
+	if (flags & DIFF_SETUP_REVERSE)
+		reverse_diff = 1;
+	if (flags & DIFF_SETUP_USE_CACHE) {
+		if (!active_cache)
+			/* read-cache does not die even when it fails
+			 * so it is safe for us to do this here.  Also
+			 * it does not smudge active_cache or active_nr
+			 * when it fails, so we do not have to worry about
+			 * cleaning it up oufselves either.
+			 */
+			read_cache();
+	}
+	if (flags & DIFF_SETUP_USE_SIZE_CACHE)
+		use_size_cache = 1;
+	
 }
 
 struct diff_queue_struct diff_queued_diff;
@@ -517,10 +597,18 @@ struct diff_filepair *diff_queue(struct 
 	dp->one = one;
 	dp->two = two;
 	dp->score = 0;
+	dp->source_stays = 0;
 	diff_q(queue, dp);
 	return dp;
 }
 
+void diff_free_filepair(struct diff_filepair *p)
+{
+	diff_free_filespec_data(p->one);
+	diff_free_filespec_data(p->two);
+	free(p);
+}
+
 static void diff_flush_raw(struct diff_filepair *p,
 			   int line_termination,
 			   int inter_name_termination)
@@ -615,7 +703,7 @@ static void diff_flush_patch(struct diff
 		sprintf(msg_,
 			"similarity index %d%%\n"
 			"copy from %s\n"
-			"copy to %s\n",
+			"copy to %s",
 			(int)(0.5 + p->score * 100.0/MAX_SCORE),
 			p->one->path, p->two->path);
 		msg = msg_;
@@ -624,7 +712,7 @@ static void diff_flush_patch(struct diff
 		sprintf(msg_,
 			"similarity index %d%%\n"
 			"rename old %s\n"
-			"rename new %s\n",
+			"rename new %s",
 			(int)(0.5 + p->score * 100.0/MAX_SCORE),
 			p->one->path, p->two->path);
 		msg = msg_;
@@ -639,28 +727,6 @@ static void diff_flush_patch(struct diff
 		run_diff(name, other, p->one, p->two, msg);
 }
 
-int diff_needs_to_stay(struct diff_queue_struct *q, int i,
-		       struct diff_filespec *it)
-{
-	/* If it will be used in later entry (either stay or used
-	 * as the source of rename/copy), we need to copy, not rename.
-	 */
-	while (i < q->nr) {
-		struct diff_filepair *p = q->queue[i++];
-		if (!DIFF_FILE_VALID(p->two))
-			continue; /* removed is fine */
-		if (strcmp(p->one->path, it->path))
-			continue; /* not relevant */
-
-		/* p has its src set to *it and it is not a delete;
-		 * it will be used for in-place change, rename/copy,
-		 * or just stays there.  We cannot rename it out.
-		 */
-		return 1;
-	}
-	return 0;
-}
-
 int diff_queue_is_empty(void)
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
@@ -689,8 +755,8 @@ void diff_debug_filepair(const struct di
 {
 	diff_debug_filespec(p->one, i, "one");
 	diff_debug_filespec(p->two, i, "two");
-	fprintf(stderr, "score %d, status %c\n",
-		p->score, p->status ? : '?');
+	fprintf(stderr, "score %d, status %c source_stays %d\n",
+		p->score, p->status ? : '?', p->source_stays);
 }
 
 void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
@@ -712,8 +778,6 @@ static void diff_resolve_rename_copy(voi
 	struct diff_filepair *p, *pp;
 	struct diff_queue_struct *q = &diff_queued_diff;
 
-	/* This should not depend on the ordering of things. */
-
 	diff_debug_queue("resolve-rename-copy", q);
 
 	for (i = 0; i < q->nr; i++) {
@@ -721,23 +785,28 @@ static void diff_resolve_rename_copy(voi
 		p->status = 0; /* undecided */
 		if (DIFF_PAIR_UNMERGED(p))
 			p->status = 'U';
-		else if (!DIFF_FILE_VALID((p)->one))
+		else if (!DIFF_FILE_VALID(p->one))
 			p->status = 'N';
-		else if (!DIFF_FILE_VALID((p)->two)) {
-			/* Deletion record should be omitted if there
-			 * are rename/copy entries using this one as
-			 * the source.  Then we can say one of them
-			 * is a rename and the rest are copies.
+		else if (!DIFF_FILE_VALID(p->two)) {
+			/* Deleted entry may have been picked up by
+			 * another rename-copy entry.  So we scan the
+			 * queue and if we find one that uses us as the
+			 * source we do not say delete for this entry.
 			 */
-			p->status = 'D';
 			for (j = 0; j < q->nr; j++) {
 				pp = q->queue[j];
-				if (!strcmp(pp->one->path, p->one->path) &&
-				    strcmp(pp->one->path, pp->two->path)) {
+				if (!strcmp(p->one->path, pp->one->path) &&
+				    pp->score) {
+					/* rename/copy are always valid
+					 * so we do not say DIFF_FILE_VALID()
+					 * on pp->one and pp->two.
+					 */
 					p->status = 'X';
 					break;
 				}
 			}
+			if (!p->status)
+				p->status = 'D';
 		}
 		else if (DIFF_PAIR_TYPE_CHANGED(p))
 			p->status = 'T';
@@ -746,33 +815,24 @@ static void diff_resolve_rename_copy(voi
 		 * whose both sides are valid and of the same type, i.e.
 		 * either in-place edit or rename/copy edit.
 		 */
-		else if (strcmp(p->one->path, p->two->path)) {
-			/* See if there is somebody else anywhere that
-			 * will keep the path (either modified or
-			 * unmodified).  If so, we have to be a copy,
-			 * not a rename.  In addition, if there is
-			 * some other rename or copy that comes later
-			 * than us that uses the same source, we
-			 * have to be a copy, not a rename.
+		else if (p->score) {
+			if (p->source_stays) {
+				p->status = 'C';
+				continue;
+			}
+			/* See if there is some other filepair that
+			 * copies from the same source as us.  If so
+			 * we are a copy.  Otherwise we are a rename.
 			 */
-			for (j = 0; j < q->nr; j++) {
+			for (j = i + 1; j < q->nr; j++) {
 				pp = q->queue[j];
 				if (strcmp(pp->one->path, p->one->path))
-					continue;
-				if (!strcmp(pp->one->path, pp->two->path)) {
-					if (DIFF_FILE_VALID(pp->two)) {
-						/* non-delete */
-						p->status = 'C';
-						break;
-					}
-					continue;
-				}
-				/* pp is a rename/copy ... */
-				if (i < j) {
-					/* ... and comes later than us */
-					p->status = 'C';
-					break;
-				}
+					continue; /* not us */
+				if (!pp->score)
+					continue; /* not a rename/copy */
+				/* pp is a rename/copy from the same source */
+				p->status = 'C';
+				break;
 			}
 			if (!p->status)
 				p->status = 'R';
@@ -781,8 +841,11 @@ static void diff_resolve_rename_copy(voi
 			 p->one->mode != p->two->mode)
 			p->status = 'M';
 		else
-			/* this is a "no-change" entry */
-			p->status = 'X';
+			/* this is a "no-change" entry.
+			 * should not happen anymore.
+			 * p->status = 'X';
+			 */
+			die("internal error in diffcore: unmodified entry remains");
 	}
 	diff_debug_queue("resolve-rename-copy done", q);
 }
@@ -817,12 +880,8 @@ void diff_flush(int diff_output_style, i
 			break;
 		}
 	}
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		diff_free_filespec_data(p->one);
-		diff_free_filespec_data(p->two);
-		free(p);
-	}
+	for (i = 0; i < q->nr; i++)
+		diff_free_filepair(q->queue[i]);
 	free(q->queue);
 	q->queue = NULL;
 	q->nr = q->alloc = 0;
@@ -883,7 +942,7 @@ void diff_helper_input(unsigned old_mode
 	if (new_mode)
 		fill_filespec(two, new_sha1, new_mode);
 	dp = diff_queue(&diff_queued_diff, one, two);
-	dp->score = score;
+	dp->score = score * MAX_SCORE / 100;
 	dp->status = status;
 }
 
diff --git a/diff.h b/diff.h
--- a/diff.h
+++ b/diff.h
@@ -28,22 +28,28 @@ extern void diff_unmerge(const char *pat
 
 extern int diff_scoreopt_parse(const char *opt);
 
-#define DIFF_FORMAT_HUMAN	0
-#define DIFF_FORMAT_MACHINE	1
-#define DIFF_FORMAT_PATCH	2
-#define DIFF_FORMAT_NO_OUTPUT	3
-extern void diff_setup(int reverse);
+#define DIFF_SETUP_REVERSE      	1
+#define DIFF_SETUP_USE_CACHE		2
+#define DIFF_SETUP_USE_SIZE_CACHE	4
+extern void diff_setup(int flags);
 
 #define DIFF_DETECT_RENAME	1
 #define DIFF_DETECT_COPY	2
 
 extern void diffcore_rename(int rename_copy, int minimum_score);
 
-extern void diffcore_pickaxe(const char *needle);
+#define DIFF_PICKAXE_ALL	1
+extern void diffcore_pickaxe(const char *needle, int opts);
+
 extern void diffcore_pathspec(const char **pathspec);
 
 extern int diff_queue_is_empty(void);
 
+#define DIFF_FORMAT_HUMAN	0
+#define DIFF_FORMAT_MACHINE	1
+#define DIFF_FORMAT_PATCH	2
+#define DIFF_FORMAT_NO_OUTPUT	3
+
 extern void diff_flush(int output_style, int resolve_rename_copy);
 
 #endif /* DIFF_H */
diff --git a/diffcore-pathspec.c b/diffcore-pathspec.c
--- a/diffcore-pathspec.c
+++ b/diffcore-pathspec.c
@@ -55,11 +55,10 @@ void diffcore_pathspec(const char **path
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
-		if (matches_pathspec(p->one->path, spec, speccnt) ||
-		    matches_pathspec(p->two->path, spec, speccnt))
+		if (matches_pathspec(p->two->path, spec, speccnt))
 			diff_q(&outq, p);
 		else
-			free(p);
+			diff_free_filepair(p);
 	}
 	free(q->queue);
 	*q = outq;
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -11,7 +11,7 @@ static int contains(struct diff_filespec
 {
 	unsigned long offset, sz;
 	const char *data;
-	if (diff_populate_filespec(one))
+	if (diff_populate_filespec(one, 0))
 		return 0;
 	sz = one->size;
 	data = one->data;
@@ -21,36 +21,73 @@ static int contains(struct diff_filespec
 	return 0;
 }
 
-void diffcore_pickaxe(const char *needle)
+void diffcore_pickaxe(const char *needle, int opts)
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
 	unsigned long len = strlen(needle);
-	int i;
+	int i, has_changes;
 	struct diff_queue_struct outq;
 	outq.queue = NULL;
 	outq.nr = outq.alloc = 0;
 
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		int onum = outq.nr;
-		if (!DIFF_FILE_VALID(p->one)) {
-			if (!DIFF_FILE_VALID(p->two))
-				continue; /* ignore nonsense */
-			/* created */
-			if (contains(p->two, needle, len))
-				diff_q(&outq, p);
+	if (opts & DIFF_PICKAXE_ALL) {
+		/* Showing the whole changeset if needle exists */
+		for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			if (!DIFF_FILE_VALID(p->one)) {
+				if (!DIFF_FILE_VALID(p->two))
+					continue; /* ignore unmerged */
+				/* created */
+				if (contains(p->two, needle, len))
+					has_changes++;
+			}
+			else if (!DIFF_FILE_VALID(p->two)) {
+				if (contains(p->one, needle, len))
+					has_changes++;
+			}
+			else if (!diff_unmodified_pair(p) &&
+				 contains(p->one, needle, len) !=
+				 contains(p->two, needle, len))
+				has_changes++;
 		}
-		else if (!DIFF_FILE_VALID(p->two)) {
-			if (contains(p->one, needle, len))
+		if (has_changes)
+			return; /* not munge the queue */
+
+		/* otherwise we will clear the whole queue
+		 * by copying the empty outq at the end of this
+		 * function, but first clear the current entries
+		 * in the queue.
+		 */
+		for (i = 0; i < q->nr; i++)
+			diff_free_filepair(q->queue[i]);
+	}
+	else 
+		/* Showing only the filepairs that has the needle */
+		for (i = 0; i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			has_changes = 0;
+			if (!DIFF_FILE_VALID(p->one)) {
+				if (!DIFF_FILE_VALID(p->two))
+					; /* ignore unmerged */
+				/* created */
+				else if (contains(p->two, needle, len))
+					has_changes = 1;
+			}
+			else if (!DIFF_FILE_VALID(p->two)) {
+				if (contains(p->one, needle, len))
+					has_changes = 1;
+			}
+			else if (!diff_unmodified_pair(p) &&
+				 contains(p->one, needle, len) !=
+				 contains(p->two, needle, len))
+				has_changes = 1;
+
+			if (has_changes)
 				diff_q(&outq, p);
+			else
+				diff_free_filepair(p);
 		}
-		else if (!diff_unmodified_pair(p) &&
-			 contains(p->one, needle, len) !=
-			 contains(p->two, needle, len))
-			diff_q(&outq, p);
-		if (onum == outq.nr)
-			free(p);
-	}
+
 	free(q->queue);
 	*q = outq;
 	return;
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -52,14 +52,15 @@ static struct diff_rename_dst *locate_re
 	return &(rename_dst[first]);
 }
 
+/* Table of rename/copy src files */
 static struct diff_rename_src {
 	struct diff_filespec *one;
-	unsigned src_used : 1;
+	unsigned src_stays : 1;
 } *rename_src;
 static int rename_src_nr, rename_src_alloc;
 
-static struct diff_rename_src *locate_rename_src(struct diff_filespec *one,
-						 int insert_ok)
+static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
+						   int src_stays)
 {
 	int first, last;
 
@@ -77,9 +78,7 @@ static struct diff_rename_src *locate_re
 		}
 		first = next+1;
 	}
-	/* not found */
-	if (!insert_ok)
-		return NULL;
+
 	/* insert to make it at "first" */
 	if (rename_src_alloc <= rename_src_nr) {
 		rename_src_alloc = alloc_nr(rename_src_alloc);
@@ -91,7 +90,7 @@ static struct diff_rename_src *locate_re
 		memmove(rename_src + first + 1, rename_src + first,
 			(rename_src_nr - first - 1) * sizeof(*rename_src));
 	rename_src[first].one = one;
-	rename_src[first].src_used = 0;
+	rename_src[first].src_stays = src_stays;
 	return &(rename_src[first]);
 }
 
@@ -100,8 +99,11 @@ static int is_exact_match(struct diff_fi
 	if (src->sha1_valid && dst->sha1_valid &&
 	    !memcmp(src->sha1, dst->sha1, 20))
 		return 1;
-	if (diff_populate_filespec(src) || diff_populate_filespec(dst))
-		/* this is an error but will be caught downstream */
+	if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
+		return 0;
+	if (src->size != dst->size)
+		return 0;
+	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
 		return 0;
 	if (src->size == dst->size &&
 	    !memcmp(src->data, dst->data, src->size))
@@ -113,7 +115,6 @@ struct diff_score {
 	int src; /* index in rename_src */
 	int dst; /* index in rename_dst */
 	int score;
-	int rank;
 };
 
 static int estimate_similarity(struct diff_filespec *src,
@@ -127,9 +128,11 @@ static int estimate_similarity(struct di
 	 * dst, and then some edit has been applied to dst.
 	 *
 	 * Compare them and return how similar they are, representing
-	 * the score as an integer between 0 and 10000, except
-	 * where they match exactly it is considered better than anything
-	 * else.
+	 * the score as an integer between 0 and MAX_SCORE.
+	 *
+	 * When there is an exact match, it is considered a better
+	 * match than anything else; the destination does not even
+	 * call into this function in that case.
 	 */
 	void *delta;
 	unsigned long delta_size, base_size;
@@ -149,6 +152,7 @@ static int estimate_similarity(struct di
 	/* We would not consider edits that change the file size so
 	 * drastically.  delta_size must be smaller than
 	 * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).
+	 *
 	 * Note that base_size == 0 case is handled here already
 	 * and the final score computation below would not have a
 	 * divide-by-zero issue.
@@ -156,6 +160,9 @@ static int estimate_similarity(struct di
 	if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
 		return 0;
 
+	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
+		return 0; /* error but caught downstream */
+
 	delta = diff_delta(src->data, src->size,
 			   dst->data, dst->size,
 			   &delta_size);
@@ -163,7 +170,7 @@ static int estimate_similarity(struct di
 	/* A delta that has a lot of literal additions would have
 	 * big delta_size no matter what else it does.
 	 */
-	if (minimum_score < MAX_SCORE * delta_size / base_size)
+	if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
 		return 0;
 
 	/* Estimate the edit size by interpreting delta. */
@@ -200,15 +207,14 @@ static void record_rename_pair(struct di
 	fill_filespec(two, dst->sha1, dst->mode);
 
 	dp = diff_queue(renq, one, two);
-	dp->score = score;
-
-	rename_src[src_index].src_used = 1;
+	dp->score = score ? : 1; /* make sure it is at least 1 */
+	dp->source_stays = rename_src[src_index].src_stays;
 	rename_dst[dst_index].pair = dp;
 }
 
 /*
  * We sort the rename similarity matrix with the score, in descending
- * order (more similar first).
+ * order (the most similar first).
  */
 static int score_compare(const void *a_, const void *b_)
 {
@@ -223,7 +229,7 @@ int diff_scoreopt_parse(const char *opt)
 		return -1; /* that is not a -M nor -C option */
 	diglen = strspn(opt+2, "0123456789");
 	if (diglen == 0 || strlen(opt+2) != diglen)
-		return 0; /* use default */
+		return DEFAULT_MINIMUM_SCORE; /* use default */
 	sscanf(opt+2, "%d", &num);
 	for (i = 0, scale = 1; i < diglen; i++)
 		scale *= 10;
@@ -255,9 +261,9 @@ void diffcore_rename(int detect_rename, 
 			else
 				locate_rename_dst(p->two, 1);
 		else if (!DIFF_FILE_VALID(p->two))
-			locate_rename_src(p->one, 1);
-		else if (1 < detect_rename) /* find copy, too */
-			locate_rename_src(p->one, 1);
+			register_rename_src(p->one, 0);
+		else if (detect_rename == DIFF_DETECT_COPY)
+			register_rename_src(p->one, 1);
 	}
 	if (rename_dst_nr == 0)
 		goto cleanup; /* nothing to do */
@@ -308,7 +314,7 @@ void diffcore_rename(int detect_rename, 
 		if (dst->pair)
 			continue; /* already done, either exact or fuzzy. */
 		if (mx[i].score < minimum_score)
-			break; /* there is not any more diffs applicable. */
+			break; /* there is no more usable pair. */
 		record_rename_pair(&renq, mx[i].dst, mx[i].src, mx[i].score);
 	}
 	free(mx);
@@ -317,28 +323,21 @@ void diffcore_rename(int detect_rename, 
  flush_rest:
 	/* At this point, we have found some renames and copies and they
 	 * are kept in renq.  The original list is still in *q.
-	 *
-	 * Scan the original list and move them into the outq; we will sort
-	 * outq and swap it into the queue supplied to pass that to
-	 * downstream, so we assign the sort keys in this loop.
-	 *
-	 * See comments at the top of record_rename_pair for numbers used
-	 * to assign rename_rank.
 	 */
 	outq.queue = NULL;
 	outq.nr = outq.alloc = 0;
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
-		struct diff_rename_src *src = locate_rename_src(p->one, 0);
 		struct diff_rename_dst *dst = locate_rename_dst(p->two, 0);
 		struct diff_filepair *pair_to_free = NULL;
 
 		if (dst) {
 			/* creation */
 			if (dst->pair) {
-				/* renq has rename/copy already to produce
-				 * this file, so we do not emit the creation
-				 * record in the output.
+				/* renq has rename/copy to produce
+				 * this file already, so we do not
+				 * emit the creation record in the
+				 * output.
 				 */
 				diff_q(&outq, dst->pair);
 				pair_to_free = p;
@@ -350,25 +349,17 @@ void diffcore_rename(int detect_rename, 
 				diff_q(&outq, p);
 		}
 		else if (!diff_unmodified_pair(p))
-			/* all the other cases need to be recorded as is */
+			/* all the usual ones need to be kept */
 			diff_q(&outq, p);
-		else {
-			/* unmodified pair needs to be recorded only if
-			 * it is used as the source of rename/copy
-			 */
-			if (src && src->src_used)
-				diff_q(&outq, p);
-			else
-				pair_to_free = p;
-		}
-		if (pair_to_free) {
-			diff_free_filespec_data(pair_to_free->one);
-			diff_free_filespec_data(pair_to_free->two);
-			free(pair_to_free);
-		}
+		else
+			/* no need to keep unmodified pairs */
+			pair_to_free = p;
+
+		if (pair_to_free)
+			diff_free_filepair(pair_to_free);
 	}
 	diff_debug_queue("done copying original", &outq);
-
+	
 	free(renq.queue);
 	free(q->queue);
 	*q = outq;
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -33,14 +33,17 @@ extern struct diff_filespec *alloc_files
 extern void fill_filespec(struct diff_filespec *, const unsigned char *,
 			  unsigned short);
 
-extern int diff_populate_filespec(struct diff_filespec *);
+extern int diff_populate_filespec(struct diff_filespec *, int);
 extern void diff_free_filespec_data(struct diff_filespec *);
 
 struct diff_filepair {
 	struct diff_filespec *one;
 	struct diff_filespec *two;
-	int score; /* only valid when one and two are different paths */
-	int status; /* M C R N D U (see Documentation/diff-format.txt) */
+	unsigned short int score; /* only valid when one and two are
+				   * different paths
+				   */
+	char source_stays; /* all of R/C are copies */
+	char status; /* M C R N D U (see Documentation/diff-format.txt) */
 };
 #define DIFF_PAIR_UNMERGED(p) \
 	(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
@@ -54,6 +57,8 @@ struct diff_filepair {
 	(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
 	S_ISLNK(mode) ? S_IFLNK : S_IFDIR)
 
+extern void diff_free_filepair(struct diff_filepair *);
+
 extern int diff_unmodified_pair(struct diff_filepair *);
 
 struct diff_queue_struct {
@@ -68,9 +73,6 @@ extern struct diff_filepair *diff_queue(
 					struct diff_filespec *);
 extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);
 
-extern int diff_needs_to_stay(struct diff_queue_struct *, int,
-			      struct diff_filespec *);
-
 #define DIFF_DEBUG 0
 #if DIFF_DEBUG
 void diff_debug_filespec(struct diff_filespec *, int, const char *);
diff --git a/git-external-diff-script b/git-external-diff-script
--- a/git-external-diff-script
+++ b/git-external-diff-script
@@ -59,7 +59,7 @@ then
     echo "new mode $mode2"
     if test "$xfrm_msg" != ""
     then
-	echo -n $xfrm_msg
+	echo "$xfrm_msg"
     fi
 fi
 diff ${GIT_DIFF_OPTS-'-pu'} -L "a/$name1" -L "b/$name2" "$tmp1" "$tmp2"
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
new file mode 100755
--- /dev/null
+++ b/t/t4007-rename-3.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Rename interaction with pathspec.
+
+'
+. ./test-lib.sh
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+sanitize_diff_raw='s/ \('"$_x40"'\) \1 \([CR]\)[0-9]*	/ \1 \1 \2#	/'
+compare_diff_raw () {
+    # When heuristics are improved, the score numbers would change.
+    # Ignore them while comparing.
+    # Also we do not check SHA1 hash generation in this test, which
+    # is a job for t0000-basic.sh
+
+    sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
+    sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+test_expect_success \
+    'prepare reference tree' \
+    'mkdir path0 path1 &&
+     cp ../../COPYING path0/COPYING &&
+     git-update-cache --add path0/COPYING &&
+    tree=$(git-write-tree) &&
+    echo $tree'
+
+test_expect_success \
+    'prepare work tree' \
+    'cp path0/COPYING path1/COPYING &&
+     git-update-cache --add --remove path0/COPYING path1/COPYING'
+
+# In the tree, there is only path0/COPYING.  In the cache, path0 and
+# path1 both have COPYING and the latter is a copy of path0/COPYING.
+# Comparing the full tree with cache should tell us so.
+
+git-diff-cache -C $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100	path0/COPYING	path1/COPYING
+EOF
+
+test_expect_success \
+    'validate the result' \
+    'compare_diff_raw current expected'
+
+# In the tree, there is only path0/COPYING.  In the cache, path0 and
+# path1 both have COPYING and the latter is a copy of path0/COPYING.
+# When we omit output from path0 it should still be able to tell us
+# that path1/COPYING is result from a copy from path0/COPYING, not
+# rename, which would imply path0/COPYING is now gone.
+
+git-diff-cache -C $tree path1 >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100	path0/COPYING	path1/COPYING
+EOF
+
+test_expect_success \
+    'validate the result' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'tweak work tree' \
+    'rm -f path0/COPYING &&
+     git-update-cache --remove path0/COPYING'
+
+# In the tree, there is only path0/COPYING.  In the cache, path0 does
+# not have COPYING anymore and path1 has COPYING which is a copy of
+# path0/COPYING.  Showing the full tree with cache should tell us about
+# the rename.
+
+git-diff-cache -C $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100	path0/COPYING	path1/COPYING
+EOF
+
+test_expect_success \
+    'validate the result' \
+    'compare_diff_raw current expected'
+
+# In the tree, there is only path0/COPYING.  In the cache, path0 does
+# not have COPYING anymore and path1 has COPYING which is a copy of
+# path0/COPYING.  Even if we restrict the output to path1, it still
+# should show us the rename.
+
+git-diff-cache -C $tree path1 >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100	path0/COPYING	path1/COPYING
+EOF
+
+test_expect_success \
+    'validate the result' \
+    'compare_diff_raw current expected'
+
+test_done
\ No newline at end of file
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Diff updates, fixing pathspec and rename/copy interaction.
  2005-05-27  6:41 ` [PATCH] Diff updates, fixing pathspec and rename/copy interaction Junio C Hamano
@ 2005-05-27 15:56   ` Linus Torvalds
  2005-05-27 18:22     ` Junio C Hamano
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
  0 siblings, 2 replies; 45+ messages in thread
From: Linus Torvalds @ 2005-05-27 15:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List



On Thu, 26 May 2005, Junio C Hamano wrote:
>
> During the mailing list discussion about diff-tree omitting to
> call diffcore-pathspec, I realized that the current rename/copy
> differentiator has a major flaw interacting with pathspec (or
> any other filepair filters, including pickaxe).
> 
> The problem was that in order to tell if the rename-copy source
> still remains in the resulting tree (that is what determines if
> one of the rename-copy can become a rename or everybody needs to
> be a copy), diffcore-rename was sending a filepair that records
> unmodified source downstream, expecting that it reaches
> resolve_rename_copy() which happens as the final stage before
> actual output happens.  Of course, pathspec and pickaxe can
> interfere and happily remove that entry, in which case what
> should be shown as a copy suddenly becomes a rename.

Umm.

I would much prefer a _much_ simpler fix at least for the pathname part,
which is to just always require that pathspec handling is done _first_.

Why? Because that's fundamentally how git-diff-tree has to work, and it's
how my mental model has always been: the path limitations are a
first-order filter, and if you give a directory, the end result should
always look exactly as if that directory was a project of its own.

In other words, if you limit yourself to a directory, and there was a
rename that moved a file from outside that directory into it, then it is
not a rename at all, it is a "create". The stuff outside the pathspec
limit simply doesn't exist.

This is fundamentally how git-diff-tree has to work for pathspec to make
any sense at all, and it's also the only usage that makes any sense (ie
when I say "git-whatchanged -p arch/i386 include/asm-i386", I expect that
the patches that show up _only_ concern themselves with what happened in
x86, and there's no cross-pollination with other stuff at all, even if
rename detection is enabled).

That in turn implies that the other pathspec users have to work the same
way, for the thing to be consistent.

Now, I don't know how you want pickaxe to work, and maybe you want that to 
run _after_ rename detection, I dunno. So I suspect you still want to do 
what this patch does, but I really don't want pathspec to be involved in 
this thing..

		Linus

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Diff updates, fixing pathspec and rename/copy interaction.
  2005-05-27 15:56   ` Linus Torvalds
@ 2005-05-27 18:22     ` Junio C Hamano
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
  1 sibling, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 18:22 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

LT> I would much prefer a _much_ simpler fix at least for the pathname part,
LT> which is to just always require that pathspec handling is done _first_.

LT> Why? Because that's fundamentally how git-diff-tree has to work, and it's
LT> how my mental model has always been: the path limitations are a
LT> first-order filter, and if you give a directory, the end result should
LT> always look exactly as if that directory was a project of its own.

Enlightment!

My initial thinking was to do rename/copy first, deliberately
ignoring pathspec (I was thinking about tweaking diff-tree to
break out of its built-in pathspec limit to feed diffcore
_everything_, even things outside of the specified directories
when --detect-copies-harder is used), to cast a wider net so
that it can come up with "the best matches".  But what you just
said made me realize that the definition of "the best matches" I
had in mind was not best at all.  I agree with you 100% that
pathspec should come first before everything else to limit the
world diffcore operates in, not just to limit the set of output
paths.

I still want to do what this patch does for another reason.  It
is so much simpler if I do not have to carry around _some_
unmatched pair after rename/copy.  The earlier representation
was forcing all the downstream diffcore filters to be aware of
what rename/copy did, which was simply _wrong_.  They should not
have to care.

About ordering of pickaxe and rename/copy, I think for most uses
of diffcore filters, not limited to pickaxe, would make more
sense if they come after rename/copy when rename/copy detection
is in effect.  What I really wanted to do (I made a side note
comment in the earlier discussion about this, saying "something
like streams", but have not pursued it further) is to make these
diffcore filters/transformations stackable so that the main
programs can control not just if each of them is used or not,
but the order of application of the used ones.  I cannot offhand
think of a good use case of having pickaxe come _before_
rename/copy, but there may be a case the user want to have
things in that order, and having the application order not
hardcoded in three diff-* brothers is a major problem if we
wanted to give that option.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 00/12] Diff updates
  2005-05-27 15:56   ` Linus Torvalds
  2005-05-27 18:22     ` Junio C Hamano
@ 2005-05-27 22:43     ` Junio C Hamano
  2005-05-27 22:49       ` [PATCH 01/12] Fix math thinko in similarity estimator Junio C Hamano
                         ` (12 more replies)
  1 sibling, 13 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:43 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

This series consists of the following 12 patches.  Most of them
are bugfixes and cleanups.  The last one is somewhat iffy,
although it does not break things, and lies somewhere between a
request for inclusion and a request for comments.

    [PATCH 01/12] Fix math thinko in similarity estimator.
    [PATCH 02/12] Introduce diff_free_filepair() funcion.
    [PATCH 03/12] Make pathspec only care about the detination tree.
    [PATCH 04/12] Remove unused rank field from diff_core structure.
    [PATCH 05/12] Do not expose internal scaling to diff-helper.
    [PATCH 06/12] Remove final newline from the value of xfrm_msg variable.
    [PATCH 07/12] Clean up diff_setup() to make it more extensible.
    [PATCH 08/12] Remove a function not used anymore.
    [PATCH 09/12] Add --pickaxe-all to diff-* brothers.
    [PATCH 10/12] Fix the way diffcore-rename records unremoved source.
    [PATCH 11/12] Move pathspec to the beginning of the diffcore chain.
    [PATCH 12/12] Optimize diff-tree -[CM] --stdin



^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 01/12] Fix math thinko in similarity estimator.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
@ 2005-05-27 22:49       ` Junio C Hamano
  2005-05-27 22:50       ` [PATCH 02/12] Introduce diff_free_filepair() funcion Junio C Hamano
                         ` (11 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:49 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

The math to reject delta that is too big was confused.

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

diffcore-rename.c |    2 +-
1 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -163,7 +163,7 @@ static int estimate_similarity(struct di
 	/* A delta that has a lot of literal additions would have
 	 * big delta_size no matter what else it does.
 	 */
-	if (minimum_score < MAX_SCORE * delta_size / base_size)
+	if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
 		return 0;
 
 	/* Estimate the edit size by interpreting delta. */
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 02/12] Introduce diff_free_filepair() funcion.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
  2005-05-27 22:49       ` [PATCH 01/12] Fix math thinko in similarity estimator Junio C Hamano
@ 2005-05-27 22:50       ` Junio C Hamano
  2005-05-27 22:51       ` [PATCH 03/12] Make pathspec only care about the detination tree Junio C Hamano
                         ` (10 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:50 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

This introduces a new function to free a common data structure,
and plugs some leaks.

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

diff.c              |   15 +++++++++------
diffcore-pathspec.c |    2 +-
diffcore-pickaxe.c  |    2 +-
diffcore-rename.c   |    7 ++-----
diffcore.h          |    2 ++
5 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -521,6 +521,13 @@ struct diff_filepair *diff_queue(struct 
 	return dp;
 }
 
+void diff_free_filepair(struct diff_filepair *p)
+{
+	diff_free_filespec_data(p->one);
+	diff_free_filespec_data(p->two);
+	free(p);
+}
+
 static void diff_flush_raw(struct diff_filepair *p,
 			   int line_termination,
 			   int inter_name_termination)
@@ -817,12 +824,8 @@ void diff_flush(int diff_output_style, i
 			break;
 		}
 	}
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		diff_free_filespec_data(p->one);
-		diff_free_filespec_data(p->two);
-		free(p);
-	}
+	for (i = 0; i < q->nr; i++)
+		diff_free_filepair(q->queue[i]);
 	free(q->queue);
 	q->queue = NULL;
 	q->nr = q->alloc = 0;
diff --git a/diffcore-pathspec.c b/diffcore-pathspec.c
--- a/diffcore-pathspec.c
+++ b/diffcore-pathspec.c
@@ -59,7 +59,7 @@ void diffcore_pathspec(const char **path
 		    matches_pathspec(p->two->path, spec, speccnt))
 			diff_q(&outq, p);
 		else
-			free(p);
+			diff_free_filepair(p);
 	}
 	free(q->queue);
 	*q = outq;
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -49,7 +49,7 @@ void diffcore_pickaxe(const char *needle
 			 contains(p->two, needle, len))
 			diff_q(&outq, p);
 		if (onum == outq.nr)
-			free(p);
+			diff_free_filepair(p);
 	}
 	free(q->queue);
 	*q = outq;
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -361,11 +361,8 @@ void diffcore_rename(int detect_rename, 
 			else
 				pair_to_free = p;
 		}
-		if (pair_to_free) {
-			diff_free_filespec_data(pair_to_free->one);
-			diff_free_filespec_data(pair_to_free->two);
-			free(pair_to_free);
-		}
+		if (pair_to_free)
+			diff_free_filepair(pair_to_free);
 	}
 	diff_debug_queue("done copying original", &outq);
 
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -54,6 +54,8 @@ struct diff_filepair {
 	(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
 	S_ISLNK(mode) ? S_IFLNK : S_IFDIR)
 
+extern void diff_free_filepair(struct diff_filepair *);
+
 extern int diff_unmodified_pair(struct diff_filepair *);
 
 struct diff_queue_struct {
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 03/12] Make pathspec only care about the detination tree.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
  2005-05-27 22:49       ` [PATCH 01/12] Fix math thinko in similarity estimator Junio C Hamano
  2005-05-27 22:50       ` [PATCH 02/12] Introduce diff_free_filepair() funcion Junio C Hamano
@ 2005-05-27 22:51       ` Junio C Hamano
  2005-05-27 22:52       ` [PATCH 04/12] Remove unused rank field from diff_core structure Junio C Hamano
                         ` (9 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:51 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Earlier it had a misguided attempt to include paths that matches
either source tree or destination tree after the rename/copy
detection.  The new semantics will be that pathspec defines a
narrowed down world the diffcore operates in, so it should not
even look at where in the source tree the path came from.

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

diffcore-pathspec.c |    3 +--
1 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/diffcore-pathspec.c b/diffcore-pathspec.c
--- a/diffcore-pathspec.c
+++ b/diffcore-pathspec.c
@@ -55,8 +55,7 @@ void diffcore_pathspec(const char **path
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
-		if (matches_pathspec(p->one->path, spec, speccnt) ||
-		    matches_pathspec(p->two->path, spec, speccnt))
+		if (matches_pathspec(p->two->path, spec, speccnt))
 			diff_q(&outq, p);
 		else
 			diff_free_filepair(p);
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 04/12] Remove unused rank field from diff_core structure.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (2 preceding siblings ...)
  2005-05-27 22:51       ` [PATCH 03/12] Make pathspec only care about the detination tree Junio C Hamano
@ 2005-05-27 22:52       ` Junio C Hamano
  2005-05-27 22:53       ` [PATCH 05/12] Do not expose internal scaling to diff-helper Junio C Hamano
                         ` (8 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:52 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

This removes a field that is no longer used from diff_score
structure.

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

diffcore-rename.c |    1 -
1 files changed, 1 deletion(-)

diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -113,7 +113,6 @@ struct diff_score {
 	int src; /* index in rename_src */
 	int dst; /* index in rename_dst */
 	int score;
-	int rank;
 };
 
 static int estimate_similarity(struct diff_filespec *src,
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 05/12] Do not expose internal scaling to diff-helper.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (3 preceding siblings ...)
  2005-05-27 22:52       ` [PATCH 04/12] Remove unused rank field from diff_core structure Junio C Hamano
@ 2005-05-27 22:53       ` Junio C Hamano
  2005-05-27 22:54       ` [PATCH 06/12] Remove final newline from the value of xfrm_msg variable Junio C Hamano
                         ` (7 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:53 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Instead we can normalize what diff-raw records at the diffcore
side.

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

diff-helper.c |    2 --
diff.c        |    2 +-
2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/diff-helper.c b/diff-helper.c
--- a/diff-helper.c
+++ b/diff-helper.c
@@ -4,7 +4,6 @@
 #include "cache.h"
 #include "strbuf.h"
 #include "diff.h"
-#include "diffcore.h" /* just for MAX_SCORE */
 
 static const char *pickaxe = NULL;
 static int line_termination = '\n';
@@ -78,7 +77,6 @@ int main(int ac, const char **av) {
 			if (status == 'R' || status == 'C') {
 				two_paths = 1;
 				sscanf(cp, "%d", &score);
-				score = score * MAX_SCORE / 100;
 				if (line_termination) {
 					cp = strchr(cp,
 						    inter_name_termination);
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -886,7 +886,7 @@ void diff_helper_input(unsigned old_mode
 	if (new_mode)
 		fill_filespec(two, new_sha1, new_mode);
 	dp = diff_queue(&diff_queued_diff, one, two);
-	dp->score = score;
+	dp->score = score * MAX_SCORE / 100;
 	dp->status = status;
 }
 
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 06/12] Remove final newline from the value of xfrm_msg variable.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (4 preceding siblings ...)
  2005-05-27 22:53       ` [PATCH 05/12] Do not expose internal scaling to diff-helper Junio C Hamano
@ 2005-05-27 22:54       ` Junio C Hamano
  2005-05-27 22:54       ` [PATCH 07/12] Clean up diff_setup() to make it more extensible Junio C Hamano
                         ` (6 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:54 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

This change makes the implementation of git-external-diff-script
cleaner.

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

diff.c                   |    6 +++---
git-external-diff-script |    2 +-
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -141,7 +141,7 @@ static void builtin_diff(const char *nam
 			printf("new mode %s\n", temp[1].mode);
 		}
 		if (xfrm_msg && xfrm_msg[0])
-			fputs(xfrm_msg, stdout);
+			puts(xfrm_msg);
 
 		if (strncmp(temp[0].mode, temp[1].mode, 3))
 			/* we do not run diff between different kind
@@ -622,7 +622,7 @@ static void diff_flush_patch(struct diff
 		sprintf(msg_,
 			"similarity index %d%%\n"
 			"copy from %s\n"
-			"copy to %s\n",
+			"copy to %s",
 			(int)(0.5 + p->score * 100.0/MAX_SCORE),
 			p->one->path, p->two->path);
 		msg = msg_;
@@ -631,7 +631,7 @@ static void diff_flush_patch(struct diff
 		sprintf(msg_,
 			"similarity index %d%%\n"
 			"rename old %s\n"
-			"rename new %s\n",
+			"rename new %s",
 			(int)(0.5 + p->score * 100.0/MAX_SCORE),
 			p->one->path, p->two->path);
 		msg = msg_;
diff --git a/git-external-diff-script b/git-external-diff-script
--- a/git-external-diff-script
+++ b/git-external-diff-script
@@ -59,7 +59,7 @@ then
     echo "new mode $mode2"
     if test "$xfrm_msg" != ""
     then
-	echo -n $xfrm_msg
+	echo "$xfrm_msg"
     fi
 fi
 diff ${GIT_DIFF_OPTS-'-pu'} -L "a/$name1" -L "b/$name2" "$tmp1" "$tmp2"
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 07/12] Clean up diff_setup() to make it more extensible.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (5 preceding siblings ...)
  2005-05-27 22:54       ` [PATCH 06/12] Remove final newline from the value of xfrm_msg variable Junio C Hamano
@ 2005-05-27 22:54       ` Junio C Hamano
  2005-05-27 22:55       ` [PATCH 08/12] Remove a function not used anymore Junio C Hamano
                         ` (5 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:54 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

This changes the argument of diff_setup() from an integer that
says if we are feeding reversed diff to a bitmask, so that later
global options can be added more easily.

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

diff-cache.c |    6 +++---
diff-files.c |    6 +++---
diff-tree.c  |    6 +++---
diff.c       |    5 +++--
diff.h       |   12 +++++++-----
5 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/diff-cache.c b/diff-cache.c
--- a/diff-cache.c
+++ b/diff-cache.c
@@ -5,7 +5,7 @@ static int cached_only = 0;
 static int diff_output_format = DIFF_FORMAT_HUMAN;
 static int match_nonexisting = 0;
 static int detect_rename = 0;
-static int reverse_diff = 0;
+static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 
@@ -202,7 +202,7 @@ int main(int argc, const char **argv)
 			continue;
 		}
 		if (!strcmp(arg, "-R")) {
-			reverse_diff = 1;
+			diff_setup_opt |= DIFF_SETUP_REVERSE;
 			continue;
 		}
 		if (!strcmp(arg, "-S")) {
@@ -224,7 +224,7 @@ int main(int argc, const char **argv)
 		usage(diff_cache_usage);
 
 	/* The rest is for paths restriction. */
-	diff_setup(reverse_diff);
+	diff_setup(diff_setup_opt);
 
 	mark_merge_entries();
 
diff --git a/diff-files.c b/diff-files.c
--- a/diff-files.c
+++ b/diff-files.c
@@ -11,7 +11,7 @@ static const char *diff_files_usage =
 
 static int diff_output_format = DIFF_FORMAT_HUMAN;
 static int detect_rename = 0;
-static int reverse_diff = 0;
+static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 static int silent = 0;
@@ -51,7 +51,7 @@ int main(int argc, const char **argv)
 		else if (!strcmp(argv[1], "-z"))
 			diff_output_format = DIFF_FORMAT_MACHINE;
 		else if (!strcmp(argv[1], "-R"))
-			reverse_diff = 1;
+			diff_setup_opt |= DIFF_SETUP_REVERSE;
 		else if (!strcmp(argv[1], "-S"))
 			pickaxe = argv[1] + 2;
 		else if (!strncmp(argv[1], "-M", 2)) {
@@ -75,7 +75,7 @@ int main(int argc, const char **argv)
 		exit(1);
 	}
 
-	diff_setup(reverse_diff);
+	diff_setup(diff_setup_opt);
 
 	for (i = 0; i < entries; i++) {
 		struct stat st;
diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -10,7 +10,7 @@ static int show_tree_entry_in_recursive 
 static int read_stdin = 0;
 static int diff_output_format = DIFF_FORMAT_HUMAN;
 static int detect_rename = 0;
-static int reverse_diff = 0;
+static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 static const char *header = NULL;
@@ -255,7 +255,7 @@ static int diff_tree_sha1(const unsigned
 
 static void call_diff_setup(void)
 {
-	diff_setup(reverse_diff);
+	diff_setup(diff_setup_opt);
 }
 
 static int call_diff_flush(void)
@@ -497,7 +497,7 @@ int main(int argc, const char **argv)
 			continue;
 		}
 		if (!strcmp(arg, "-R")) {
-			reverse_diff = 1;
+			diff_setup_opt |= DIFF_SETUP_REVERSE;
 			continue;
 		}
 		if (!strcmp(arg, "-p")) {
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -492,9 +492,10 @@ static void run_diff(const char *name,
 		run_external_diff(pgm, name, other, one, two, xfrm_msg);
 }
 
-void diff_setup(int reverse_diff_)
+void diff_setup(int flags)
 {
-	reverse_diff = reverse_diff_;
+	if (flags & DIFF_SETUP_REVERSE)
+		reverse_diff = 1;
 }
 
 struct diff_queue_struct diff_queued_diff;
diff --git a/diff.h b/diff.h
--- a/diff.h
+++ b/diff.h
@@ -28,11 +28,8 @@ extern void diff_unmerge(const char *pat
 
 extern int diff_scoreopt_parse(const char *opt);
 
-#define DIFF_FORMAT_HUMAN	0
-#define DIFF_FORMAT_MACHINE	1
-#define DIFF_FORMAT_PATCH	2
-#define DIFF_FORMAT_NO_OUTPUT	3
-extern void diff_setup(int reverse);
+#define DIFF_SETUP_REVERSE      	1
+extern void diff_setup(int flags);
 
 #define DIFF_DETECT_RENAME	1
 #define DIFF_DETECT_COPY	2
@@ -44,6 +41,11 @@ extern void diffcore_pathspec(const char
 
 extern int diff_queue_is_empty(void);
 
+#define DIFF_FORMAT_HUMAN	0
+#define DIFF_FORMAT_MACHINE	1
+#define DIFF_FORMAT_PATCH	2
+#define DIFF_FORMAT_NO_OUTPUT	3
+
 extern void diff_flush(int output_style, int resolve_rename_copy);
 
 #endif /* DIFF_H */
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 08/12] Remove a function not used anymore.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (6 preceding siblings ...)
  2005-05-27 22:54       ` [PATCH 07/12] Clean up diff_setup() to make it more extensible Junio C Hamano
@ 2005-05-27 22:55       ` Junio C Hamano
  2005-05-27 22:55       ` [PATCH 09/12] Add --pickaxe-all to diff-* brothers Junio C Hamano
                         ` (4 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:55 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Earlier rename/copy detection left unmodified filepair in the
output and forced downstream to keep them even when they are
filtering, and the diff_needs_to_stay() function was used for
the logic.  It is not used anymore, so remove it.

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

diff.c     |   22 ----------------------
diffcore.h |    3 ---
2 files changed, 25 deletions(-)

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -647,28 +647,6 @@ static void diff_flush_patch(struct diff
 		run_diff(name, other, p->one, p->two, msg);
 }
 
-int diff_needs_to_stay(struct diff_queue_struct *q, int i,
-		       struct diff_filespec *it)
-{
-	/* If it will be used in later entry (either stay or used
-	 * as the source of rename/copy), we need to copy, not rename.
-	 */
-	while (i < q->nr) {
-		struct diff_filepair *p = q->queue[i++];
-		if (!DIFF_FILE_VALID(p->two))
-			continue; /* removed is fine */
-		if (strcmp(p->one->path, it->path))
-			continue; /* not relevant */
-
-		/* p has its src set to *it and it is not a delete;
-		 * it will be used for in-place change, rename/copy,
-		 * or just stays there.  We cannot rename it out.
-		 */
-		return 1;
-	}
-	return 0;
-}
-
 int diff_queue_is_empty(void)
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -70,9 +70,6 @@ extern struct diff_filepair *diff_queue(
 					struct diff_filespec *);
 extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);
 
-extern int diff_needs_to_stay(struct diff_queue_struct *, int,
-			      struct diff_filespec *);
-
 #define DIFF_DEBUG 0
 #if DIFF_DEBUG
 void diff_debug_filespec(struct diff_filespec *, int, const char *);
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 09/12] Add --pickaxe-all to diff-* brothers.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (7 preceding siblings ...)
  2005-05-27 22:55       ` [PATCH 08/12] Remove a function not used anymore Junio C Hamano
@ 2005-05-27 22:55       ` Junio C Hamano
  2005-05-27 22:55       ` [PATCH 10/12] Fix the way diffcore-rename records unremoved source Junio C Hamano
                         ` (3 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:55 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

When --pickaxe-all is given in addition to -S, pickaxe shows the
entire diffs contained in the changeset, not just the diffs for
the filepair that touched the sought-after string.  This is
useful to see the changes in context.

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

diff-cache.c       |    7 ++++
diff-files.c       |    5 ++-
diff-helper.c      |    7 +++-
diff-tree.c        |    7 ++++
diff.h             |    4 ++
diffcore-pickaxe.c |   77 +++++++++++++++++++++++++++++++++++++++--------------
6 files changed, 81 insertions(+), 26 deletions(-)

diff --git a/diff-cache.c b/diff-cache.c
--- a/diff-cache.c
+++ b/diff-cache.c
@@ -8,6 +8,7 @@ static int detect_rename = 0;
 static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
+static int pickaxe_opts = 0;
 
 /* A file entry went away or appeared */
 static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)
@@ -209,6 +210,10 @@ int main(int argc, const char **argv)
 			pickaxe = arg + 2;
 			continue;
 		}
+		if (!strcmp(arg, "--pickaxe-all")) {
+			pickaxe_opts = DIFF_PICKAXE_ALL;
+			continue;
+		}
 		if (!strcmp(arg, "-m")) {
 			match_nonexisting = 1;
 			continue;
@@ -238,7 +243,7 @@ int main(int argc, const char **argv)
 	if (detect_rename)
 		diffcore_rename(detect_rename, diff_score_opt);
 	if (pickaxe)
-		diffcore_pickaxe(pickaxe);
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (pathspec)
 		diffcore_pathspec(pathspec);
 	diff_flush(diff_output_format, 1);
diff --git a/diff-files.c b/diff-files.c
--- a/diff-files.c
+++ b/diff-files.c
@@ -14,6 +14,7 @@ static int detect_rename = 0;
 static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
+static int pickaxe_opts = 0;
 static int silent = 0;
 
 static void show_unmerge(const char *path)
@@ -54,6 +55,8 @@ int main(int argc, const char **argv)
 			diff_setup_opt |= DIFF_SETUP_REVERSE;
 		else if (!strcmp(argv[1], "-S"))
 			pickaxe = argv[1] + 2;
+		else if (!strcmp(argv[1], "--pickaxe-all"))
+			pickaxe_opts = DIFF_PICKAXE_ALL;
 		else if (!strncmp(argv[1], "-M", 2)) {
 			diff_score_opt = diff_scoreopt_parse(argv[1]);
 			detect_rename = DIFF_DETECT_RENAME;
@@ -116,7 +119,7 @@ int main(int argc, const char **argv)
 	if (detect_rename)
 		diffcore_rename(detect_rename, diff_score_opt);
 	if (pickaxe)
-		diffcore_pickaxe(pickaxe);
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (1 < argc)
 		diffcore_pathspec(argv + 1);
 	diff_flush(diff_output_format, 1);
diff --git a/diff-helper.c b/diff-helper.c
--- a/diff-helper.c
+++ b/diff-helper.c
@@ -6,6 +6,7 @@
 #include "diff.h"
 
 static const char *pickaxe = NULL;
+static int pickaxe_opts = 0;
 static int line_termination = '\n';
 static int inter_name_termination = '\t';
 
@@ -23,6 +24,8 @@ int main(int ac, const char **av) {
 		else if (av[1][1] == 'S') {
 			pickaxe = av[1] + 2;
 		}
+		else if (!strcmp(av[1], "--pickaxe-all"))
+			pickaxe_opts = DIFF_PICKAXE_ALL;
 		else
 			usage(diff_helper_usage);
 		ac--; av++;
@@ -127,14 +130,14 @@ int main(int ac, const char **av) {
 			continue;
 		}
 		if (pickaxe)
-			diffcore_pickaxe(pickaxe);
+			diffcore_pickaxe(pickaxe, pickaxe_opts);
 		if (1 < ac)
 			diffcore_pathspec(av + 1);
 		diff_flush(DIFF_FORMAT_PATCH, 0);
 		printf("%s\n", sb.buf);
 	}
 	if (pickaxe)
-		diffcore_pickaxe(pickaxe);
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (1 < ac)
 		diffcore_pathspec(av + 1);
 	diff_flush(DIFF_FORMAT_PATCH, 0);
diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -13,6 +13,7 @@ static int detect_rename = 0;
 static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
+static int pickaxe_opts = 0;
 static const char *header = NULL;
 static const char *header_prefix = "";
 
@@ -263,7 +264,7 @@ static int call_diff_flush(void)
 	if (detect_rename)
 		diffcore_rename(detect_rename, diff_score_opt);
 	if (pickaxe)
-		diffcore_pickaxe(pickaxe);
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (diff_queue_is_empty()) {
 		diff_flush(DIFF_FORMAT_NO_OUTPUT, 0);
 		return 0;
@@ -509,6 +510,10 @@ int main(int argc, const char **argv)
 			pickaxe = arg + 2;
 			continue;
 		}
+		if (!strcmp(arg, "--pickaxe-all")) {
+			pickaxe_opts = DIFF_PICKAXE_ALL;
+			continue;
+		}
 		if (!strncmp(arg, "-M", 2)) {
 			detect_rename = DIFF_DETECT_RENAME;
 			diff_score_opt = diff_scoreopt_parse(arg);
diff --git a/diff.h b/diff.h
--- a/diff.h
+++ b/diff.h
@@ -36,7 +36,9 @@ extern void diff_setup(int flags);
 
 extern void diffcore_rename(int rename_copy, int minimum_score);
 
-extern void diffcore_pickaxe(const char *needle);
+#define DIFF_PICKAXE_ALL	1
+extern void diffcore_pickaxe(const char *needle, int opts);
+
 extern void diffcore_pathspec(const char **pathspec);
 
 extern int diff_queue_is_empty(void);
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -21,36 +21,73 @@ static int contains(struct diff_filespec
 	return 0;
 }
 
-void diffcore_pickaxe(const char *needle)
+void diffcore_pickaxe(const char *needle, int opts)
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
 	unsigned long len = strlen(needle);
-	int i;
+	int i, has_changes;
 	struct diff_queue_struct outq;
 	outq.queue = NULL;
 	outq.nr = outq.alloc = 0;
 
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		int onum = outq.nr;
-		if (!DIFF_FILE_VALID(p->one)) {
-			if (!DIFF_FILE_VALID(p->two))
-				continue; /* ignore nonsense */
-			/* created */
-			if (contains(p->two, needle, len))
-				diff_q(&outq, p);
+	if (opts & DIFF_PICKAXE_ALL) {
+		/* Showing the whole changeset if needle exists */
+		for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			if (!DIFF_FILE_VALID(p->one)) {
+				if (!DIFF_FILE_VALID(p->two))
+					continue; /* ignore unmerged */
+				/* created */
+				if (contains(p->two, needle, len))
+					has_changes++;
+			}
+			else if (!DIFF_FILE_VALID(p->two)) {
+				if (contains(p->one, needle, len))
+					has_changes++;
+			}
+			else if (!diff_unmodified_pair(p) &&
+				 contains(p->one, needle, len) !=
+				 contains(p->two, needle, len))
+				has_changes++;
 		}
-		else if (!DIFF_FILE_VALID(p->two)) {
-			if (contains(p->one, needle, len))
+		if (has_changes)
+			return; /* not munge the queue */
+
+		/* otherwise we will clear the whole queue
+		 * by copying the empty outq at the end of this
+		 * function, but first clear the current entries
+		 * in the queue.
+		 */
+		for (i = 0; i < q->nr; i++)
+			diff_free_filepair(q->queue[i]);
+	}
+	else 
+		/* Showing only the filepairs that has the needle */
+		for (i = 0; i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			has_changes = 0;
+			if (!DIFF_FILE_VALID(p->one)) {
+				if (!DIFF_FILE_VALID(p->two))
+					; /* ignore unmerged */
+				/* created */
+				else if (contains(p->two, needle, len))
+					has_changes = 1;
+			}
+			else if (!DIFF_FILE_VALID(p->two)) {
+				if (contains(p->one, needle, len))
+					has_changes = 1;
+			}
+			else if (!diff_unmodified_pair(p) &&
+				 contains(p->one, needle, len) !=
+				 contains(p->two, needle, len))
+				has_changes = 1;
+
+			if (has_changes)
 				diff_q(&outq, p);
+			else
+				diff_free_filepair(p);
 		}
-		else if (!diff_unmodified_pair(p) &&
-			 contains(p->one, needle, len) !=
-			 contains(p->two, needle, len))
-			diff_q(&outq, p);
-		if (onum == outq.nr)
-			diff_free_filepair(p);
-	}
+
 	free(q->queue);
 	*q = outq;
 	return;
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 10/12] Fix the way diffcore-rename records unremoved source.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (8 preceding siblings ...)
  2005-05-27 22:55       ` [PATCH 09/12] Add --pickaxe-all to diff-* brothers Junio C Hamano
@ 2005-05-27 22:55       ` Junio C Hamano
  2005-05-27 22:56       ` [PATCH 11/12] Move pathspec to the beginning of the diffcore chain Junio C Hamano
                         ` (2 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:55 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Earier version of diffcore-rename used to keep unmodified
filepair in its output so that the last stage of the processing
that tells renames from copies can make all of rename/copy to
copies.  However this had a bad interaction with other diffcore
filters that wanted to run after diffcore-rename, in that such
unmodified filepair must be retained for proper distinction
between renames and copies to happen.

This patch fixes the problem by changing the way diffcore-rename
records the information needed to distinguish "all are copies"
case and "the last one is a rename" case.

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

diff.c              |   76 ++++++++++++++++++--------------------
diffcore-rename.c   |   63 ++++++++++++-------------------
diffcore.h          |    7 ++-
t/t4007-rename-3.sh |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 169 insertions(+), 80 deletions(-)
new file (100644): t/t4007-rename-3.sh

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -518,6 +518,7 @@ struct diff_filepair *diff_queue(struct 
 	dp->one = one;
 	dp->two = two;
 	dp->score = 0;
+	dp->source_stays = 0;
 	diff_q(queue, dp);
 	return dp;
 }
@@ -675,8 +676,8 @@ void diff_debug_filepair(const struct di
 {
 	diff_debug_filespec(p->one, i, "one");
 	diff_debug_filespec(p->two, i, "two");
-	fprintf(stderr, "score %d, status %c\n",
-		p->score, p->status ? : '?');
+	fprintf(stderr, "score %d, status %c source_stays %d\n",
+		p->score, p->status ? : '?', p->source_stays);
 }
 
 void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
@@ -698,8 +699,6 @@ static void diff_resolve_rename_copy(voi
 	struct diff_filepair *p, *pp;
 	struct diff_queue_struct *q = &diff_queued_diff;
 
-	/* This should not depend on the ordering of things. */
-
 	diff_debug_queue("resolve-rename-copy", q);
 
 	for (i = 0; i < q->nr; i++) {
@@ -707,23 +706,28 @@ static void diff_resolve_rename_copy(voi
 		p->status = 0; /* undecided */
 		if (DIFF_PAIR_UNMERGED(p))
 			p->status = 'U';
-		else if (!DIFF_FILE_VALID((p)->one))
+		else if (!DIFF_FILE_VALID(p->one))
 			p->status = 'N';
-		else if (!DIFF_FILE_VALID((p)->two)) {
-			/* Deletion record should be omitted if there
-			 * are rename/copy entries using this one as
-			 * the source.  Then we can say one of them
-			 * is a rename and the rest are copies.
+		else if (!DIFF_FILE_VALID(p->two)) {
+			/* Deleted entry may have been picked up by
+			 * another rename-copy entry.  So we scan the
+			 * queue and if we find one that uses us as the
+			 * source we do not say delete for this entry.
 			 */
-			p->status = 'D';
 			for (j = 0; j < q->nr; j++) {
 				pp = q->queue[j];
-				if (!strcmp(pp->one->path, p->one->path) &&
-				    strcmp(pp->one->path, pp->two->path)) {
+				if (!strcmp(p->one->path, pp->one->path) &&
+				    pp->score) {
+					/* rename/copy are always valid
+					 * so we do not say DIFF_FILE_VALID()
+					 * on pp->one and pp->two.
+					 */
 					p->status = 'X';
 					break;
 				}
 			}
+			if (!p->status)
+				p->status = 'D';
 		}
 		else if (DIFF_PAIR_TYPE_CHANGED(p))
 			p->status = 'T';
@@ -732,33 +736,24 @@ static void diff_resolve_rename_copy(voi
 		 * whose both sides are valid and of the same type, i.e.
 		 * either in-place edit or rename/copy edit.
 		 */
-		else if (strcmp(p->one->path, p->two->path)) {
-			/* See if there is somebody else anywhere that
-			 * will keep the path (either modified or
-			 * unmodified).  If so, we have to be a copy,
-			 * not a rename.  In addition, if there is
-			 * some other rename or copy that comes later
-			 * than us that uses the same source, we
-			 * have to be a copy, not a rename.
+		else if (p->score) {
+			if (p->source_stays) {
+				p->status = 'C';
+				continue;
+			}
+			/* See if there is some other filepair that
+			 * copies from the same source as us.  If so
+			 * we are a copy.  Otherwise we are a rename.
 			 */
-			for (j = 0; j < q->nr; j++) {
+			for (j = i + 1; j < q->nr; j++) {
 				pp = q->queue[j];
 				if (strcmp(pp->one->path, p->one->path))
-					continue;
-				if (!strcmp(pp->one->path, pp->two->path)) {
-					if (DIFF_FILE_VALID(pp->two)) {
-						/* non-delete */
-						p->status = 'C';
-						break;
-					}
-					continue;
-				}
-				/* pp is a rename/copy ... */
-				if (i < j) {
-					/* ... and comes later than us */
-					p->status = 'C';
-					break;
-				}
+					continue; /* not us */
+				if (!pp->score)
+					continue; /* not a rename/copy */
+				/* pp is a rename/copy from the same source */
+				p->status = 'C';
+				break;
 			}
 			if (!p->status)
 				p->status = 'R';
@@ -767,8 +762,11 @@ static void diff_resolve_rename_copy(voi
 			 p->one->mode != p->two->mode)
 			p->status = 'M';
 		else
-			/* this is a "no-change" entry */
-			p->status = 'X';
+			/* this is a "no-change" entry.
+			 * should not happen anymore.
+			 * p->status = 'X';
+			 */
+			die("internal error in diffcore: unmodified entry remains");
 	}
 	diff_debug_queue("resolve-rename-copy done", q);
 }
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -52,14 +52,15 @@ static struct diff_rename_dst *locate_re
 	return &(rename_dst[first]);
 }
 
+/* Table of rename/copy src files */
 static struct diff_rename_src {
 	struct diff_filespec *one;
-	unsigned src_used : 1;
+	unsigned src_stays : 1;
 } *rename_src;
 static int rename_src_nr, rename_src_alloc;
 
-static struct diff_rename_src *locate_rename_src(struct diff_filespec *one,
-						 int insert_ok)
+static struct diff_rename_src *register_rename_src(struct diff_filespec *one,
+						   int src_stays)
 {
 	int first, last;
 
@@ -77,9 +78,7 @@ static struct diff_rename_src *locate_re
 		}
 		first = next+1;
 	}
-	/* not found */
-	if (!insert_ok)
-		return NULL;
+
 	/* insert to make it at "first" */
 	if (rename_src_alloc <= rename_src_nr) {
 		rename_src_alloc = alloc_nr(rename_src_alloc);
@@ -91,7 +90,7 @@ static struct diff_rename_src *locate_re
 		memmove(rename_src + first + 1, rename_src + first,
 			(rename_src_nr - first - 1) * sizeof(*rename_src));
 	rename_src[first].one = one;
-	rename_src[first].src_used = 0;
+	rename_src[first].src_stays = src_stays;
 	return &(rename_src[first]);
 }
 
@@ -199,15 +198,14 @@ static void record_rename_pair(struct di
 	fill_filespec(two, dst->sha1, dst->mode);
 
 	dp = diff_queue(renq, one, two);
-	dp->score = score;
-
-	rename_src[src_index].src_used = 1;
+	dp->score = score ? : 1; /* make sure it is at least 1 */
+	dp->source_stays = rename_src[src_index].src_stays;
 	rename_dst[dst_index].pair = dp;
 }
 
 /*
  * We sort the rename similarity matrix with the score, in descending
- * order (more similar first).
+ * order (the most similar first).
  */
 static int score_compare(const void *a_, const void *b_)
 {
@@ -254,9 +252,9 @@ void diffcore_rename(int detect_rename, 
 			else
 				locate_rename_dst(p->two, 1);
 		else if (!DIFF_FILE_VALID(p->two))
-			locate_rename_src(p->one, 1);
-		else if (1 < detect_rename) /* find copy, too */
-			locate_rename_src(p->one, 1);
+			register_rename_src(p->one, 0);
+		else if (detect_rename == DIFF_DETECT_COPY)
+			register_rename_src(p->one, 1);
 	}
 	if (rename_dst_nr == 0)
 		goto cleanup; /* nothing to do */
@@ -280,7 +278,7 @@ void diffcore_rename(int detect_rename, 
 	 * doing the delta matrix altogether.
 	 */
 	if (renq.nr == rename_dst_nr)
-		goto flush_rest;
+		goto cleanup;
 
 	num_create = (rename_dst_nr - renq.nr);
 	num_src = rename_src_nr;
@@ -307,37 +305,30 @@ void diffcore_rename(int detect_rename, 
 		if (dst->pair)
 			continue; /* already done, either exact or fuzzy. */
 		if (mx[i].score < minimum_score)
-			break; /* there is not any more diffs applicable. */
+			break; /* there is no more usable pair. */
 		record_rename_pair(&renq, mx[i].dst, mx[i].src, mx[i].score);
 	}
 	free(mx);
 	diff_debug_queue("done detecting fuzzy", &renq);
 
- flush_rest:
+ cleanup:
 	/* At this point, we have found some renames and copies and they
 	 * are kept in renq.  The original list is still in *q.
-	 *
-	 * Scan the original list and move them into the outq; we will sort
-	 * outq and swap it into the queue supplied to pass that to
-	 * downstream, so we assign the sort keys in this loop.
-	 *
-	 * See comments at the top of record_rename_pair for numbers used
-	 * to assign rename_rank.
 	 */
 	outq.queue = NULL;
 	outq.nr = outq.alloc = 0;
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
-		struct diff_rename_src *src = locate_rename_src(p->one, 0);
 		struct diff_rename_dst *dst = locate_rename_dst(p->two, 0);
 		struct diff_filepair *pair_to_free = NULL;
 
 		if (dst) {
 			/* creation */
 			if (dst->pair) {
-				/* renq has rename/copy already to produce
-				 * this file, so we do not emit the creation
-				 * record in the output.
+				/* renq has rename/copy to produce
+				 * this file already, so we do not
+				 * emit the creation record in the
+				 * output.
 				 */
 				diff_q(&outq, dst->pair);
 				pair_to_free = p;
@@ -349,17 +340,12 @@ void diffcore_rename(int detect_rename, 
 				diff_q(&outq, p);
 		}
 		else if (!diff_unmodified_pair(p))
-			/* all the other cases need to be recorded as is */
+			/* all the usual ones need to be kept */
 			diff_q(&outq, p);
-		else {
-			/* unmodified pair needs to be recorded only if
-			 * it is used as the source of rename/copy
-			 */
-			if (src && src->src_used)
-				diff_q(&outq, p);
-			else
-				pair_to_free = p;
-		}
+		else
+			/* no need to keep unmodified pairs */
+			pair_to_free = p;
+
 		if (pair_to_free)
 			diff_free_filepair(pair_to_free);
 	}
@@ -370,7 +356,6 @@ void diffcore_rename(int detect_rename, 
 	*q = outq;
 	diff_debug_queue("done collapsing", q);
 
- cleanup:
 	free(rename_dst);
 	rename_dst = NULL;
 	rename_dst_nr = rename_dst_alloc = 0;
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -39,8 +39,11 @@ extern void diff_free_filespec_data(stru
 struct diff_filepair {
 	struct diff_filespec *one;
 	struct diff_filespec *two;
-	int score; /* only valid when one and two are different paths */
-	int status; /* M C R N D U (see Documentation/diff-format.txt) */
+	unsigned short int score; /* only valid when one and two are
+				   * different paths
+				   */
+	char source_stays; /* all of R/C are copies */
+	char status; /* M C R N D U (see Documentation/diff-format.txt) */
 };
 #define DIFF_PAIR_UNMERGED(p) \
 	(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
new file mode 100644
--- /dev/null
+++ b/t/t4007-rename-3.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Rename interaction with pathspec.
+
+'
+. ./test-lib.sh
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+sanitize_diff_raw='s/ \('"$_x40"'\) \1 \([CR]\)[0-9]*	/ \1 \1 \2#	/'
+compare_diff_raw () {
+    # When heuristics are improved, the score numbers would change.
+    # Ignore them while comparing.
+    # Also we do not check SHA1 hash generation in this test, which
+    # is a job for t0000-basic.sh
+
+    sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
+    sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+test_expect_success \
+    'prepare reference tree' \
+    'mkdir path0 path1 &&
+     cp ../../COPYING path0/COPYING &&
+     git-update-cache --add path0/COPYING &&
+    tree=$(git-write-tree) &&
+    echo $tree'
+
+test_expect_success \
+    'prepare work tree' \
+    'cp path0/COPYING path1/COPYING &&
+     git-update-cache --add --remove path0/COPYING path1/COPYING'
+
+# In the tree, there is only path0/COPYING.  In the cache, path0 and
+# path1 both have COPYING and the latter is a copy of path0/COPYING.
+# Comparing the full tree with cache should tell us so.
+
+git-diff-cache -C $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100	path0/COPYING	path1/COPYING
+EOF
+
+test_expect_success \
+    'validate the result' \
+    'compare_diff_raw current expected'
+
+# In the tree, there is only path0/COPYING.  In the cache, path0 and
+# path1 both have COPYING and the latter is a copy of path0/COPYING.
+# When we omit output from path0 it should still be able to tell us
+# that path1/COPYING is result from a copy from path0/COPYING, not
+# rename, which would imply path0/COPYING is now gone.
+
+git-diff-cache -C $tree path1 >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100	path0/COPYING	path1/COPYING
+EOF
+
+test_expect_success \
+    'validate the result' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'tweak work tree' \
+    'rm -f path0/COPYING &&
+     git-update-cache --remove path0/COPYING'
+
+# In the tree, there is only path0/COPYING.  In the cache, path0 does
+# not have COPYING anymore and path1 has COPYING which is a copy of
+# path0/COPYING.  Showing the full tree with cache should tell us about
+# the rename.
+
+git-diff-cache -C $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100	path0/COPYING	path1/COPYING
+EOF
+
+test_expect_success \
+    'validate the result' \
+    'compare_diff_raw current expected'
+
+# In the tree, there is only path0/COPYING.  In the cache, path0 does
+# not have COPYING anymore and path1 has COPYING which is a copy of
+# path0/COPYING.  Even if we restrict the output to path1, it still
+# should show us the rename.
+
+git-diff-cache -C $tree path1 >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100	path0/COPYING	path1/COPYING
+EOF
+
+test_expect_success \
+    'validate the result' \
+    'compare_diff_raw current expected'
+
+test_done
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 11/12] Move pathspec to the beginning of the diffcore chain.
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (9 preceding siblings ...)
  2005-05-27 22:55       ` [PATCH 10/12] Fix the way diffcore-rename records unremoved source Junio C Hamano
@ 2005-05-27 22:56       ` Junio C Hamano
  2005-05-27 22:56       ` [PATCH 12/12] Optimize diff-tree -[CM] --stdin Junio C Hamano
  2005-05-27 23:03       ` [PATCH 00/12] Diff updates Junio C Hamano
  12 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:56 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

This changes the way how pathspec is used in the three diff-*
brothers.  Earlier, they tried to grab as much information from
the original input and used pathspec to limit the output.  This
version uses pathspec upfront to narrow the world diffcore
operates in, so "git-diff-* <arguments> some-directory" does not
look at things outside the specified subtree when finding
rename/copy or running pickaxe.

Since diff-tree already takes this view and does not feed
anything outside the specified directotires to begin with, this
patch does not have to touch that command.

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

diff-cache.c        |    4 ++--
diff-files.c        |    4 ++--
diff-helper.c       |    8 ++++----
t/t4007-rename-3.sh |   22 +++++++++++-----------
4 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/diff-cache.c b/diff-cache.c
--- a/diff-cache.c
+++ b/diff-cache.c
@@ -240,12 +240,12 @@ int main(int argc, const char **argv)
 		die("unable to read tree object %s", tree_name);
 
 	ret = diff_cache(active_cache, active_nr);
+	if (pathspec)
+		diffcore_pathspec(pathspec);
 	if (detect_rename)
 		diffcore_rename(detect_rename, diff_score_opt);
 	if (pickaxe)
 		diffcore_pickaxe(pickaxe, pickaxe_opts);
-	if (pathspec)
-		diffcore_pathspec(pathspec);
 	diff_flush(diff_output_format, 1);
 	return ret;
 }
diff --git a/diff-files.c b/diff-files.c
--- a/diff-files.c
+++ b/diff-files.c
@@ -116,12 +116,12 @@ int main(int argc, const char **argv)
 		show_modified(oldmode, mode, ce->sha1, null_sha1,
 			      ce->name);
 	}
+	if (1 < argc)
+		diffcore_pathspec(argv + 1);
 	if (detect_rename)
 		diffcore_rename(detect_rename, diff_score_opt);
 	if (pickaxe)
 		diffcore_pickaxe(pickaxe, pickaxe_opts);
-	if (1 < argc)
-		diffcore_pathspec(argv + 1);
 	diff_flush(diff_output_format, 1);
 	return 0;
 }
diff --git a/diff-helper.c b/diff-helper.c
--- a/diff-helper.c
+++ b/diff-helper.c
@@ -129,17 +129,17 @@ int main(int ac, const char **av) {
 					  new_path);
 			continue;
 		}
-		if (pickaxe)
-			diffcore_pickaxe(pickaxe, pickaxe_opts);
 		if (1 < ac)
 			diffcore_pathspec(av + 1);
+		if (pickaxe)
+			diffcore_pickaxe(pickaxe, pickaxe_opts);
 		diff_flush(DIFF_FORMAT_PATCH, 0);
 		printf("%s\n", sb.buf);
 	}
-	if (pickaxe)
-		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	if (1 < ac)
 		diffcore_pathspec(av + 1);
+	if (pickaxe)
+		diffcore_pickaxe(pickaxe, pickaxe_opts);
 	diff_flush(DIFF_FORMAT_PATCH, 0);
 	return 0;
 }
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -46,23 +46,23 @@ cat >expected <<\EOF
 EOF
 
 test_expect_success \
-    'validate the result' \
+    'validate the result (#1)' \
     'compare_diff_raw current expected'
 
 # In the tree, there is only path0/COPYING.  In the cache, path0 and
 # path1 both have COPYING and the latter is a copy of path0/COPYING.
-# When we omit output from path0 it should still be able to tell us
-# that path1/COPYING is result from a copy from path0/COPYING, not
-# rename, which would imply path0/COPYING is now gone.
+# However when we say we care only about path1, we should just see
+# path1/COPYING suddenly appearing from nowhere, not detected as
+# a copy from path0/COPYING.
 
 git-diff-cache -C $tree path1 >current
 
 cat >expected <<\EOF
-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100	path0/COPYING	path1/COPYING
+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 N	path1/COPYING
 EOF
 
 test_expect_success \
-    'validate the result' \
+    'validate the result (#2)' \
     'compare_diff_raw current expected'
 
 test_expect_success \
@@ -82,22 +82,22 @@ cat >expected <<\EOF
 EOF
 
 test_expect_success \
-    'validate the result' \
+    'validate the result (#3)' \
     'compare_diff_raw current expected'
 
 # In the tree, there is only path0/COPYING.  In the cache, path0 does
 # not have COPYING anymore and path1 has COPYING which is a copy of
-# path0/COPYING.  Even if we restrict the output to path1, it still
-# should show us the rename.
+# path0/COPYING.  When we say we care only about path1, we should just
+# see path1/COPYING appearing from nowhere.
 
 git-diff-cache -C $tree path1 >current
 
 cat >expected <<\EOF
-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100	path0/COPYING	path1/COPYING
+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 N	path1/COPYING
 EOF
 
 test_expect_success \
-    'validate the result' \
+    'validate the result (#4)' \
     'compare_diff_raw current expected'
 
 test_done
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 12/12] Optimize diff-tree -[CM] --stdin
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (10 preceding siblings ...)
  2005-05-27 22:56       ` [PATCH 11/12] Move pathspec to the beginning of the diffcore chain Junio C Hamano
@ 2005-05-27 22:56       ` Junio C Hamano
  2005-06-04  2:17         ` Yoichi Yuasa
  2005-05-27 23:03       ` [PATCH 00/12] Diff updates Junio C Hamano
  12 siblings, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 22:56 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

This attempts to optimize "diff-tree -[CM] --stdin", which
compares successible tree pairs.  This optimization does not
make much sense for other commands in the diff-* brothers.

When reading from --stdin and using rename/copy detection, the
patch makes diff-tree to read the current index file first.
This is done to reuse the optimization used by diff-cache in the
non-cached case.  Similarity estimator can avoid expanding a
blob if the index says what is in the work tree has an exact
copy of that blob already expanded.

Another optimization the patch makes is to check only file sizes
first to terminate similarity estimation early.  In order for
this to work, it needs a way to tell the size of the blob
without expanding it.  Since an obvious way of doing it, which
is to keep all the blobs previously used in the memory, is too
costly, it does so by keeping the filesize for each object it
has already seen in memory.

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

diff-tree.c        |    3 +
diff.c             |   83 +++++++++++++++++++++++++++++++++++++++++++++++++++--
diff.h             |    2 +
diffcore-pickaxe.c |    2 -
diffcore-rename.c  |   19 ++++++++----
diffcore.h         |    2 -
6 files changed, 102 insertions(+), 9 deletions(-)

diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -578,6 +578,9 @@ int main(int argc, const char **argv)
 	if (!read_stdin)
 		return 0;
 
+	if (detect_rename)
+		diff_setup_opt |= (DIFF_SETUP_USE_SIZE_CACHE |
+				   DIFF_SETUP_USE_CACHE);
 	while (fgets(line, sizeof(line), stdin))
 		diff_tree_stdin(line);
 
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -12,6 +12,7 @@ static const char *diff_opts = "-pu";
 static unsigned char null_sha1[20] = { 0, };
 
 static int reverse_diff;
+static int use_size_cache;
 
 static const char *external_diff(void)
 {
@@ -222,12 +223,60 @@ static int work_tree_matches(const char 
 	return 1;
 }
 
+static struct sha1_size_cache {
+	unsigned char sha1[20];
+	unsigned long size;
+} **sha1_size_cache;
+static int sha1_size_cache_nr, sha1_size_cache_alloc;
+
+static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
+						 unsigned long size)
+{
+	int first, last;
+	struct sha1_size_cache *e;
+
+	first = 0;
+	last = sha1_size_cache_nr;
+	while (last > first) {
+		int next = (last + first) >> 1;
+		e = sha1_size_cache[next];
+		int cmp = memcmp(e->sha1, sha1, 20);
+		if (!cmp)
+			return e;
+		if (cmp < 0) {
+			last = next;
+			continue;
+		}
+		first = next+1;
+	}
+	/* not found */
+	if (size == UINT_MAX)
+		return NULL;
+	/* insert to make it at "first" */
+	if (sha1_size_cache_alloc <= sha1_size_cache_nr) {
+		sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc);
+		sha1_size_cache = xrealloc(sha1_size_cache,
+					   sha1_size_cache_alloc *
+					   sizeof(*sha1_size_cache));
+	}
+	sha1_size_cache_nr++;
+	if (first < sha1_size_cache_nr)
+		memmove(sha1_size_cache + first + 1, sha1_size_cache + first,
+			(sha1_size_cache_nr - first - 1) *
+			sizeof(*sha1_size_cache));
+	e = xmalloc(sizeof(struct sha1_size_cache));
+	sha1_size_cache[first] = e;
+	memcpy(e->sha1, sha1, 20);
+	e->size = size;
+	return e;
+}
+
 /*
  * While doing rename detection and pickaxe operation, we may need to
  * grab the data for the blob (or file) for our own in-core comparison.
  * diff_filespec has data and size fields for this purpose.
  */
-int diff_populate_filespec(struct diff_filespec *s)
+int diff_populate_filespec(struct diff_filespec *s, int size_only)
 {
 	int err = 0;
 	if (!DIFF_FILE_VALID(s))
@@ -235,6 +284,9 @@ int diff_populate_filespec(struct diff_f
 	if (S_ISDIR(s->mode))
 		return -1;
 
+	if (!use_size_cache)
+		size_only = 0;
+
 	if (s->data)
 		return err;
 	if (!s->sha1_valid ||
@@ -254,6 +306,8 @@ int diff_populate_filespec(struct diff_f
 		s->size = st.st_size;
 		if (!s->size)
 			goto empty;
+		if (size_only)
+			return 0;
 		if (S_ISLNK(st.st_mode)) {
 			int ret;
 			s->data = xmalloc(s->size);
@@ -273,9 +327,21 @@ int diff_populate_filespec(struct diff_f
 		close(fd);
 	}
 	else {
+		/* We cannot do size only for SHA1 blobs */
 		char type[20];
+		struct sha1_size_cache *e;
+
+		if (size_only) {
+			e = locate_size_cache(s->sha1, UINT_MAX);
+			if (e) {
+				s->size = e->size;
+				return 0;
+			}
+		}
 		s->data = read_sha1_file(s->sha1, type, &s->size);
 		s->should_free = 1;
+		if (s->data && size_only)
+			locate_size_cache(s->sha1, s->size);
 	}
 	return 0;
 }
@@ -361,7 +427,7 @@ static void prepare_temp_file(const char
 		return;
 	}
 	else {
-		if (diff_populate_filespec(one))
+		if (diff_populate_filespec(one, 0))
 			die("cannot read data blob for %s", one->path);
 		prep_temp_blob(temp, one->data, one->size,
 			       one->sha1, one->mode);
@@ -496,6 +562,19 @@ void diff_setup(int flags)
 {
 	if (flags & DIFF_SETUP_REVERSE)
 		reverse_diff = 1;
+	if (flags & DIFF_SETUP_USE_CACHE) {
+		if (!active_cache)
+			/* read-cache does not die even when it fails
+			 * so it is safe for us to do this here.  Also
+			 * it does not smudge active_cache or active_nr
+			 * when it fails, so we do not have to worry about
+			 * cleaning it up oufselves either.
+			 */
+			read_cache();
+	}
+	if (flags & DIFF_SETUP_USE_SIZE_CACHE)
+		use_size_cache = 1;
+	
 }
 
 struct diff_queue_struct diff_queued_diff;
diff --git a/diff.h b/diff.h
--- a/diff.h
+++ b/diff.h
@@ -29,6 +29,8 @@ extern void diff_unmerge(const char *pat
 extern int diff_scoreopt_parse(const char *opt);
 
 #define DIFF_SETUP_REVERSE      	1
+#define DIFF_SETUP_USE_CACHE		2
+#define DIFF_SETUP_USE_SIZE_CACHE	4
 extern void diff_setup(int flags);
 
 #define DIFF_DETECT_RENAME	1
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -11,7 +11,7 @@ static int contains(struct diff_filespec
 {
 	unsigned long offset, sz;
 	const char *data;
-	if (diff_populate_filespec(one))
+	if (diff_populate_filespec(one, 0))
 		return 0;
 	sz = one->size;
 	data = one->data;
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -99,8 +99,11 @@ static int is_exact_match(struct diff_fi
 	if (src->sha1_valid && dst->sha1_valid &&
 	    !memcmp(src->sha1, dst->sha1, 20))
 		return 1;
-	if (diff_populate_filespec(src) || diff_populate_filespec(dst))
-		/* this is an error but will be caught downstream */
+	if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
+		return 0;
+	if (src->size != dst->size)
+		return 0;
+	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
 		return 0;
 	if (src->size == dst->size &&
 	    !memcmp(src->data, dst->data, src->size))
@@ -125,9 +128,11 @@ static int estimate_similarity(struct di
 	 * dst, and then some edit has been applied to dst.
 	 *
 	 * Compare them and return how similar they are, representing
-	 * the score as an integer between 0 and 10000, except
-	 * where they match exactly it is considered better than anything
-	 * else.
+	 * the score as an integer between 0 and MAX_SCORE.
+	 *
+	 * When there is an exact match, it is considered a better
+	 * match than anything else; the destination does not even
+	 * call into this function in that case.
 	 */
 	void *delta;
 	unsigned long delta_size, base_size;
@@ -147,6 +152,7 @@ static int estimate_similarity(struct di
 	/* We would not consider edits that change the file size so
 	 * drastically.  delta_size must be smaller than
 	 * (MAX_SCORE-minimum_score)/MAX_SCORE * min(src->size, dst->size).
+	 *
 	 * Note that base_size == 0 case is handled here already
 	 * and the final score computation below would not have a
 	 * divide-by-zero issue.
@@ -154,6 +160,9 @@ static int estimate_similarity(struct di
 	if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
 		return 0;
 
+	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
+		return 0; /* error but caught downstream */
+
 	delta = diff_delta(src->data, src->size,
 			   dst->data, dst->size,
 			   &delta_size);
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -33,7 +33,7 @@ extern struct diff_filespec *alloc_files
 extern void fill_filespec(struct diff_filespec *, const unsigned char *,
 			  unsigned short);
 
-extern int diff_populate_filespec(struct diff_filespec *);
+extern int diff_populate_filespec(struct diff_filespec *, int);
 extern void diff_free_filespec_data(struct diff_filespec *);
 
 struct diff_filepair {
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 00/12] Diff updates
  2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
                         ` (11 preceding siblings ...)
  2005-05-27 22:56       ` [PATCH 12/12] Optimize diff-tree -[CM] --stdin Junio C Hamano
@ 2005-05-27 23:03       ` Junio C Hamano
  2005-05-28 10:11         ` [PATCH] Do not show empty diff in diff-cache uncached Junio C Hamano
  12 siblings, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-05-27 23:03 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

I've said "I am done with diff" twice on this list already.
Aside from unevitable bug reports ;-) I think this time I am
done.  At least the major ones I wanted to do.

Except one thing.  What do you think about the current behaviour
of "diff-cache -p (uncached") in a work tree which was freshly
checked-out, unmodified but you "touch"ed some files to make
them stat-dirty?

I think ancient diff-cache did not report those files, but with
the new "diff --git" headers it will show the "diff --git"
header mentioning those files followed by no content nor mode
changes.  Admittedly this matches the diff-raw output behaviour
more closely, but I find it a bit distracting.  Do you care
about cleaning this up?



^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Do not show empty diff in diff-cache uncached.
  2005-05-27 23:03       ` [PATCH 00/12] Diff updates Junio C Hamano
@ 2005-05-28 10:11         ` Junio C Hamano
  2005-05-28 19:22           ` [PATCH] Diff: two fixes Junio C Hamano
  2005-05-29 18:53           ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
  0 siblings, 2 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-28 10:11 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Pre- "diff --git" built-in diff did not add any extended header
on its own, so it did not show anything for unmodified but
stat-dirty file from diff-cache command without --cached flag.

Recent diff-cache produces "diff --git" header internally before
calling the "diff" command, which results in an empty diff for
such a file, cluttering the output.  This patch fixes this.

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

diff.c            |   26 ++++++++++++++++++++++++++
diffcore-rename.c |   17 -----------------
diffcore.h        |    2 ++
3 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -684,6 +684,23 @@ int diff_unmodified_pair(struct diff_fil
 	return 0;
 }
 
+int is_exact_match(struct diff_filespec *src, struct diff_filespec *dst)
+{
+	if (src->sha1_valid && dst->sha1_valid &&
+	    !memcmp(src->sha1, dst->sha1, 20))
+		return 1;
+	if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
+		return 0;
+	if (src->size != dst->size)
+		return 0;
+	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
+		return 0;
+	if (src->size == dst->size &&
+	    !memcmp(src->data, dst->data, src->size))
+		return 1;
+	return 0;
+}
+
 static void diff_flush_patch(struct diff_filepair *p)
 {
 	const char *name, *other;
@@ -692,6 +709,15 @@ static void diff_flush_patch(struct diff
 	if (diff_unmodified_pair(p))
 		return;
 
+	/* When diff-raw would have said "look at the filesystem", we
+	 * need to see if the two are the same and if so not to emit
+	 * anything at all.  Avoid is_exact_match() comparison when it
+	 * does not matter.
+	 */
+	if ((DIFF_FILE_VALID(p->two) && !p->two->sha1_valid) &&
+	    is_exact_match(p->one, p->two))
+		return;
+
 	name = p->one->path;
 	other = (strcmp(name, p->two->path) ? p->two->path : NULL);
 	if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -94,23 +94,6 @@ static struct diff_rename_src *register_
 	return &(rename_src[first]);
 }
 
-static int is_exact_match(struct diff_filespec *src, struct diff_filespec *dst)
-{
-	if (src->sha1_valid && dst->sha1_valid &&
-	    !memcmp(src->sha1, dst->sha1, 20))
-		return 1;
-	if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
-		return 0;
-	if (src->size != dst->size)
-		return 0;
-	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
-		return 0;
-	if (src->size == dst->size &&
-	    !memcmp(src->data, dst->data, src->size))
-		return 1;
-	return 0;
-}
-
 struct diff_score {
 	int src; /* index in rename_src */
 	int dst; /* index in rename_dst */
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -29,6 +29,8 @@ struct diff_filespec {
 	unsigned should_munmap : 1; /* data should be munmap()'ed */
 };
 
+extern int is_exact_match(struct diff_filespec *, struct diff_filespec *);
+
 extern struct diff_filespec *alloc_filespec(const char *);
 extern void fill_filespec(struct diff_filespec *, const unsigned char *,
 			  unsigned short);
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] Diff: two fixes.
  2005-05-28 10:11         ` [PATCH] Do not show empty diff in diff-cache uncached Junio C Hamano
@ 2005-05-28 19:22           ` Junio C Hamano
  2005-05-29  4:20             ` [PATCH] diff-helper: fix R/C score parsing under -z flag Junio C Hamano
  2005-05-29 18:53           ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
  1 sibling, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-05-28 19:22 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

The count-delta routine sometimes overcounted the copied source
material which resulted in unsigned int wraparound.

The latest diff-cache (uncached) fix to eliminate empty diffs
from the output revealed that is_exact_match() was not careful
enough, which resulted in a sanity check routine triggering when
a file is added to an index.

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

count-delta.c |    3 +++
diff.c        |    3 +++
2 files changed, 6 insertions(+)

diff --git a/count-delta.c b/count-delta.c
--- a/count-delta.c
+++ b/count-delta.c
@@ -88,5 +88,8 @@ unsigned long count_delta(void *delta_bu
 	/* delete size is what was _not_ copied from source.
 	 * edit size is that and literal additions.
 	 */
+	if (src_size + added_literal < copied_from_source)
+		/* we ended up overcounting and underflowed */
+		return 0;
 	return (src_size - copied_from_source) + added_literal;
 }
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -689,6 +689,9 @@ int is_exact_match(struct diff_filespec 
 	if (src->sha1_valid && dst->sha1_valid &&
 	    !memcmp(src->sha1, dst->sha1, 20))
 		return 1;
+	/* if either is invalid they cannot match */
+	if (!DIFF_FILE_VALID(src) || !DIFF_FILE_VALID(dst))
+		return 0;
 	if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
 		return 0;
 	if (src->size != dst->size)
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] diff-helper: fix R/C score parsing under -z flag.
  2005-05-28 19:22           ` [PATCH] Diff: two fixes Junio C Hamano
@ 2005-05-29  4:20             ` Junio C Hamano
  2005-05-29  5:23               ` [PATCH] diff-cache: diff-patch (-p) format fixes Junio C Hamano
  0 siblings, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29  4:20 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

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

diff-helper.c |   17 ++++++++---------
1 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/diff-helper.c b/diff-helper.c
--- a/diff-helper.c
+++ b/diff-helper.c
@@ -80,17 +80,16 @@ int main(int ac, const char **av) {
 			if (!strchr("MCRNDU", status))
 				break;
 			two_paths = score = 0;
-			if (status == 'R' || status == 'C') {
+			if (status == 'R' || status == 'C')
 				two_paths = 1;
-				sscanf(cp, "%d", &score);
-				if (line_termination) {
-					cp = strchr(cp,
-						    inter_name_termination);
-					if (!cp)
-						break;
-				}
-			}
 
+			/* pick up score if exists */
+			if (sscanf(cp, "%d", &score) != 1)
+				score = 0;
+			cp = strchr(cp,
+				    inter_name_termination);
+			if (!cp)
+				break;
 			if (*cp++ != inter_name_termination)
 				break;
 
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] diff-cache: diff-patch (-p) format fixes.
  2005-05-29  4:20             ` [PATCH] diff-helper: fix R/C score parsing under -z flag Junio C Hamano
@ 2005-05-29  5:23               ` Junio C Hamano
  2005-05-29  9:10                 ` [PATCH] diff: code clean-up Junio C Hamano
  0 siblings, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29  5:23 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

This fixes two more bugs.

 - When diff-cache is run with -R and without --cached, a
   stat-dirty but otherwise unmodified file still produced an
   empty "diff --git" header (earlier fix was only for the case
   without -R).

 - When diff-cache is run without --cached, an unmodifed file
   that has different mode bits from the tree incorrectly picked
   up the mode bits from the filesystem, when it should have
   used what was recorded in the tree object.

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

diff.c |   26 ++++++++++++++++++++++----
1 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -183,6 +183,9 @@ void fill_filespec(struct diff_filespec 
  * Given a name and sha1 pair, if the dircache tells us the file in
  * the work tree has that object contents, return true, so that
  * prepare_temp_file() does not have to inflate and extract.
+ *
+ * NOTE: this function does not use the mode bits, so diff_filespec
+ * users must be careful about mode handling!
  */
 static int work_tree_matches(const char *name, const unsigned char *sha1)
 {
@@ -394,6 +397,10 @@ static void prepare_temp_file(const char
 
 	if (!one->sha1_valid ||
 	    work_tree_matches(name, one->sha1)) {
+		/* NOTE: we only say the matching file has the same
+		 * contents.  It may have a different mode, in which
+		 * case we should not pick it up from the filesystem!
+		 */
 		struct stat st;
 		if (lstat(name, &st) < 0) {
 			if (errno == ENOENT)
@@ -421,8 +428,15 @@ static void prepare_temp_file(const char
 				strcpy(temp->hex, sha1_to_hex(null_sha1));
 			else
 				strcpy(temp->hex, sha1_to_hex(one->sha1));
-			sprintf(temp->mode, "%06o",
-				S_IFREG |ce_permissions(st.st_mode));
+			/* even though we borrow the contents from the
+			 * work tree, we want our mode if we are not told
+			 * to look at the filesystem.
+			 */
+			if (one->sha1_valid)
+				sprintf(temp->mode, "%06o", one->mode);
+			else
+				sprintf(temp->mode, "%06o",
+					S_IFREG | ce_permissions(st.st_mode));
 		}
 		return;
 	}
@@ -716,9 +730,13 @@ static void diff_flush_patch(struct diff
 	 * need to see if the two are the same and if so not to emit
 	 * anything at all.  Avoid is_exact_match() comparison when it
 	 * does not matter.
+	 * Notice that "look at the filesystem" can happen on p->one
+	 * if we are operating under reverse-diff option.
 	 */
-	if ((DIFF_FILE_VALID(p->two) && !p->two->sha1_valid) &&
-	    is_exact_match(p->one, p->two))
+	if ( ((DIFF_FILE_VALID(p->one) && !p->one->sha1_valid) ||
+	      (DIFF_FILE_VALID(p->two) && !p->two->sha1_valid)) &&
+	     is_exact_match(p->one, p->two) &&
+	     (p->one->mode == p->two->mode) )
 		return;
 
 	name = p->one->path;
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] diff: code clean-up.
  2005-05-29  5:23               ` [PATCH] diff-cache: diff-patch (-p) format fixes Junio C Hamano
@ 2005-05-29  9:10                 ` Junio C Hamano
  0 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29  9:10 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

 - The previous fix for the "diff-cache -p mode bits problem"
   had an unnecessary conditional statement not to use the mode
   that came from the calling program when we get 0{40} SHA1.
   This was totally unnecessary, since the caller gives the
   valid mode bits even when calling us with 0{40} SHA1.
   This mistake did not break anything, since the mode bits we
   got ourselves were the same one taken from the filesystem,
   but it was doing unnecessary work.  This has been fixed.

 - DIFF_PAIR_RENAME() macro is introduced to distinguish a filepair
   that is a rename/copy (the definition of which is src and dst
   are different paths, of course).  This removes the hack used
   in the record_rename_pair() to always put some non-zero value
   in the score field.

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

diff.c            |   17 +++++++----------
diffcore-rename.c |    2 +-
diffcore.h        |    6 +++---
3 files changed, 11 insertions(+), 14 deletions(-)

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -429,14 +429,11 @@ static void prepare_temp_file(const char
 			else
 				strcpy(temp->hex, sha1_to_hex(one->sha1));
 			/* even though we borrow the contents from the
-			 * work tree, we want our mode if we are not told
-			 * to look at the filesystem.
+			 * work tree, we always want our mode.  mode is
+			 * trustworthy even when !(one->sha1_valid), as
+			 * long as DIFF_FILE_VALID(one).
 			 */
-			if (one->sha1_valid)
-				sprintf(temp->mode, "%06o", one->mode);
-			else
-				sprintf(temp->mode, "%06o",
-					S_IFREG | ce_permissions(st.st_mode));
+			sprintf(temp->mode, "%06o", one->mode);
 		}
 		return;
 	}
@@ -843,7 +840,7 @@ static void diff_resolve_rename_copy(voi
 			for (j = 0; j < q->nr; j++) {
 				pp = q->queue[j];
 				if (!strcmp(p->one->path, pp->one->path) &&
-				    pp->score) {
+				    DIFF_PAIR_RENAME(pp)) {
 					/* rename/copy are always valid
 					 * so we do not say DIFF_FILE_VALID()
 					 * on pp->one and pp->two.
@@ -862,7 +859,7 @@ static void diff_resolve_rename_copy(voi
 		 * whose both sides are valid and of the same type, i.e.
 		 * either in-place edit or rename/copy edit.
 		 */
-		else if (p->score) {
+		else if (DIFF_PAIR_RENAME(p)) {
 			if (p->source_stays) {
 				p->status = 'C';
 				continue;
@@ -875,7 +872,7 @@ static void diff_resolve_rename_copy(voi
 				pp = q->queue[j];
 				if (strcmp(pp->one->path, p->one->path))
 					continue; /* not us */
-				if (!pp->score)
+				if (!DIFF_PAIR_RENAME(pp))
 					continue; /* not a rename/copy */
 				/* pp is a rename/copy from the same source */
 				p->status = 'C';
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -190,7 +190,7 @@ static void record_rename_pair(struct di
 	fill_filespec(two, dst->sha1, dst->mode);
 
 	dp = diff_queue(renq, one, two);
-	dp->score = score ? : 1; /* make sure it is at least 1 */
+	dp->score = score;
 	dp->source_stays = rename_src[src_index].src_stays;
 	rename_dst[dst_index].pair = dp;
 }
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -42,15 +42,15 @@ extern void diff_free_filespec_data(stru
 struct diff_filepair {
 	struct diff_filespec *one;
 	struct diff_filespec *two;
-	unsigned short int score; /* only valid when one and two are
-				   * different paths
-				   */
+	unsigned short int score;
 	char source_stays; /* all of R/C are copies */
 	char status; /* M C R N D U (see Documentation/diff-format.txt) */
 };
 #define DIFF_PAIR_UNMERGED(p) \
 	(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
 
+#define DIFF_PAIR_RENAME(p) (strcmp((p)->one->path, (p)->two->path))
+
 #define DIFF_PAIR_TYPE_CHANGED(p) \
 	((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))
 
------------------------------------------------


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Do not show empty diff in diff-cache uncached.
  2005-05-28 10:11         ` [PATCH] Do not show empty diff in diff-cache uncached Junio C Hamano
  2005-05-28 19:22           ` [PATCH] Diff: two fixes Junio C Hamano
@ 2005-05-29 18:53           ` Linus Torvalds
  2005-05-29 20:09             ` Junio C Hamano
  2005-06-11 23:27             ` [PATCH] apply.c: tolerate diff from a dirty but unchanged path Junio C Hamano
  1 sibling, 2 replies; 45+ messages in thread
From: Linus Torvalds @ 2005-05-29 18:53 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List



On Sat, 28 May 2005, Junio C Hamano wrote:
>
> Pre- "diff --git" built-in diff did not add any extended header
> on its own, so it did not show anything for unmodified but
> stat-dirty file from diff-cache command without --cached flag.
> 
> Recent diff-cache produces "diff --git" header internally before
> calling the "diff" command, which results in an empty diff for
> such a file, cluttering the output.  This patch fixes this.

I'm not sure I like this.

I actually _expect_ that "git-diff-files" will show files that don't match 
the index, even if they happen to have the exact content that the index 
points to. It's how I know whether the index is up-to-date or not.

The exact same thing is trye of git-diff-cache. If something isn't 
up-to-date in the cache, you should show it, since certain operations 
depend on the cache being updated.

		Linus

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Do not show empty diff in diff-cache uncached.
  2005-05-29 18:53           ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
@ 2005-05-29 20:09             ` Junio C Hamano
  2005-05-29 21:52               ` Junio C Hamano
  2005-06-11 23:27             ` [PATCH] apply.c: tolerate diff from a dirty but unchanged path Junio C Hamano
  1 sibling, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29 20:09 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

LT> I'm not sure I like this.

LT> I actually _expect_ that "git-diff-files" will show files that don't match 
LT> the index, even if they happen to have the exact content that the index 
LT> points to. It's how I know whether the index is up-to-date or not.

LT> The exact same thing is trye of git-diff-cache. If something isn't 
LT> up-to-date in the cache, you should show it, since certain operations 
LT> depend on the cache being updated.

Let me make sure that we are on the same page.  I am only
talking about '-p' (diff-patch) case in this patch.  The code
should continue to show the 0{40} SHA1 on the right hand side in
diff-raw output.  Do you still want to see the header in that
case to match the diff-raw exactly?



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Do not show empty diff in diff-cache uncached.
  2005-05-29 20:09             ` Junio C Hamano
@ 2005-05-29 21:52               ` Junio C Hamano
  2005-05-29 23:41                 ` [PATCH 0/3] Leftover bits after 12-series Junio C Hamano
  2005-05-30  5:34                 ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
  0 siblings, 2 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29 21:52 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

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

Linus,

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

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

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


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 0/3] Leftover bits after 12-series
  2005-05-29 21:52               ` Junio C Hamano
@ 2005-05-29 23:41                 ` Junio C Hamano
  2005-05-29 23:54                   ` [PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag Junio C Hamano
                                     ` (3 more replies)
  2005-05-30  5:34                 ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
  1 sibling, 4 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29 23:41 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

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

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

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



^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag.
  2005-05-29 23:41                 ` [PATCH 0/3] Leftover bits after 12-series Junio C Hamano
@ 2005-05-29 23:54                   ` Junio C Hamano
  2005-05-29 23:56                   ` [PATCH 2/3] diff: consolidate various calls into diffcore Junio C Hamano
                                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29 23:54 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

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

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

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

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

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


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 2/3] diff: consolidate various calls into diffcore.
  2005-05-29 23:41                 ` [PATCH 0/3] Leftover bits after 12-series Junio C Hamano
  2005-05-29 23:54                   ` [PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag Junio C Hamano
@ 2005-05-29 23:56                   ` Junio C Hamano
  2005-05-29 23:56                   ` [PATCH 3/3] diff: code clean-up and removal of rename hack Junio C Hamano
  2005-05-30  6:58                   ` [PATCH 0/4] Junio C Hamano
  3 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29 23:56 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

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

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

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


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 3/3] diff: code clean-up and removal of rename hack.
  2005-05-29 23:41                 ` [PATCH 0/3] Leftover bits after 12-series Junio C Hamano
  2005-05-29 23:54                   ` [PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag Junio C Hamano
  2005-05-29 23:56                   ` [PATCH 2/3] diff: consolidate various calls into diffcore Junio C Hamano
@ 2005-05-29 23:56                   ` Junio C Hamano
  2005-05-30  6:58                   ` [PATCH 0/4] Junio C Hamano
  3 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-29 23:56 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

A new macro, DIFF_PAIR_RENAME(), is introduced to distinguish a
filepair that is a rename/copy (the definition of which is src
and dst are different paths, of course).  This removes the hack
used in the record_rename_pair() to always put a non-zero value
in the score field.

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

diff.c            |    6 +++---
diffcore-rename.c |    2 +-
diffcore.h        |    6 +++---
3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -796,7 +796,7 @@ static void diff_resolve_rename_copy(voi
 			for (j = 0; j < q->nr; j++) {
 				pp = q->queue[j];
 				if (!strcmp(p->one->path, pp->one->path) &&
-				    pp->score) {
+				    DIFF_PAIR_RENAME(pp)) {
 					/* rename/copy are always valid
 					 * so we do not say DIFF_FILE_VALID()
 					 * on pp->one and pp->two.
@@ -815,7 +815,7 @@ static void diff_resolve_rename_copy(voi
 		 * whose both sides are valid and of the same type, i.e.
 		 * either in-place edit or rename/copy edit.
 		 */
-		else if (p->score) {
+		else if (DIFF_PAIR_RENAME(p)) {
 			if (p->source_stays) {
 				p->status = 'C';
 				continue;
@@ -828,7 +828,7 @@ static void diff_resolve_rename_copy(voi
 				pp = q->queue[j];
 				if (strcmp(pp->one->path, p->one->path))
 					continue; /* not us */
-				if (!pp->score)
+				if (!DIFF_PAIR_RENAME(pp))
 					continue; /* not a rename/copy */
 				/* pp is a rename/copy from the same source */
 				p->status = 'C';
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -207,7 +207,7 @@ static void record_rename_pair(struct di
 	fill_filespec(two, dst->sha1, dst->mode);
 
 	dp = diff_queue(renq, one, two);
-	dp->score = score ? : 1; /* make sure it is at least 1 */
+	dp->score = score;
 	dp->source_stays = rename_src[src_index].src_stays;
 	rename_dst[dst_index].pair = dp;
 }
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -39,15 +39,15 @@ extern void diff_free_filespec_data(stru
 struct diff_filepair {
 	struct diff_filespec *one;
 	struct diff_filespec *two;
-	unsigned short int score; /* only valid when one and two are
-				   * different paths
-				   */
+	unsigned short int score;
 	char source_stays; /* all of R/C are copies */
 	char status; /* M C R N D U (see Documentation/diff-format.txt) */
 };
 #define DIFF_PAIR_UNMERGED(p) \
 	(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
 
+#define DIFF_PAIR_RENAME(p) (strcmp((p)->one->path, (p)->two->path))
+
 #define DIFF_PAIR_TYPE_CHANGED(p) \
 	((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))
 


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Do not show empty diff in diff-cache uncached.
  2005-05-29 21:52               ` Junio C Hamano
  2005-05-29 23:41                 ` [PATCH 0/3] Leftover bits after 12-series Junio C Hamano
@ 2005-05-30  5:34                 ` Linus Torvalds
  2005-05-30  5:53                   ` Junio C Hamano
  1 sibling, 1 reply; 45+ messages in thread
From: Linus Torvalds @ 2005-05-30  5:34 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List



On Sun, 29 May 2005, Junio C Hamano wrote:
> 
> Please disregard the patches you have already discarded so far;
> this request-to-discard includes -O and -B enhancements.

I actually like -B, it's just that that patch depended on -O and also came 
with a separate patch that was the reason I liked -B in the first place..

IO, if we have

	/* start out with files "a" and "b" */
	mv b c
	mv a b
	git-update-cache --add --remove a b c
	git-diff-cache -B HEAD

then I htink you're 100% right that it should show up as two renames in
a diffs, and "-B" would catch it. I think that's a great thing.

		Linus

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] Do not show empty diff in diff-cache uncached.
  2005-05-30  5:34                 ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
@ 2005-05-30  5:53                   ` Junio C Hamano
  0 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-30  5:53 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

LT> On Sun, 29 May 2005, Junio C Hamano wrote:
>> 
>> Please disregard the patches you have already discarded so far;
>> this request-to-discard includes -O and -B enhancements.

LT> I actually like -B, it's just that that patch depended on -O and also came 
LT> with a separate patch that was the reason I liked -B in the first place..

Good.  I actually just finished testing the complete reorder of
the patches and about to start the final review before throwing
the bundle at you again.  In the new set, -B comes before -O.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 0/4]
  2005-05-29 23:41                 ` [PATCH 0/3] Leftover bits after 12-series Junio C Hamano
                                     ` (2 preceding siblings ...)
  2005-05-29 23:56                   ` [PATCH 3/3] diff: code clean-up and removal of rename hack Junio C Hamano
@ 2005-05-30  6:58                   ` Junio C Hamano
  2005-05-30  7:07                     ` [PATCH 1/4] diff: further cleanup Junio C Hamano
                                       ` (3 more replies)
  3 siblings, 4 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-30  6:58 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Linus,

	as promised, I am sending a couple of further cleanup,
and a pair of new diffcore routines.

I am assuming that you would have applied the three clean-up
patches I sent after you rejected some of my 12-piece set,
before you use these four.  They come on top of the previous
three.  Although I do not think they depend on the previous
three, I am letting you know that that was how I tested these
four:

    [PATCH 1/4] diff: further cleanup.
    [PATCH 2/4] diff: fix the culling of unneeded delete record.
    [PATCH 3/4] Add -B flag to diff-* brothers.
    [PATCH 4/4] Add -O<orderfile> option to diff-* brothers.

The third one is the gem of this series.  I think I covered the
basics I can think of in the new test script, but there could
still be cases that rename/copy detector does something
interesting when broken pairs are involved.  Please give it a
good beating before you use it for anything important.  This
being diff routine, it obviously cannot corrupt your data,
though.

The fourth one was what both you and Petr expressed reluctance,
although Thomas was supportive.  I admit it is of "nice to have"
category not "great we need to have it inside" category, but it
is my favorite.

Oh, before I forget, I was wondering if you want me to mark
broken pair in any special way, just line I mark matched
rename/copy pairs.  Something along the lines of:

    diff --git a/foo b/foo
    dissimilarity index 100%
    deleted file mode 100644
    --- a/foo
    +++ /dev/null
    @@ ...
    diff --git a/foo b/foo
    dissimilarity index 100%
    new file mode 100644
    --- /dev/null
    +++ a/foo
    @@ ...



^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 1/4] diff: further cleanup.
  2005-05-30  6:58                   ` [PATCH 0/4] Junio C Hamano
@ 2005-05-30  7:07                     ` Junio C Hamano
  2005-05-30  7:08                     ` [PATCH 2/4] diff: fix the culling of unneeded delete record Junio C Hamano
                                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-30  7:07 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

When preparing data to feed the external diff, we should give
the mode we obtained from the caller, even when we are dealing
with a file with 0{40} SHA1 (i.e. the caller said "look at the
filesystem"), since the mode passed by the caller via
diff_addremove() or diff_change() is always trustworthy.

This is _not_ a bugfix --- the existing code stat() on the file
ifself and does the same computation on st.st_mode to compute
the mode the same way the caller did to give the original mode.
We cannot remove the stat() call from here, but the extra
computation to create the mode value is unnecessary.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 diff.c |    9 +++++++--
 1 files changed, 7 insertions(+), 2 deletions(-)

9c88ea020142346a56e36cea87e349814675f3f5 (from f96e0e2250f29f5ba2ae06c6b401a83fa1b828b4)
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -421,8 +421,13 @@ static void prepare_temp_file(const char
 				strcpy(temp->hex, sha1_to_hex(null_sha1));
 			else
 				strcpy(temp->hex, sha1_to_hex(one->sha1));
-			sprintf(temp->mode, "%06o",
-				S_IFREG |ce_permissions(st.st_mode));
+			/* Even though we may sometimes borrow the
+			 * contents from the work tree, we always want
+			 * one->mode.  mode is trustworthy even when
+			 * !(one->sha1_valid), as long as
+			 * DIFF_FILE_VALID(one).
+			 */
+			sprintf(temp->mode, "%06o", one->mode);
 		}
 		return;
 	}


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 2/4] diff: fix the culling of unneeded delete record.
  2005-05-30  6:58                   ` [PATCH 0/4] Junio C Hamano
  2005-05-30  7:07                     ` [PATCH 1/4] diff: further cleanup Junio C Hamano
@ 2005-05-30  7:08                     ` Junio C Hamano
  2005-05-30  7:08                     ` [PATCH 3/4] Add -B flag to diff-* brothers Junio C Hamano
  2005-05-30  7:09                     ` [PATCH 4/4] Add -O<orderfile> option " Junio C Hamano
  3 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-30  7:08 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

The commit 15d061b435a7e3b6bead39df3889f4af78c4b00a

    [PATCH] Fix the way diffcore-rename records unremoved source.

still leaves unneeded delete records in its output stream by
mistake, which was covered up by having an extra check to turn
such a delete into a no-op downstream.  Fix the check in the
diffcore-rename to simplify the output routine.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 diff.c            |   23 ++---------------------
 diffcore-rename.c |   44 +++++++++++++++++++++++++++++++++-----------
 2 files changed, 35 insertions(+), 32 deletions(-)

3fde726e95a828e680c95297f185d6d25fcf853a (from 9c88ea020142346a56e36cea87e349814675f3f5)
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -792,27 +792,8 @@ static void diff_resolve_rename_copy(voi
 			p->status = 'U';
 		else if (!DIFF_FILE_VALID(p->one))
 			p->status = 'N';
-		else if (!DIFF_FILE_VALID(p->two)) {
-			/* Deleted entry may have been picked up by
-			 * another rename-copy entry.  So we scan the
-			 * queue and if we find one that uses us as the
-			 * source we do not say delete for this entry.
-			 */
-			for (j = 0; j < q->nr; j++) {
-				pp = q->queue[j];
-				if (!strcmp(p->one->path, pp->one->path) &&
-				    DIFF_PAIR_RENAME(pp)) {
-					/* rename/copy are always valid
-					 * so we do not say DIFF_FILE_VALID()
-					 * on pp->one and pp->two.
-					 */
-					p->status = 'X';
-					break;
-				}
-			}
-			if (!p->status)
-				p->status = 'D';
-		}
+		else if (!DIFF_FILE_VALID(p->two))
+			p->status = 'D';
 		else if (DIFF_PAIR_TYPE_CHANGED(p))
 			p->status = 'T';
 
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -328,26 +328,48 @@ void diffcore_rename(int detect_rename, 
 	outq.nr = outq.alloc = 0;
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
-		struct diff_rename_dst *dst = locate_rename_dst(p->two, 0);
 		struct diff_filepair *pair_to_free = NULL;
 
-		if (dst) {
-			/* creation */
-			if (dst->pair) {
-				/* renq has rename/copy to produce
-				 * this file already, so we do not
-				 * emit the creation record in the
-				 * output.
-				 */
+		if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
+			/*
+			 * Creation
+			 *
+			 * We would output this create record if it has
+			 * not been turned into a rename/copy already.
+			 */
+			struct diff_rename_dst *dst =
+				locate_rename_dst(p->two, 0);
+			if (dst && dst->pair) {
 				diff_q(&outq, dst->pair);
 				pair_to_free = p;
 			}
 			else
-				/* no matching rename/copy source, so record
-				 * this as a creation.
+				/* no matching rename/copy source, so
+				 * record this as a creation.
 				 */
 				diff_q(&outq, p);
 		}
+		else if (DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two)) {
+			/*
+			 * Deletion
+			 *
+			 * We would output this delete record if renq
+			 * does not have a rename/copy to move
+			 * p->one->path out.
+			 */
+			for (j = 0; j < renq.nr; j++)
+				if (!strcmp(renq.queue[j]->one->path,
+					    p->one->path))
+					break;
+			if (j < renq.nr)
+				/* this path remains */
+				pair_to_free = p;
+
+			if (pair_to_free)
+				;
+			else
+				diff_q(&outq, p);
+		}
 		else if (!diff_unmodified_pair(p))
 			/* all the usual ones need to be kept */
 			diff_q(&outq, p);


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 3/4] Add -B flag to diff-* brothers.
  2005-05-30  6:58                   ` [PATCH 0/4] Junio C Hamano
  2005-05-30  7:07                     ` [PATCH 1/4] diff: further cleanup Junio C Hamano
  2005-05-30  7:08                     ` [PATCH 2/4] diff: fix the culling of unneeded delete record Junio C Hamano
@ 2005-05-30  7:08                     ` Junio C Hamano
  2005-05-30  7:09                     ` [PATCH 4/4] Add -O<orderfile> option " Junio C Hamano
  3 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-30  7:08 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

A new diffcore transformation, diffcore-break.c, is introduced.

When the -B flag is given, a patch that represents a complete
rewrite is broken into a deletion followed by a creation.  This
makes it easier to review such a complete rewrite patch.

The -B flag takes the same syntax as the -M and -C flags to
specify the minimum amount of non-source material the resulting
file needs to have to be considered a complete rewrite, and
defaults to 99% if not specified.

As the new test t4008-diff-break-rewrite.sh demonstrates, if a
file is a complete rewrite, it is broken into a delete/create
pair, which can further be subjected to the usual rename
detection if -M or -C is used.  For example, if file0 gets
completely rewritten to make it as if it were rather based on
file1 which itself disappeared, the following happens:

    The original change looks like this:

	file0     --> file0' (quite different from file0)
	file1     --> /dev/null

    After diffcore-break runs, it would become this:

	file0     --> /dev/null
	/dev/null --> file0'
	file1     --> /dev/null

    Then diffcore-rename matches them up:

	file1     --> file0'

The internal score values are finer grained now.  Earlier
maximum of 10000 has been raised to 60000; there is no user
visible changes but there is no reason to waste available bits.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-diff-cache.txt |    5 
 Documentation/git-diff-files.txt |    5 
 Documentation/git-diff-tree.txt  |    5 
 Makefile                         |    3 
 diff-cache.c                     |   11 +-
 diff-files.c                     |    8 +
 diff-tree.c                      |    8 +
 diff.c                           |   21 +++
 diff.h                           |    5 
 diffcore-break.c                 |  127 +++++++++++++++++++++++
 diffcore-rename.c                |   45 ++++++--
 diffcore.h                       |   12 +-
 t/t4008-diff-break-rewrite.sh    |  207 +++++++++++++++++++++++++++++++++++++++
 13 files changed, 433 insertions(+), 29 deletions(-)

96c0ba825059e65743bfce718e54413c4583ff35 (from 3fde726e95a828e680c95297f185d6d25fcf853a)
diff --git a/Documentation/git-diff-cache.txt b/Documentation/git-diff-cache.txt
--- a/Documentation/git-diff-cache.txt
+++ b/Documentation/git-diff-cache.txt
@@ -9,7 +9,7 @@ git-diff-cache - Compares content and mo
 
 SYNOPSIS
 --------
-'git-diff-cache' [-p] [-r] [-z] [-m] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [--cached] <tree-ish> [<path>...]
+'git-diff-cache' [-p] [-r] [-z] [-m] [-B] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [--cached] <tree-ish> [<path>...]
 
 DESCRIPTION
 -----------
@@ -35,6 +35,9 @@ OPTIONS
 -z::
 	\0 line termination on output
 
+-B::
+	Break complete rewrite changes into pairs of delete and create.
+
 -M::
 	Detect renames.
 
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -9,7 +9,7 @@ git-diff-files - Compares files in the w
 
 SYNOPSIS
 --------
-'git-diff-files' [-p] [-q] [-r] [-z] [-M] [-C] [-R] [-S<string>] [--pickaxe-all] [<pattern>...]
+'git-diff-files' [-p] [-q] [-r] [-z] [-B] [-M] [-C] [-R] [-S<string>] [--pickaxe-all] [<pattern>...]
 
 DESCRIPTION
 -----------
@@ -29,6 +29,9 @@ OPTIONS
 -R::
 	Output diff in reverse.
 
+-B::
+	Break complete rewrite changes into pairs of delete and create.
+
 -M::
 	Detect renames.
 
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -9,7 +9,7 @@ git-diff-tree - Compares the content and
 
 SYNOPSIS
 --------
-'git-diff-tree' [-p] [-r] [-z] [--stdin] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [-m] [-s] [-v] [-t] <tree-ish> <tree-ish> [<pattern>]\*
+'git-diff-tree' [-p] [-r] [-z] [--stdin] [-B] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [-m] [-s] [-v] [-t] <tree-ish> <tree-ish> [<pattern>]\*
 
 DESCRIPTION
 -----------
@@ -33,6 +33,9 @@ OPTIONS
 	generate patch (see section on generating patches).  For
 	git-diff-tree, this flag implies '-r' as well.
 
+-B::
+	Break complete rewrite changes into pairs of delete and create.
+
 -M::
 	Detect renames.
 
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,7 @@ LIB_OBJS += strbuf.o
 
 LIB_H += diff.h count-delta.h
 LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o diffcore-pathspec.o \
-	count-delta.o
+	count-delta.o diffcore-break.o
 
 LIB_OBJS += gitenv.o
 
@@ -130,6 +130,7 @@ diff.o: $(LIB_H) diffcore.h
 diffcore-rename.o : $(LIB_H) diffcore.h
 diffcore-pathspec.o : $(LIB_H) diffcore.h
 diffcore-pickaxe.o : $(LIB_H) diffcore.h
+diffcore-break.o : $(LIB_H) diffcore.h
 
 test: all
 	$(MAKE) -C t/ all
diff --git a/diff-cache.c b/diff-cache.c
--- a/diff-cache.c
+++ b/diff-cache.c
@@ -9,6 +9,7 @@ static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 static int pickaxe_opts = 0;
+static int diff_break_opt = -1;
 
 /* A file entry went away or appeared */
 static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)
@@ -188,6 +189,10 @@ int main(int argc, const char **argv)
 			diff_output_format = DIFF_FORMAT_PATCH;
 			continue;
 		}
+		if (!strncmp(arg, "-B", 2)) {
+			diff_break_opt = diff_scoreopt_parse(arg);
+			continue;
+		}
 		if (!strncmp(arg, "-M", 2)) {
 			detect_rename = DIFF_DETECT_RENAME;
 			diff_score_opt = diff_scoreopt_parse(arg);
@@ -240,9 +245,11 @@ int main(int argc, const char **argv)
 		die("unable to read tree object %s", tree_name);
 
 	ret = diff_cache(active_cache, active_nr);
-	diffcore_std(pathspec,
+
+	diffcore_std(pathspec ? : NULL,
 		     detect_rename, diff_score_opt,
-		     pickaxe, pickaxe_opts);
+		     pickaxe, pickaxe_opts,
+		     diff_break_opt);
 	diff_flush(diff_output_format, 1);
 	return ret;
 }
diff --git a/diff-files.c b/diff-files.c
--- a/diff-files.c
+++ b/diff-files.c
@@ -15,6 +15,7 @@ static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 static int pickaxe_opts = 0;
+static int diff_break_opt = -1;
 static int silent = 0;
 
 static void show_unmerge(const char *path)
@@ -57,6 +58,8 @@ int main(int argc, const char **argv)
 			pickaxe = argv[1] + 2;
 		else if (!strcmp(argv[1], "--pickaxe-all"))
 			pickaxe_opts = DIFF_PICKAXE_ALL;
+		else if (!strncmp(argv[1], "-B", 2))
+			diff_break_opt = diff_scoreopt_parse(argv[1]);
 		else if (!strncmp(argv[1], "-M", 2)) {
 			diff_score_opt = diff_scoreopt_parse(argv[1]);
 			detect_rename = DIFF_DETECT_RENAME;
@@ -116,9 +119,10 @@ int main(int argc, const char **argv)
 		show_modified(oldmode, mode, ce->sha1, null_sha1,
 			      ce->name);
 	}
-	diffcore_std(argv + 1,
+	diffcore_std((1 < argc) ? argv + 1 : NULL,
 		     detect_rename, diff_score_opt,
-		     pickaxe, pickaxe_opts);
+		     pickaxe, pickaxe_opts,
+		     diff_break_opt);
 	diff_flush(diff_output_format, 1);
 	return 0;
 }
diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -14,6 +14,7 @@ static int diff_setup_opt = 0;
 static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 static int pickaxe_opts = 0;
+static int diff_break_opt = -1;
 static const char *header = NULL;
 static const char *header_prefix = "";
 
@@ -263,7 +264,8 @@ static int call_diff_flush(void)
 {
 	diffcore_std(0,
 		     detect_rename, diff_score_opt,
-		     pickaxe, pickaxe_opts);
+		     pickaxe, pickaxe_opts,
+		     diff_break_opt);
 	if (diff_queue_is_empty()) {
 		diff_flush(DIFF_FORMAT_NO_OUTPUT, 0);
 		return 0;
@@ -523,6 +525,10 @@ int main(int argc, const char **argv)
 			diff_score_opt = diff_scoreopt_parse(arg);
 			continue;
 		}
+		if (!strncmp(arg, "-B", 2)) {
+			diff_break_opt = diff_scoreopt_parse(arg);
+			continue;
+		}
 		if (!strcmp(arg, "-z")) {
 			diff_output_format = DIFF_FORMAT_MACHINE;
 			continue;
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -603,6 +603,7 @@ struct diff_filepair *diff_queue(struct 
 	dp->two = two;
 	dp->score = 0;
 	dp->source_stays = 0;
+	dp->broken_pair = 0;
 	diff_q(queue, dp);
 	return dp;
 }
@@ -637,6 +638,16 @@ static void diff_flush_raw(struct diff_f
 		sprintf(status, "%c%03d", p->status,
 			(int)(0.5 + p->score * 100.0/MAX_SCORE));
 		break;
+	case 'N': case 'D':
+		two_paths = 0;
+		if (p->score)
+			sprintf(status, "%c%03d", p->status,
+				(int)(0.5 + p->score * 100.0/MAX_SCORE));
+		else {
+			status[0] = p->status;
+			status[1] = 0;
+		}
+		break;
 	default:
 		two_paths = 0;
 		status[0] = p->status;
@@ -760,8 +771,9 @@ void diff_debug_filepair(const struct di
 {
 	diff_debug_filespec(p->one, i, "one");
 	diff_debug_filespec(p->two, i, "two");
-	fprintf(stderr, "score %d, status %c source_stays %d\n",
-		p->score, p->status ? : '?', p->source_stays);
+	fprintf(stderr, "score %d, status %c stays %d broken %d\n",
+		p->score, p->status ? : '?',
+		p->source_stays, p->broken_pair);
 }
 
 void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
@@ -875,10 +887,13 @@ void diff_flush(int diff_output_style, i
 
 void diffcore_std(const char **paths,
 		  int detect_rename, int rename_score,
-		  const char *pickaxe, int pickaxe_opts)
+		  const char *pickaxe, int pickaxe_opts,
+		  int break_opt)
 {
 	if (paths && paths[0])
 		diffcore_pathspec(paths);
+	if (0 <= break_opt)
+		diffcore_break(break_opt);
 	if (detect_rename)
 		diffcore_rename(detect_rename, rename_score);
 	if (pickaxe)
diff --git a/diff.h b/diff.h
--- a/diff.h
+++ b/diff.h
@@ -43,9 +43,12 @@ extern void diffcore_pickaxe(const char 
 
 extern void diffcore_pathspec(const char **pathspec);
 
+extern void diffcore_break(int);
+
 extern void diffcore_std(const char **paths,
 			 int detect_rename, int rename_score,
-			 const char *pickaxe, int pickaxe_opts);
+			 const char *pickaxe, int pickaxe_opts,
+			 int break_opt);
 
 extern int diff_queue_is_empty(void);
 
diff --git a/diffcore-break.c b/diffcore-break.c
new file mode 100644
--- /dev/null
+++ b/diffcore-break.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2005 Junio C Hamano
+ */
+#include "cache.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "delta.h"
+#include "count-delta.h"
+
+static int very_different(struct diff_filespec *src,
+			  struct diff_filespec *dst,
+			  int min_score)
+{
+	/* dst is recorded as a modification of src.  Are they so
+	 * different that we are better off recording this as a pair
+	 * of delete and create?  min_score is the minimum amount of
+	 * new material that must exist in the dst and not in src for
+	 * the pair to be considered a complete rewrite, and recommended
+	 * to be set to a very high value, 99% or so.
+	 *
+	 * The value we return represents the amount of new material
+	 * that is in dst and not in src.  We return 0 when we do not
+	 * want to get the filepair broken.
+	 */
+	void *delta;
+	unsigned long delta_size, base_size;
+
+	if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
+		return 0; /* leave symlink rename alone */
+
+	if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
+		return 0; /* error but caught downstream */
+
+	delta_size = ((src->size < dst->size) ?
+		      (dst->size - src->size) : (src->size - dst->size));
+
+	/* Notice that we use max of src and dst as the base size,
+	 * unlike rename similarity detection.  This is so that we do
+	 * not mistake a large addition as a complete rewrite.
+	 */
+	base_size = ((src->size < dst->size) ? dst->size : src->size);
+
+	/*
+	 * If file size difference is too big compared to the
+	 * base_size, we declare this a complete rewrite.
+	 */
+	if (base_size * min_score < delta_size * MAX_SCORE)
+		return MAX_SCORE;
+
+	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
+		return 0; /* error but caught downstream */
+
+	delta = diff_delta(src->data, src->size,
+			   dst->data, dst->size,
+			   &delta_size);
+
+	/* A delta that has a lot of literal additions would have
+	 * big delta_size no matter what else it does.
+	 */
+	if (base_size * min_score < delta_size * MAX_SCORE)
+		return MAX_SCORE;
+
+	/* Estimate the edit size by interpreting delta. */
+	delta_size = count_delta(delta, delta_size);
+	free(delta);
+	if (delta_size == UINT_MAX)
+		return 0; /* error in delta computation */
+
+	if (base_size < delta_size)
+		return MAX_SCORE;
+
+	return delta_size * MAX_SCORE / base_size; 
+}
+
+void diffcore_break(int min_score)
+{
+	struct diff_queue_struct *q = &diff_queued_diff;
+	struct diff_queue_struct outq;
+	int i;
+
+	if (!min_score)
+		min_score = DEFAULT_BREAK_SCORE;
+
+	outq.nr = outq.alloc = 0;
+	outq.queue = NULL;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		int score;
+
+		/* We deal only with in-place edit of non directory.
+		 * We do not break anything else.
+		 */
+		if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two) &&
+		    !S_ISDIR(p->one->mode) && !S_ISDIR(p->two->mode) &&
+		    !strcmp(p->one->path, p->two->path)) {
+			score = very_different(p->one, p->two, min_score);
+			if (min_score <= score) {
+				/* Split this into delete and create */
+				struct diff_filespec *null_one, *null_two;
+				struct diff_filepair *dp;
+
+				/* deletion of one */
+				null_one = alloc_filespec(p->one->path);
+				dp = diff_queue(&outq, p->one, null_one);
+				dp->score = score;
+				dp->broken_pair = 1;
+
+				/* creation of two */
+				null_two = alloc_filespec(p->two->path);
+				dp = diff_queue(&outq, null_two, p->two);
+				dp->score = score;
+				dp->broken_pair = 1;
+
+				free(p); /* not diff_free_filepair(), we are
+					  * reusing one and two here.
+					  */
+				continue;
+			}
+		}
+		diff_q(&outq, p);
+	}
+	free(q->queue);
+	*q = outq;
+
+	return;
+}
diff --git a/diffcore-rename.c b/diffcore-rename.c
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -225,8 +225,8 @@ static int score_compare(const void *a_,
 int diff_scoreopt_parse(const char *opt)
 {
 	int diglen, num, scale, i;
-	if (opt[0] != '-' || (opt[1] != 'M' && opt[1] != 'C'))
-		return -1; /* that is not a -M nor -C option */
+	if (opt[0] != '-' || (opt[1] != 'M' && opt[1] != 'C' && opt[1] != 'B'))
+		return -1; /* that is not a -M, -C nor -B option */
 	diglen = strspn(opt+2, "0123456789");
 	if (diglen == 0 || strlen(opt+2) != diglen)
 		return 0; /* use default */
@@ -249,7 +249,7 @@ void diffcore_rename(int detect_rename, 
 	int num_create, num_src, dst_cnt;
 
 	if (!minimum_score)
-		minimum_score = DEFAULT_MINIMUM_SCORE;
+		minimum_score = DEFAULT_RENAME_SCORE;
 	renq.queue = NULL;
 	renq.nr = renq.alloc = 0;
 
@@ -353,17 +353,36 @@ void diffcore_rename(int detect_rename, 
 			/*
 			 * Deletion
 			 *
-			 * We would output this delete record if renq
-			 * does not have a rename/copy to move
-			 * p->one->path out.
+			 * We would output this delete record if:
+			 *
+			 * (1) this is a broken delete and the counterpart
+			 *     broken create remains in the output; or
+			 * (2) this is not a broken delete, and renq does
+			 *     not have a rename/copy to move p->one->path
+			 *     out.
+			 *
+			 * Otherwise, the counterpart broken create
+			 * has been turned into a rename-edit; or
+			 * delete did not have a matching create to
+			 * begin with.
 			 */
-			for (j = 0; j < renq.nr; j++)
-				if (!strcmp(renq.queue[j]->one->path,
-					    p->one->path))
-					break;
-			if (j < renq.nr)
-				/* this path remains */
-				pair_to_free = p;
+			if (DIFF_PAIR_BROKEN(p)) {
+				/* broken delete */
+				struct diff_rename_dst *dst =
+					locate_rename_dst(p->one, 0);
+				if (dst && dst->pair)
+					/* counterpart is now rename/copy */
+					pair_to_free = p;
+			}
+			else {
+				for (j = 0; j < renq.nr; j++)
+					if (!strcmp(renq.queue[j]->one->path,
+						    p->one->path))
+						break;
+				if (j < renq.nr)
+					/* this path remains */
+					pair_to_free = p;
+			}
 
 			if (pair_to_free)
 				;
diff --git a/diffcore.h b/diffcore.h
--- a/diffcore.h
+++ b/diffcore.h
@@ -8,8 +8,9 @@
  * (e.g. diffcore-rename, diffcore-pickaxe).  Never include this header
  * in anything else.
  */
-#define MAX_SCORE 10000
-#define DEFAULT_MINIMUM_SCORE 5000
+#define MAX_SCORE 60000
+#define DEFAULT_RENAME_SCORE 30000 /* rename/copy similarity minimum (50%) */
+#define DEFAULT_BREAK_SCORE  59400 /* minimum for break to happen (99%)*/
 
 #define RENAME_DST_MATCHED 01
 
@@ -40,14 +41,19 @@ struct diff_filepair {
 	struct diff_filespec *one;
 	struct diff_filespec *two;
 	unsigned short int score;
-	char source_stays; /* all of R/C are copies */
 	char status; /* M C R N D U (see Documentation/diff-format.txt) */
+	unsigned source_stays : 1; /* all of R/C are copies */
+	unsigned broken_pair : 1;
 };
 #define DIFF_PAIR_UNMERGED(p) \
 	(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
 
 #define DIFF_PAIR_RENAME(p) (strcmp((p)->one->path, (p)->two->path))
 
+#define DIFF_PAIR_BROKEN(p) \
+	( (!DIFF_FILE_VALID((p)->one) != !DIFF_FILE_VALID((p)->two)) && \
+	  ((p)->broken_pair != 0) )
+
 #define DIFF_PAIR_TYPE_CHANGED(p) \
 	((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))
 
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
new file mode 100755
--- /dev/null
+++ b/t/t4008-diff-break-rewrite.sh
@@ -0,0 +1,207 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Break and then rename
+
+We have two very different files, file0 and file1, registered in a tree.
+
+We update file1 so drastically that it is more similar to file0, and
+then remove file0.  With -B, changes to file1 should be broken into
+separate delete and create, resulting in removal of file0, removal of
+original file1 and creation of completely rewritten file1.
+
+Further, with -B and -M together, these three modifications should
+turn into rename-edit of file0 into file1.
+
+Starting from the same two files in the tree, we swap file0 and file1.
+With -B, this should be detected as two complete rewrites, resulting in
+four changes in total.
+
+Further, with -B and -M together, these should turn into two renames.
+'
+. ./test-lib.sh
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+sanitize_diff_raw='s/ '"$_x40"' '"$_x40"' \([CDNR]\)[0-9]*	/ X X \1#	/'
+compare_diff_raw () {
+    # When heuristics are improved, the score numbers would change.
+    # Ignore them while comparing.
+    # Also we do not check SHA1 hash generation in this test, which
+    # is a job for t0000-basic.sh
+
+    sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
+    sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
+    diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+test_expect_success \
+    setup \
+    'cat ../../README >file0 &&
+     cat ../../COPYING >file1 &&
+    git-update-cache --add file0 file1 &&
+    tree=$(git-write-tree) &&
+    echo "$tree"'
+
+test_expect_success \
+    'change file1 with copy-edit of file0 and remove file0' \
+    'sed -e "s/git/GIT/" file0 >file1 &&
+     rm -f file0 &&
+    git-update-cache --remove file0 file1'
+
+test_expect_success \
+    'run diff with -B' \
+    'git-diff-cache -B --cached "$tree" >current'
+
+cat >expected <<\EOF
+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D	file0
+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100	file1
+:000000 100644 0000000000000000000000000000000000000000 11e331465a89c394dc25c780de230043750c1ec8 N100	file1
+EOF
+
+test_expect_success \
+    'validate result of -B (#1)' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'run diff with -B and -M' \
+    'git-diff-cache -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c R100	file0	file1
+EOF
+
+test_expect_success \
+    'validate result of -B -M (#2)' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'swap file0 and file1' \
+    'rm -f file0 file1 &&
+     git-read-tree -m $tree &&
+     git-checkout-cache -f -u -a &&
+     mv file0 tmp &&
+     mv file1 file0 &&
+     mv tmp file1 &&
+     git-update-cache file0 file1'
+
+test_expect_success \
+    'run diff with -B' \
+    'git-diff-cache -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D100	file0
+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 N100	file0
+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100	file1
+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 N100	file1
+EOF
+
+test_expect_success \
+    'validate result of -B (#3)' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'run diff with -B and -M' \
+    'git-diff-cache -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100	file1	file0
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R100	file0	file1
+EOF
+
+test_expect_success \
+    'validate result of -B -M (#4)' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'make file0 into something completely different' \
+    'rm -f file0 &&
+     ln -s frotz file0 &&
+     git-update-cache file0 file1'
+
+test_expect_success \
+    'run diff with -B' \
+    'git-diff-cache -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T	file0
+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100	file1
+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 N100	file1
+EOF
+
+test_expect_success \
+    'validate result of -B (#5)' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'run diff with -B' \
+    'git-diff-cache -B -M "$tree" >current'
+
+# This should not mistake file0 as the copy source of new file1
+# due to type differences.
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T	file0
+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100	file1
+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 N100	file1
+EOF
+
+test_expect_success \
+    'validate result of -B -M (#6)' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'run diff with -M' \
+    'git-diff-cache -M "$tree" >current'
+
+# This should not mistake file0 as the copy source of new file1
+# due to type differences.
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T	file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M	file1
+EOF
+
+test_expect_success \
+    'validate result of -M (#7)' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'file1 edited to look like file0 and file0 rename-edited to file2' \
+    'rm -f file0 file1 &&
+     git-read-tree -m $tree &&
+     git-checkout-cache -f -u -a &&
+     sed -e "s/git/GIT/" file0 >file1 &&
+     sed -e "s/git/GET/" file0 >file2 &&
+     rm -f file0
+     git-update-cache --add --remove file0 file1 file2'
+
+test_expect_success \
+    'run diff with -B' \
+    'git-diff-cache -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D	file0
+:100644 000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0000000000000000000000000000000000000000 D100	file1
+:000000 100644 0000000000000000000000000000000000000000 08bb2fb671deff4c03a4d4a0a1315dff98d5732c N100	file1
+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 N	file2
+EOF
+
+test_expect_success \
+    'validate result of -B (#8)' \
+    'compare_diff_raw current expected'
+
+test_expect_success \
+    'run diff with -B -M' \
+    'git-diff-cache -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c C095	file0	file1
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 59f832e5c8b3f7e486be15ad0cd3e95ba9af8998 R095	file0	file2
+EOF
+
+test_expect_success \
+    'validate result of -B -M (#9)' \
+    'compare_diff_raw current expected'
+
+test_done


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 4/4] Add -O<orderfile> option to diff-* brothers.
  2005-05-30  6:58                   ` [PATCH 0/4] Junio C Hamano
                                       ` (2 preceding siblings ...)
  2005-05-30  7:08                     ` [PATCH 3/4] Add -B flag to diff-* brothers Junio C Hamano
@ 2005-05-30  7:09                     ` Junio C Hamano
  3 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-05-30  7:09 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

A new diffcore filter diffcore-order is introduced.  This takes
a text file each of whose line is a shell glob pattern.  Patches
that match a glob pattern on an earlier line in the file are
output before patches that match a later line, and patches that
do not match any glob pattern are output last.

A typical orderfile for git project probably should look like
this:

    README
    Makefile
    Documentation
    *.h
    *.c

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-diff-cache.txt |    6 +
 Documentation/git-diff-files.txt |    6 +
 Documentation/git-diff-tree.txt  |    6 +
 Makefile                         |    3 
 diff-cache.c                     |    8 ++
 diff-files.c                     |    6 +
 diff-tree.c                      |    8 ++
 diff.c                           |    5 +
 diff.h                           |    7 +-
 diffcore-order.c                 |  122 +++++++++++++++++++++++++++++++++++++++
 10 files changed, 167 insertions(+), 10 deletions(-)

6575b8d66ea48891aaba3fba3eb8a4fddc596c2c (from 96c0ba825059e65743bfce718e54413c4583ff35)
diff --git a/Documentation/git-diff-cache.txt b/Documentation/git-diff-cache.txt
--- a/Documentation/git-diff-cache.txt
+++ b/Documentation/git-diff-cache.txt
@@ -9,7 +9,7 @@ git-diff-cache - Compares content and mo
 
 SYNOPSIS
 --------
-'git-diff-cache' [-p] [-r] [-z] [-m] [-B] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [--cached] <tree-ish> [<path>...]
+'git-diff-cache' [-p] [-r] [-z] [-m] [-B] [-M] [-R] [-C] [-O<orderfile>] [-S<string>] [--pickaxe-all] [--cached] <tree-ish> [<path>...]
 
 DESCRIPTION
 -----------
@@ -52,6 +52,10 @@ OPTIONS
 	changeset, not just the files that contains the change
 	in <string>.
 
+-O<orderfile>::
+	Output the patch in the order specified in the
+	<orderfile>, which has one shell glob pattern per line.
+
 -R::
 	Output diff in reverse.
 
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -9,7 +9,7 @@ git-diff-files - Compares files in the w
 
 SYNOPSIS
 --------
-'git-diff-files' [-p] [-q] [-r] [-z] [-B] [-M] [-C] [-R] [-S<string>] [--pickaxe-all] [<pattern>...]
+'git-diff-files' [-p] [-q] [-r] [-z] [-B] [-M] [-C] [-R] [-O<orderfile>] [-S<string>] [--pickaxe-all] [<pattern>...]
 
 DESCRIPTION
 -----------
@@ -46,6 +46,10 @@ OPTIONS
 	changeset, not just the files that contains the change
 	in <string>.
 
+-O<orderfile>::
+	Output the patch in the order specified in the
+	<orderfile>, which has one shell glob pattern per line.
+
 -r::
 	This flag does not mean anything.  It is there only to match
 	git-diff-tree.  Unlike git-diff-tree, git-diff-files always looks
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -9,7 +9,7 @@ git-diff-tree - Compares the content and
 
 SYNOPSIS
 --------
-'git-diff-tree' [-p] [-r] [-z] [--stdin] [-B] [-M] [-R] [-C] [-S<string>] [--pickaxe-all] [-m] [-s] [-v] [-t] <tree-ish> <tree-ish> [<pattern>]\*
+'git-diff-tree' [-p] [-r] [-z] [--stdin] [-B] [-M] [-R] [-C] [-O<orderfile>] [-S<string>] [--pickaxe-all] [-m] [-s] [-v] [-t] <tree-ish> <tree-ish> [<pattern>]\*
 
 DESCRIPTION
 -----------
@@ -53,6 +53,10 @@ OPTIONS
 	changeset, not just the files that contains the change
 	in <string>.
 
+-O<orderfile>::
+	Output the patch in the order specified in the
+	<orderfile>, which has one shell glob pattern per line.
+
 -r::
 	recurse
 
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,7 @@ LIB_OBJS += strbuf.o
 
 LIB_H += diff.h count-delta.h
 LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o diffcore-pathspec.o \
-	count-delta.o diffcore-break.o
+	count-delta.o diffcore-break.o diffcore-order.o
 
 LIB_OBJS += gitenv.o
 
@@ -131,6 +131,7 @@ diffcore-rename.o : $(LIB_H) diffcore.h
 diffcore-pathspec.o : $(LIB_H) diffcore.h
 diffcore-pickaxe.o : $(LIB_H) diffcore.h
 diffcore-break.o : $(LIB_H) diffcore.h
+diffcore-order.o : $(LIB_H) diffcore.h
 
 test: all
 	$(MAKE) -C t/ all
diff --git a/diff-cache.c b/diff-cache.c
--- a/diff-cache.c
+++ b/diff-cache.c
@@ -10,6 +10,7 @@ static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 static int pickaxe_opts = 0;
 static int diff_break_opt = -1;
+static const char *orderfile = NULL;
 
 /* A file entry went away or appeared */
 static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)
@@ -215,6 +216,10 @@ int main(int argc, const char **argv)
 			pickaxe = arg + 2;
 			continue;
 		}
+		if (!strncmp(arg, "-O", 2)) {
+			orderfile = arg + 2;
+			continue;
+		}
 		if (!strcmp(arg, "--pickaxe-all")) {
 			pickaxe_opts = DIFF_PICKAXE_ALL;
 			continue;
@@ -249,7 +254,8 @@ int main(int argc, const char **argv)
 	diffcore_std(pathspec ? : NULL,
 		     detect_rename, diff_score_opt,
 		     pickaxe, pickaxe_opts,
-		     diff_break_opt);
+		     diff_break_opt,
+		     orderfile);
 	diff_flush(diff_output_format, 1);
 	return ret;
 }
diff --git a/diff-files.c b/diff-files.c
--- a/diff-files.c
+++ b/diff-files.c
@@ -16,6 +16,7 @@ static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 static int pickaxe_opts = 0;
 static int diff_break_opt = -1;
+static const char *orderfile = NULL;
 static int silent = 0;
 
 static void show_unmerge(const char *path)
@@ -56,6 +57,8 @@ int main(int argc, const char **argv)
 			diff_setup_opt |= DIFF_SETUP_REVERSE;
 		else if (!strncmp(argv[1], "-S", 2))
 			pickaxe = argv[1] + 2;
+		else if (!strncmp(argv[1], "-O", 2))
+			orderfile = argv[1] + 2;
 		else if (!strcmp(argv[1], "--pickaxe-all"))
 			pickaxe_opts = DIFF_PICKAXE_ALL;
 		else if (!strncmp(argv[1], "-B", 2))
@@ -122,7 +125,8 @@ int main(int argc, const char **argv)
 	diffcore_std((1 < argc) ? argv + 1 : NULL,
 		     detect_rename, diff_score_opt,
 		     pickaxe, pickaxe_opts,
-		     diff_break_opt);
+		     diff_break_opt,
+		     orderfile);
 	diff_flush(diff_output_format, 1);
 	return 0;
 }
diff --git a/diff-tree.c b/diff-tree.c
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -15,6 +15,7 @@ static int diff_score_opt = 0;
 static const char *pickaxe = NULL;
 static int pickaxe_opts = 0;
 static int diff_break_opt = -1;
+static const char *orderfile = NULL;
 static const char *header = NULL;
 static const char *header_prefix = "";
 
@@ -265,7 +266,8 @@ static int call_diff_flush(void)
 	diffcore_std(0,
 		     detect_rename, diff_score_opt,
 		     pickaxe, pickaxe_opts,
-		     diff_break_opt);
+		     diff_break_opt,
+		     orderfile);
 	if (diff_queue_is_empty()) {
 		diff_flush(DIFF_FORMAT_NO_OUTPUT, 0);
 		return 0;
@@ -511,6 +513,10 @@ int main(int argc, const char **argv)
 			pickaxe = arg + 2;
 			continue;
 		}
+		if (!strncmp(arg, "-O", 2)) {
+			orderfile = arg + 2;
+			continue;
+		}
 		if (!strcmp(arg, "--pickaxe-all")) {
 			pickaxe_opts = DIFF_PICKAXE_ALL;
 			continue;
diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -888,7 +888,8 @@ void diff_flush(int diff_output_style, i
 void diffcore_std(const char **paths,
 		  int detect_rename, int rename_score,
 		  const char *pickaxe, int pickaxe_opts,
-		  int break_opt)
+		  int break_opt,
+		  const char *orderfile)
 {
 	if (paths && paths[0])
 		diffcore_pathspec(paths);
@@ -898,6 +899,8 @@ void diffcore_std(const char **paths,
 		diffcore_rename(detect_rename, rename_score);
 	if (pickaxe)
 		diffcore_pickaxe(pickaxe, pickaxe_opts);
+	if (orderfile)
+		diffcore_order(orderfile);
 }
 
 void diff_addremove(int addremove, unsigned mode,
diff --git a/diff.h b/diff.h
--- a/diff.h
+++ b/diff.h
@@ -43,12 +43,15 @@ extern void diffcore_pickaxe(const char 
 
 extern void diffcore_pathspec(const char **pathspec);
 
-extern void diffcore_break(int);
+extern void diffcore_order(const char *orderfile);
+
+extern void diffcore_break(int max_score);
 
 extern void diffcore_std(const char **paths,
 			 int detect_rename, int rename_score,
 			 const char *pickaxe, int pickaxe_opts,
-			 int break_opt);
+			 int break_opt,
+			 const char *orderfile);
 
 extern int diff_queue_is_empty(void);
 
diff --git a/diffcore-order.c b/diffcore-order.c
new file mode 100644
--- /dev/null
+++ b/diffcore-order.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2005 Junio C Hamano
+ */
+#include "cache.h"
+#include "diff.h"
+#include "diffcore.h"
+#include <fnmatch.h>
+
+static char **order;
+static int order_cnt;
+
+static void prepare_order(const char *orderfile)
+{
+	int fd, cnt, pass;
+	void *map;
+	char *cp, *endp;
+	struct stat st;
+
+	if (order)
+		return;
+
+	fd = open(orderfile, O_RDONLY);
+	if (fd < 0)
+		return;
+	if (fstat(fd, &st)) {
+		close(fd);
+		return;
+	}
+	map = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+	close(fd);
+	if (-1 == (int)(long)map)
+		return;
+	endp = map + st.st_size;
+	for (pass = 0; pass < 2; pass++) {
+		cnt = 0;
+		cp = map;
+		while (cp < endp) {
+			char *ep;
+			for (ep = cp; ep < endp && *ep != '\n'; ep++)
+				;
+			/* cp to ep has one line */
+			if (*cp == '\n' || *cp == '#')
+				; /* comment */
+			else if (pass == 0)
+				cnt++;
+			else {
+				if (*ep == '\n') {
+					*ep = 0;
+					order[cnt] = cp;
+				}
+				else {
+					order[cnt] = xmalloc(ep-cp+1);
+					memcpy(order[cnt], cp, ep-cp);
+					order[cnt][ep-cp] = 0;
+				}
+				cnt++;
+			}
+			if (ep < endp)
+				ep++;
+			cp = ep;
+		}
+		if (pass == 0) {
+			order_cnt = cnt;
+			order = xmalloc(sizeof(*order) * cnt);
+		}
+	}
+}
+
+struct pair_order {
+	struct diff_filepair *pair;
+	int orig_order;
+	int order;
+};
+
+static int match_order(const char *path)
+{
+	int i;
+	char p[PATH_MAX];
+
+	for (i = 0; i < order_cnt; i++) {
+		strcpy(p, path);
+		while (p[0]) {
+			char *cp;
+			if (!fnmatch(order[i], p, 0))
+				return i;
+			cp = strrchr(p, '/');
+			if (!cp)
+				break;
+			*cp = 0;
+		}
+	}
+	return order_cnt;
+}
+
+static int compare_pair_order(const void *a_, const void *b_)
+{
+	struct pair_order const *a, *b;
+	a = (struct pair_order const *)a_;
+	b = (struct pair_order const *)b_;
+	if (a->order != b->order)
+		return a->order - b->order;
+	return a->orig_order - b->orig_order;
+}
+
+void diffcore_order(const char *orderfile)
+{
+	struct diff_queue_struct *q = &diff_queued_diff;
+	struct pair_order *o = xmalloc(sizeof(*o) * q->nr);
+	int i;
+
+	prepare_order(orderfile);
+	for (i = 0; i < q->nr; i++) {
+		o[i].pair = q->queue[i];
+		o[i].orig_order = i;
+		o[i].order = match_order(o[i].pair->two->path);
+	}
+	qsort(o, q->nr, sizeof(*o), compare_pair_order);
+	for (i = 0; i < q->nr; i++)
+		q->queue[i] = o[i].pair;
+	free(o);
+	return;
+}


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 12/12] Optimize diff-tree -[CM] --stdin
  2005-05-27 22:56       ` [PATCH 12/12] Optimize diff-tree -[CM] --stdin Junio C Hamano
@ 2005-06-04  2:17         ` Yoichi Yuasa
  0 siblings, 0 replies; 45+ messages in thread
From: Yoichi Yuasa @ 2005-06-04  2:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: yuasa, torvalds, git

Hi,

On Fri, 27 May 2005 15:56:38 -0700
Junio C Hamano <junkio@cox.net> wrote:

>

<snip>

> diff --git a/diff.c b/diff.c
> --- a/diff.c
> +++ b/diff.c
> @@ -12,6 +12,7 @@ static const char *diff_opts = "-pu";
>  static unsigned char null_sha1[20] = { 0, };
>  
>  static int reverse_diff;
> +static int use_size_cache;
>  
>  static const char *external_diff(void)
>  {
> @@ -222,12 +223,60 @@ static int work_tree_matches(const char 
>  	return 1;
>  }
>  
> +static struct sha1_size_cache {
> +	unsigned char sha1[20];
> +	unsigned long size;
> +} **sha1_size_cache;
> +static int sha1_size_cache_nr, sha1_size_cache_alloc;
> +
> +static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
> +						 unsigned long size)
> +{
> +	int first, last;
> +	struct sha1_size_cache *e;
> +
> +	first = 0;
> +	last = sha1_size_cache_nr;
> +	while (last > first) {
> +		int next = (last + first) >> 1;
> +		e = sha1_size_cache[next];
> +		int cmp = memcmp(e->sha1, sha1, 20);

I got build error in diff.c
Please apply the following patch.

Thanks,

Yoichi

Signed-off-by: Yoichi Yuasa <yuasa@hh.iij4u.or.jp>

diff --git a/diff.c b/diff.c
--- a/diff.c
+++ b/diff.c
@@ -244,9 +244,9 @@ static struct sha1_size_cache *locate_si
 	first = 0;
 	last = sha1_size_cache_nr;
 	while (last > first) {
-		int next = (last + first) >> 1;
+		int cmp, next = (last + first) >> 1;
 		e = sha1_size_cache[next];
-		int cmp = memcmp(e->sha1, sha1, 20);
+		cmp = memcmp(e->sha1, sha1, 20);
 		if (!cmp)
 			return e;
 		if (cmp < 0) {

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH] apply.c: tolerate diff from a dirty but unchanged path
  2005-05-29 18:53           ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
  2005-05-29 20:09             ` Junio C Hamano
@ 2005-06-11 23:27             ` Junio C Hamano
  2005-06-12 16:14               ` Linus Torvalds
  1 sibling, 1 reply; 45+ messages in thread
From: Junio C Hamano @ 2005-06-11 23:27 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

I just noticed a problem with empty "diff --git" header a dirty
but otherwise unmodified path spits out.  The problem is related
to an old topic:

    Re: [PATCH] Do not show empty diff in diff-cache uncached.
    >>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:

    LT> On Sat, 28 May 2005, Junio C Hamano wrote:
    >> Recent diff-cache produces "diff --git" header internally before
    >> calling the "diff" command, which results in an empty diff for
    >> such a file, cluttering the output.  This patch fixes this.

    LT> I actually _expect_ that "git-diff-files" will show
    LT> files that don't match the index, even if they happen to
    LT> have the exact content that the index points to. It's
    LT> how I know whether the index is up-to-date or not.

I now agree with the reasoning and think showing an empty "diff
--git" header is a good thing.  I had something like this, while
fixing a rename/copy bug:

    ...
     #endif

     static void diff_resolve_rename_copy(void)
    diff --git a/diffcore-break.c b/diffcore-break.c
    diff --git a/diffcore-rename.c b/diffcore-rename.c
    --- a/diffcore-rename.c
    +++ b/diffcore-rename.c
    @@ -249,8 +249,12 @@ void diffcore_rename(int detect_rename, 
    ...

I touched diffcore-break.c and decided I should not change it so
I reverted, which is why the empty "diff --git" for that file
shows that the cache is dirty.  The problem is that currently,
"git-apply --stat" seems to barf reading such a patch.

Here is a proposed fix.

------------
apply.c: tolerate diff from a dirty but otherwise unchanged cache.

For such an entry, "diff --git" line without anything else is
output, but the next "diff --git" header is not really treated
as an unrecognized diff.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
diff --git a/apply.c b/apply.c
--- a/apply.c
+++ b/apply.c
@@ -343,7 +343,7 @@ static int gitdiff_dissimilarity(const c
  */
 static int gitdiff_unrecognized(const char *line, struct patch *patch)
 {
-	return -1;
+	return -2;
 }
 
 static char *git_header_name(char *line)
@@ -453,8 +453,12 @@ static int parse_git_header(char *line, 
 			int oplen = strlen(p->str);
 			if (len < oplen || memcmp(p->str, line, oplen))
 				continue;
-			if (p->fn(line + oplen, patch) < 0)
+			switch (p->fn(line + oplen, patch)) {
+			case -1:
 				return offset;
+			case -2:
+				return -1;
+			}
 			break;
 		}
 	}


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] apply.c: tolerate diff from a dirty but unchanged path
  2005-06-11 23:27             ` [PATCH] apply.c: tolerate diff from a dirty but unchanged path Junio C Hamano
@ 2005-06-12 16:14               ` Linus Torvalds
  2005-06-12 17:05                 ` Linus Torvalds
  0 siblings, 1 reply; 45+ messages in thread
From: Linus Torvalds @ 2005-06-12 16:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List



On Sat, 11 Jun 2005, Junio C Hamano wrote:
> 
> Here is a proposed fix.

This is wrong. It will make us reject all patches that don't end the magic 
sequence of "@@ -", ie it will make us reject pure renames etc.

I'll fix it up,

		Linus

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] apply.c: tolerate diff from a dirty but unchanged path
  2005-06-12 16:14               ` Linus Torvalds
@ 2005-06-12 17:05                 ` Linus Torvalds
  2005-06-12 18:34                   ` Junio C Hamano
  0 siblings, 1 reply; 45+ messages in thread
From: Linus Torvalds @ 2005-06-12 17:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List



On Sun, 12 Jun 2005, Linus Torvalds wrote:
> 
> I'll fix it up,

One-liner fix checked in: we should ignore all git headers that are just a 
single line. A valid git header is _always_ multiple lines: either you 
have the "---/+++" lines of a diff, or you have the old/new lines of a 
mode or name change.

So the

	if (git_hdr_len < 0)

test of the return value of "parse_git_header()" was changed into

	if (git_hdr_len <= len)

and that cleanly solves the problem.

		Linus

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH] apply.c: tolerate diff from a dirty but unchanged path
  2005-06-12 17:05                 ` Linus Torvalds
@ 2005-06-12 18:34                   ` Junio C Hamano
  0 siblings, 0 replies; 45+ messages in thread
From: Junio C Hamano @ 2005-06-12 18:34 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

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

LT> One-liner fix checked in: we should ignore all git headers that are just a 
LT> single line. A valid git header is _always_ multiple lines: either you 
LT> have the "---/+++" lines of a diff, or you have the old/new lines of a 
LT> mode or name change.

Yup.  I was about to say "it always amazes me" but by now I
should instead say "I am used to see" that your fix to my
crapola is always cleaner, simpler, and shorter.

By the way, just to help you sort the heap of recent patches
from me, here is the list of patches from me not in your tree
that I think you _could_ consider.  Other patches I posted to
the list that have not been merged are mostly earlier attempts
that I consider you have already discarded; there _could_ be
something I am forgetting, but if even I myself forget, they
probably do not matter ;-):

------------
This is a fix for a bug that would embarrass me unless fixed.

    Subject: [PATCH] Fix rename/copy when dealing with temporarily broken...
    Date: Sat, 11 Jun 2005 20:55:20 -0700
    Message-ID: <7vwtp0p6tz.fsf@assigned-by-dhcp.cox.net>

A good test case is to run and compare these two in the GIT
repository.  The latter case is mishandled with unpatched code:

    commit=7ef76925d9c19ef74874e1735e2436e56d0c4897
    git-diff-tree -C $commit
    git-diff-tree -B -C $commit

------------
This one adjusts what the tutorial tells the user to expect to
the reality with the "corrected" -B behaviour we already have in
your tree.  We must have this before 1.0 in order not to confuse
the reader.  Unless you drop -B from git-status-script, that is.

    Subject: [PATCH 5/4] Tutorial update to adjust for -B fix
    Date: Fri, 03 Jun 2005 12:11:07 -0700
    Message-ID: <7vd5r3l0hg.fsf_-_@assigned-by-dhcp.cox.net>

------------
These two are enhancements that would help writing the
ultra-smart three-way merge that would look at not just merge
base and two heads, but the changes in the ancestry chain:

    Message-ID: <7vpsut7k89.fsf@assigned-by-dhcp.cox.net>
    Subject: [PATCH] diff-tree: --find-copies-harder
    Date: Fri, 10 Jun 2005 18:31:02 -0700

    Message-ID: <7vr7f8p6qu.fsf@assigned-by-dhcp.cox.net>
    Subject: [PATCH] Add --diff-filter= output restriction to diff-* family.
    Date: Sat, 11 Jun 2005 20:57:13 -0700

I mentioned them in the reply to this message from you:

    Date: Thu, 9 Jun 2005 08:15:52 -0700 (PDT)
    Subject: Re: [PATCH 3/3] read-tree -m 3-way: handle more trivial merges
    Message-ID: <Pine.LNX.4.58.0506090800580.2286@ppc970.osdl.org>

    No, I think this is quite possibly wrong for several reasons.
    ...
    So I just need a little convincing that this is a good idea.

------------
These are reworked "help carrying forward local changes in 2-way
fast forward".

    Message-ID: <7vd5qt7k2d.fsf@assigned-by-dhcp.cox.net>
    Subject: [PATCH 1/3] Clean up read-tree two-way tests.
    Date: Fri, 10 Jun 2005 18:34:34 -0700

    Message-ID: <7v7jh17jzr.fsf@assigned-by-dhcp.cox.net>
    Subject: [PATCH 2/3] read-tree --emu23.
    Date: Fri, 10 Jun 2005 18:36:08 -0700

    Message-ID: <7v1x797jx0.fsf@assigned-by-dhcp.cox.net>
    Subject: [PATCH 3/3] read-tree: fix too strong index requirement #5ALT
    Date: Fri, 10 Jun 2005 18:37:47 -0700

    Message-ID: <7voeadxlvo.fsf@assigned-by-dhcp.cox.net>
    Subject: [PATCH 4/3] Finish making --emu23 equivalent to pure 2-way merge.
    Date: Sat, 11 Jun 2005 02:50:51 -0700

    Message-ID: <7vfyvpxlqi.fsf@assigned-by-dhcp.cox.net>
    Subject: [PATCH 5/3] read-tree: loosen too strict index requirements
    Date: Sat, 11 Jun 2005 02:53:57 -0700

The first four implement --emu23, a two-tree fast forward
emulated internally using the three-way mechanism.  This is to
help cases where the user has local changes since the old head
by not refusing to run in many cases straight 2-tree fast
forward would refuse, and letting the user use the usual
merge-cache three-way merge postprocessing machinery to carry
local changes forward instead.

The changes these four introduce do affect 3-way in 3 cases,
which you objected, but this set has smaller impact than my
earlier attempts.  It only resolves 3 extra cases the original
3-way refuses to touch (my earlier one made it to resolve 3
cases that involve removed paths as well, none of which this
round touches, and leaves stages in the resulting index).  The
cases new code internally resolves, instead of refusing to run,
are:

 - only "merged head" created a new path, and the index happened
   to have the same change; we resolve it internally by taking
   the "merged head" and keep the index dirty if it was (#2ALT).

 - only "our head" created a new path, and the cache entry is
   dirty (i.e. user has local changes in the work tree file); we
   resolve it internally by taking "our head" and keep the index
   dirty if it was (#3ALT).

 - both "merged head" and "our head" created a new path
   identically, and the cache entry is dirty; we resolve it
   internally by taking "our head" and keep the index dirty if
   it was (#5ALT).

I think your earlier objection of "closing the door to the
clever merge algorithm" would not apply to these three cases.
Note that --emu23 does not need 3-way code to resolve the #2ALT
case, but needs the other two cases not refused; the patch makes
it resolve #2ALT case only to keep things symmetric with #3ALT
case --- otherwise users of 3-way merge would get confusing (but
still correct) results.

The last one [5/3] deals with the case where a path is only
changed in the merged head in 3-way case, and the index already
happens to have a version from the merged head (probably the
user was playing with a patch floating around in the mailing
list before initiating the merge).  In this case we can make the
3-way merge succeed instead of refusing to run, and that is what
this patch is about.  This does not change the outcome of the
merge, and I think your "closing the door" objection would not
apply to it.

------------
This is to enhance the git-*-script suite.  I do not mind too
much if it stays outside, but I think it would be useful to
other people as well, not just me.

    Message-ID: <7vll5h7k5t.fsf@assigned-by-dhcp.cox.net>
    Subject: [PATCH] Add script for patch submission via e-mail.
    Date: Fri, 10 Jun 2005 18:32:30 -0700

------------
This attempts to split the "too big main() that does it all"
which you hated.  Purely cosmetic at this point but as you
pointed out it is a good discipline to help later maintenance to
have a set of smaller single-task functions.

    Message-ID: <7vu0k56517.fsf_-_@assigned-by-dhcp.cox.net>
    Subject: [PATCH] diff-stages: unuglify the too big main() function.
    Date: Fri, 10 Jun 2005 18:44:36 -0700



^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2005-06-12 18:30 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-05-27  0:41 Broken directory pathname pruning Linus Torvalds
2005-05-27  0:42 ` Junio C Hamano
2005-05-27  0:49 ` [PATCH] allow pathspec to end with a slash Junio C Hamano
2005-05-27  0:52   ` [PATCH] allow pathspec to end with a slash (take #2) Junio C Hamano
2005-05-27  6:41 ` [PATCH] Diff updates, fixing pathspec and rename/copy interaction Junio C Hamano
2005-05-27 15:56   ` Linus Torvalds
2005-05-27 18:22     ` Junio C Hamano
2005-05-27 22:43     ` [PATCH 00/12] Diff updates Junio C Hamano
2005-05-27 22:49       ` [PATCH 01/12] Fix math thinko in similarity estimator Junio C Hamano
2005-05-27 22:50       ` [PATCH 02/12] Introduce diff_free_filepair() funcion Junio C Hamano
2005-05-27 22:51       ` [PATCH 03/12] Make pathspec only care about the detination tree Junio C Hamano
2005-05-27 22:52       ` [PATCH 04/12] Remove unused rank field from diff_core structure Junio C Hamano
2005-05-27 22:53       ` [PATCH 05/12] Do not expose internal scaling to diff-helper Junio C Hamano
2005-05-27 22:54       ` [PATCH 06/12] Remove final newline from the value of xfrm_msg variable Junio C Hamano
2005-05-27 22:54       ` [PATCH 07/12] Clean up diff_setup() to make it more extensible Junio C Hamano
2005-05-27 22:55       ` [PATCH 08/12] Remove a function not used anymore Junio C Hamano
2005-05-27 22:55       ` [PATCH 09/12] Add --pickaxe-all to diff-* brothers Junio C Hamano
2005-05-27 22:55       ` [PATCH 10/12] Fix the way diffcore-rename records unremoved source Junio C Hamano
2005-05-27 22:56       ` [PATCH 11/12] Move pathspec to the beginning of the diffcore chain Junio C Hamano
2005-05-27 22:56       ` [PATCH 12/12] Optimize diff-tree -[CM] --stdin Junio C Hamano
2005-06-04  2:17         ` Yoichi Yuasa
2005-05-27 23:03       ` [PATCH 00/12] Diff updates Junio C Hamano
2005-05-28 10:11         ` [PATCH] Do not show empty diff in diff-cache uncached Junio C Hamano
2005-05-28 19:22           ` [PATCH] Diff: two fixes Junio C Hamano
2005-05-29  4:20             ` [PATCH] diff-helper: fix R/C score parsing under -z flag Junio C Hamano
2005-05-29  5:23               ` [PATCH] diff-cache: diff-patch (-p) format fixes Junio C Hamano
2005-05-29  9:10                 ` [PATCH] diff: code clean-up Junio C Hamano
2005-05-29 18:53           ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
2005-05-29 20:09             ` Junio C Hamano
2005-05-29 21:52               ` Junio C Hamano
2005-05-29 23:41                 ` [PATCH 0/3] Leftover bits after 12-series Junio C Hamano
2005-05-29 23:54                   ` [PATCH 1/3] diff-helper: Fix R/C score parsing under -z flag Junio C Hamano
2005-05-29 23:56                   ` [PATCH 2/3] diff: consolidate various calls into diffcore Junio C Hamano
2005-05-29 23:56                   ` [PATCH 3/3] diff: code clean-up and removal of rename hack Junio C Hamano
2005-05-30  6:58                   ` [PATCH 0/4] Junio C Hamano
2005-05-30  7:07                     ` [PATCH 1/4] diff: further cleanup Junio C Hamano
2005-05-30  7:08                     ` [PATCH 2/4] diff: fix the culling of unneeded delete record Junio C Hamano
2005-05-30  7:08                     ` [PATCH 3/4] Add -B flag to diff-* brothers Junio C Hamano
2005-05-30  7:09                     ` [PATCH 4/4] Add -O<orderfile> option " Junio C Hamano
2005-05-30  5:34                 ` [PATCH] Do not show empty diff in diff-cache uncached Linus Torvalds
2005-05-30  5:53                   ` Junio C Hamano
2005-06-11 23:27             ` [PATCH] apply.c: tolerate diff from a dirty but unchanged path Junio C Hamano
2005-06-12 16:14               ` Linus Torvalds
2005-06-12 17:05                 ` Linus Torvalds
2005-06-12 18:34                   ` Junio C Hamano

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).