Thread (3 messages) flat view 3 messages, 2 authors, 2016-06-15

Re: [PATCH] Add `git diff2`, a GNU diff workalike

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:54

Possibly related (same subject, not in this thread)

Johannes Schindelin [off-list ref] writes:
Git does have a wonderful diff engine. For example, colored diffs
really shine, and there are other useful options like --check,
--patch-with-stat, etc. I always dreamt of using this diff engine
also outside of a git repository.

With this commit, you can say

	git diff2 file1 file2
Why diff2?  I would have expected this to be a new low-level
sibling of diff-files and diff-index, which in turn hook into
the overall "diff" driver.
+static int queue_diff(struct diff_options *o,
+		const char *name1, const char *name2)
+{
+	struct stat st;
+	int mode1 = 0, mode2 = 0;
+
+	if (name1) {
+		if (stat(name1, &st))
+			return error("Could not access '%s'", name1);
+		mode1 = st.st_mode;
+	}
+	if (name2) {
+		if (stat(name2, &st))
+			return error("Could not access '%s'", name1);
+		mode2 = st.st_mode;
+	}
I am still debating myself if these should be lstat(2) instead
of stat(2).  The former is more consistent with what git does.
+	if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))
+		return error("file/directory conflict: %s, %s", name1, name2);
If a/frotz is a file and b/frotz/nitfol is there, I do not think
we show an error; we say "a/frotz" was removed (see notes below,
though).
+	if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
+		char buffer1[PATH_MAX], buffer2[PATH_MAX];
+		struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
+		int len1 = 0, len2 = 0, i1, i2, ret = 0;
+
+		if (name1 && read_directory(name1, &p1))
+			return -1;
+		if (name2 && read_directory(name2, &p2)) {
+			path_list_clear(&p1, 0);
+			return -1;
+		}
I suspect your favorite path-list might not be optimal for this
kind of codeflow; wouldn't reading everything in an expanding
array and sorting them with a single qsort() after reading one
directory more efficient?
+		for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
+			const char *n1, *n2;
+			int comp;
+
+			if (i1 == p1.nr)
+				comp = 1;
+			else if (i2 == p2.nr)
+				comp = -1;
+			else
+				comp = strcmp(p1.items[i1].path,
+					p2.items[i2].path);
I think you would want to be consistent with how git sorts paths
here.  In particular, you can always pretend that a path that is
a directory has '/' at the end.  Which (as a side effect) means
that you do not have to worry about a/frotz and b/frotz/nitfol,
because element from p1 will be "frotz" and the one from p2 will
be "frotz/" in this case.  You will never feed queue_diff() with
the same name with d/f conflicts that way.
+int cmd_diff2(int argc, char **argv, char **envp)
+{
The rest looks quite straightforward use of diffcore API, done
very cleanly.
quoted hunk
diff --git a/diff.c b/diff.c
index c2d9abe..be73621 100644
--- a/diff.c
+++ b/diff.c
@@ -2443,7 +2443,8 @@ static void diff_resolve_rename_copy(void)
 				p->status = DIFF_STATUS_RENAMED;
 		}
 		else if (hashcmp(p->one->sha1, p->two->sha1) ||
-			 p->one->mode != p->two->mode)
+			 p->one->mode != p->two->mode ||
+			 is_null_sha1(p->one->sha1))
 			p->status = DIFF_STATUS_MODIFIED;
I didn't look, but you might also need to teach diffcore-rename
that two objects both with null object names are not equal.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help