Commit 12e422a0562de2aebb05f5f414dbcde7caf85886

Russell Belfer 2014-04-21T16:08:05

Some doc and examples/diff.c changes I was playing with "git diff-index" and wanted to be able to emulate that behavior a little more closely with the diff example. Also, I wanted to play with running `git_diff_tree_to_workdir` directly even though core Git doesn't exactly have the equivalent, so I added a command line option for that and tweaked some other things in the example code. This changes a minor output thing in that the "raw" print helper function will no longer add ellipses (...) if the OID is not actually abbreviated.

diff --git a/examples/diff.c b/examples/diff.c
index 6f68e83..1dbf85f 100644
--- a/examples/diff.c
+++ b/examples/diff.c
@@ -33,14 +33,26 @@ static const char *colors[] = {
 	"\033[36m" /* cyan */
 };
 
+enum {
+	OUTPUT_DIFF = 0,
+	OUTPUT_STAT = 1,
+	OUTPUT_SHORTSTAT = 2,
+	OUTPUT_NUMSTAT = 3
+};
+
+enum {
+	CACHE_NORMAL = 0,
+	CACHE_ONLY = 1,
+	CACHE_NONE = 2
+};
+
 /** The 'opts' struct captures all the various parsed command line options. */
 struct opts {
 	git_diff_options diffopts;
 	git_diff_find_options findopts;
 	int color;
-	int cached;
-	int numstat;
-	int shortstat;
+	int cache;
+	int output;
 	git_diff_format_t format;
 	const char *treeish1;
 	const char *treeish2;
@@ -48,11 +60,11 @@ struct opts {
 };
 
 /** These functions are implemented at the end */
+static void usage(const char *message, const char *arg);
 static void parse_opts(struct opts *o, int argc, char *argv[]);
 static int color_printer(
 	const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
-static void diff_print_numstat(git_diff *diff);
-static void diff_print_shortstat(git_diff *diff);
+static void diff_print_stats(git_diff *diff, struct opts *o);
 
 int main(int argc, char *argv[])
 {
@@ -61,7 +73,7 @@ int main(int argc, char *argv[])
 	git_diff *diff;
 	struct opts o = {
 		GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
-		-1, 0, 0, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
+		-1, 0, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
 	};
 
 	git_threads_init();
@@ -78,6 +90,7 @@ int main(int argc, char *argv[])
 	 *  * <sha1> --cached
 	 *  * <sha1>
 	 *  * --cached
+	 *  * --nocache (don't use index data in diff at all)
 	 *  * nothing
 	 *
 	 * Currently ranged arguments like <sha1>..<sha2> and <sha1>...<sha2>
@@ -93,20 +106,23 @@ int main(int argc, char *argv[])
 		check_lg2(
 			git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts),
 			"diff trees", NULL);
-	else if (t1 && o.cached)
-		check_lg2(
-			git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
-			"diff tree to index", NULL);
+	else if (o.cache != CACHE_NORMAL) {
+		if (!t1)
+			treeish_to_tree(&t1, repo, "HEAD");
+
+		if (o.cache == CACHE_NONE)
+			check_lg2(
+				git_diff_tree_to_workdir(&diff, repo, t1, &o.diffopts),
+				"diff tree to working directory", NULL);
+		else
+			check_lg2(
+				git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
+				"diff tree to index", NULL);
+	}
 	else if (t1)
 		check_lg2(
 			git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts),
 			"diff tree to working directory", NULL);
-	else if (o.cached) {
-		treeish_to_tree(&t1, repo, "HEAD");
-		check_lg2(
-			git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
-			"diff tree to index", NULL);
-	}
 	else
 		check_lg2(
 			git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts),
@@ -121,11 +137,14 @@ int main(int argc, char *argv[])
 
 	/** Generate simple output using libgit2 display helper. */
 
-	if (o.numstat == 1)
-		diff_print_numstat(diff);
-	else if (o.shortstat == 1)
-		diff_print_shortstat(diff);
-	else {
+	switch (o.output) {
+	case OUTPUT_STAT:
+	case OUTPUT_NUMSTAT:
+	case OUTPUT_SHORTSTAT:
+		diff_print_stats(diff, &o);
+		break;
+
+	case OUTPUT_DIFF:
 		if (o.color >= 0)
 			fputs(colors[0], stdout);
 
@@ -135,6 +154,10 @@ int main(int argc, char *argv[])
 
 		if (o.color >= 0)
 			fputs(colors[0], stdout);
+		break;
+
+	default:
+		usage("Unknown output format", "programmer error");
 	}
 
 	/** Cleanup before exiting. */
@@ -213,13 +236,20 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
 			!strcmp(a, "--patch"))
 			o->format = GIT_DIFF_FORMAT_PATCH;
 		else if (!strcmp(a, "--cached"))
-			o->cached = 1;
-		else if (!strcmp(a, "--name-only"))
+			o->cache = CACHE_ONLY;
+		else if (!strcmp(a, "--nocache"))
+			o->cache = CACHE_NONE;
+		else if (!strcmp(a, "--name-only") || !strcmp(a, "--format=name"))
 			o->format = GIT_DIFF_FORMAT_NAME_ONLY;
-		else if (!strcmp(a, "--name-status"))
+		else if (!strcmp(a, "--name-status") ||
+				!strcmp(a, "--format=name-status"))
 			o->format = GIT_DIFF_FORMAT_NAME_STATUS;
-		else if (!strcmp(a, "--raw"))
+		else if (!strcmp(a, "--raw") || !strcmp(a, "--format=raw"))
 			o->format = GIT_DIFF_FORMAT_RAW;
+		else if (!strcmp(a, "--format=diff-index")) {
+			o->format = GIT_DIFF_FORMAT_RAW;
+			o->diffopts.id_abbrev = 40;
+		}
 		else if (!strcmp(a, "--color"))
 			o->color = 0;
 		else if (!strcmp(a, "--no-color"))
@@ -242,10 +272,12 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
 			o->diffopts.flags |= GIT_DIFF_PATIENCE;
 		else if (!strcmp(a, "--minimal"))
 			o->diffopts.flags |= GIT_DIFF_MINIMAL;
+		else if (!strcmp(a, "--stat"))
+			o->output = OUTPUT_STAT;
 		else if (!strcmp(a, "--numstat"))
-			o->numstat = 1;
+			o->output = OUTPUT_NUMSTAT;
 		else if (!strcmp(a, "--shortstat"))
-			o->shortstat = 1;
+			o->output = OUTPUT_SHORTSTAT;
 		else if (match_uint16_arg(
 				&o->findopts.rename_threshold, &args, "-M") ||
 			match_uint16_arg(
@@ -267,6 +299,8 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
 				&o->diffopts.context_lines, &args, "--unified") &&
 			!match_uint16_arg(
 				&o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
+			!match_uint16_arg(
+				&o->diffopts.id_abbrev, &args, "--abbrev") &&
 			!match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
 			!match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
 			!match_str_arg(&o->dir, &args, "--git-dir"))
@@ -274,34 +308,8 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
 	}
 }
 
-/** Display diff output with "--numstat".*/
-static void diff_print_numstat(git_diff *diff)
-{
-	git_patch *patch;
-	const git_diff_delta *delta;
-	size_t d, ndeltas = git_diff_num_deltas(diff);
-	size_t nadditions, ndeletions;
-
-	for (d = 0; d < ndeltas; d++){
-		check_lg2(
-			git_patch_from_diff(&patch, diff, d),
-			"generating patch from diff", NULL);
-
-		check_lg2(
-			git_patch_line_stats(NULL, &nadditions, &ndeletions, patch),
-			"generating the number of additions and deletions", NULL);
-
-		delta = git_patch_get_delta(patch);
-
-		printf("%ld\t%ld\t%s\n",
-			   (long)nadditions, (long)ndeletions, delta->new_file.path);
-
-		git_patch_free(patch);
-	}
-}
-
-/** Display diff output with "--shortstat".*/
-static void diff_print_shortstat(git_diff *diff)
+/** Display diff output with "--numstat" or "--shortstat" */
+static void diff_print_stats(git_diff *diff, struct opts *o)
 {
 	git_patch *patch;
 	size_t d, ndeltas = git_diff_num_deltas(diff);
@@ -320,26 +328,39 @@ static void diff_print_shortstat(git_diff *diff)
 			git_patch_line_stats(NULL, &nadditions, &ndeletions, patch),
 			"generating the number of additions and deletions", NULL);
 
+		if (o->output == OUTPUT_NUMSTAT) {
+			const git_diff_delta *delta = git_patch_get_delta(patch);
+			printf("%ld\t%ld\t%s\n",
+				   (long)nadditions, (long)ndeletions, delta->new_file.path);
+		}
+		else if (o->output == OUTPUT_STAT) {
+			const git_diff_delta *delta = git_patch_get_delta(patch);
+			printf(" %s\t| %ld\t(%ld+ %ld-)\n",
+				   delta->new_file.path, (long)nadditions + (long)ndeletions,
+				   (long)nadditions, (long)ndeletions);
+		}
+
 		nadditions_sum += nadditions;
 		ndeletions_sum += ndeletions;
 
 		git_patch_free(patch);
 	}
 
