Commit 28f704433b949bfd7fe43a15aab0023b114fe706

Edward Thomson 2015-09-23T10:38:51

patch_parse: use names from `diff --git` header When a text file is added or deleted, use the file names from the `diff --git` header instead of the `---` or `+++` lines. This is for compatibility with git.

diff --git a/src/diff_print.c b/src/diff_print.c
index 7c9af24..29ffcdd 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -282,7 +282,6 @@ static int diff_print_oid_range(
 	git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id);
 	git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id);
 
-	/* TODO: Match git diff more closely */
 	if (delta->old_file.mode == delta->new_file.mode) {
 		git_buf_printf(out, "index %s..%s %o\n",
 			start_oid, end_oid, delta->old_file.mode);
diff --git a/src/patch_parse.c b/src/patch_parse.c
index a450ecc..f375688 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -628,6 +628,49 @@ done:
 	return error;
 }
 
+static int check_filenames(
+	git_patch_parsed *patch,
+	patch_parse_ctx *ctx)
+{
+	/* For modechange only patches, it does not include filenames;
+	 * instead we need to use the paths in the diff --git header.
+	 */
+	if (!patch->base.delta->old_file.path &&
+		!patch->base.delta->new_file.path) {
+
+		if (!ctx->header_old_path || !ctx->header_new_path)
+			return parse_err("git diff header lacks old / new paths");
+
+		patch->base.delta->old_file.path = ctx->header_old_path;
+		ctx->header_old_path = NULL;
+
+		patch->base.delta->new_file.path = ctx->header_new_path;
+		ctx->header_new_path = NULL;
+	}
+
+	/* For additions that have a `diff --git` header, set the old path
+	 * to the path from the header, not `/dev/null`.
+	 */
+	if (patch->base.delta->status == GIT_DELTA_ADDED &&
+			ctx->header_old_path) {
+		git__free((char *)patch->base.delta->old_file.path);
+		patch->base.delta->old_file.path = ctx->header_old_path;
+		ctx->header_old_path = NULL;
+	}
+
+	/* For deletes, set the new path to the path from the
+	* `diff --git` header, not `/dev/null`.
+	*/
+	if (patch->base.delta->status == GIT_DELTA_DELETED &&
+			ctx->header_new_path) {
+		git__free((char *)patch->base.delta->new_file.path);
+		patch->base.delta->new_file.path = ctx->header_new_path;
+		ctx->header_new_path = NULL;
+	}
+
+	return 0;
+}
+
 static int parsed_patch_header(
 	git_patch_parsed *patch,
 	patch_parse_ctx *ctx)
@@ -667,23 +710,7 @@ static int parsed_patch_header(
 			if ((error = parse_header_git(patch, ctx)) < 0)
 				goto done;
 
-			/* For modechange only patches, it does not include filenames;
-			* instead we need to use the paths in the diff --git header.
-			*/
-			if (!patch->base.delta->old_file.path &&
-				!patch->base.delta->new_file.path) {
-
-				if (!ctx->header_old_path || !ctx->header_new_path) {
-					error = parse_err("git diff header lacks old / new paths");
-					goto done;
-				}
-
-				patch->base.delta->old_file.path = ctx->header_old_path;
-				ctx->header_old_path = NULL;
-
-				patch->base.delta->new_file.path = ctx->header_new_path;
-				ctx->header_new_path = NULL;
-			}
+			error = check_filenames(patch, ctx);
 
 			goto done;
 		}