-	if (ndeltas) {
-
-	    printf(" %ld ", (long)ndeltas);
-	    printf("%s", 1==ndeltas ? "file changed" : "files changed");
+	if (o->output != OUTPUT_NUMSTAT && ndeltas > 0) {
+	    printf(" %ld %s", (long)ndeltas,
+			1 == ndeltas ? "file changed" : "files changed");
 
-	    if(nadditions_sum) {
-		printf(", %ld ",nadditions_sum);
-		printf("%s", 1==nadditions_sum ? "insertion(+)" : "insertions(+)");
+	    if (nadditions_sum) {
+			printf(", %ld ",nadditions_sum);
+			printf("%s", 1 == nadditions_sum ? "insertion(+)" : "insertions(+)");
 	    }
 
-	    if(ndeletions_sum) {
-		printf(", %ld ",ndeletions_sum);
-		printf("%s", 1==ndeletions_sum ? "deletion(-)" : "deletions(-)");
+	    if (ndeletions_sum) {
+			printf(", %ld ",ndeletions_sum);
+			printf("%s", 1 == ndeletions_sum ? "deletion(-)" : "deletions(-)");
 	    }
+
 	    printf("\n");
 	}
 }
+
diff --git a/include/git2/diff.h b/include/git2/diff.h
index a0cfbc9..c8e1ad1 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -725,24 +725,17 @@ GIT_EXTERN(int) git_diff_index_to_workdir(
  * The tree you provide will be used for the "old_file" side of the delta,
  * and the working directory will be used for the "new_file" side.
  *
- * Please note: this is *NOT* the same as `git diff <treeish>`.  Running
- * `git diff HEAD` or the like actually uses information from the index,
- * along with the tree and working directory info.
- *
- * This function returns strictly the differences between the tree and the
- * files contained in the working directory, regardless of the state of
- * files in the index.  It may come as a surprise, but there is no direct
- * equivalent in core git.
- *
- * To emulate `git diff <tree>`, use `git_diff_tree_to_workdir_with_index`
- * (or `git_diff_tree_to_index` and `git_diff_index_to_workdir`, then call
- * `git_diff_merge` on the results).  That will yield a `git_diff` that
- * matches the git output.
- *
- * If this seems confusing, take the case of a file with a staged deletion
- * where the file has then been put back into the working dir and modified.
- * The tree-to-workdir diff for that file is 'modified', but core git would
- * show status 'deleted' since there is a pending deletion in the index.
+ * This is not the same as `git diff <treeish>` or `git diff-index
+ * <treeish>`.  Those commands use information from the index, whereas this
+ * function strictly returns the differences between the tree and the files
+ * in the working directory, regardless of the state of the index.  Use
+ * `git_diff_tree_to_workdir_with_index` to emulate those commands.
+ *
+ * To see difference between this and `git_diff_tree_to_workdir_with_index`,
+ * consider the example of a staged file deletion where the file has then
+ * been put back into the working dir and further modified.  The
+ * tree-to-workdir diff for that file is 'modified', but `git diff` would
+ * show status 'deleted' since there is a staged delete.
  *
  * @param diff A pointer to a git_diff pointer that will be allocated.
  * @param repo The repository containing the tree.
diff --git a/src/diff_print.c b/src/diff_print.c
index a7f7b6f..ee5cd8d 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -175,7 +175,8 @@ static int diff_print_one_raw(
 	git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id);
 
 	git_buf_printf(
-		out, ":%06o %06o %s... %s... %c",
+		out, (pi->oid_strlen <= GIT_OID_HEXSZ) ?
+			":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
 		delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
 
 	if (delta->similarity > 0)