Commit d68cb736776e0f2f9494b49e2da30a9c4b9fc2c7

Edward Thomson 2015-09-22T18:25:03

diff: include oid length in deltas Now that `git_diff_delta` data can be produced by reading patch file data, which may have an abbreviated oid, allow consumers to know that the id is abbreviated.

diff --git a/include/git2/diff.h b/include/git2/diff.h
index f3bb337..065a786 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -264,10 +264,15 @@ typedef enum {
  * link, a submodule commit id, or even a tree (although that only if you
  * are tracking type changes or ignored/untracked directories).
  *
- * The `oid` is the `git_oid` of the item.  If the entry represents an
+ * The `id` is the `git_oid` of the item.  If the entry represents an
  * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
  * then the oid will be zeroes.
  *
+ * The `id_abbrev` represents the known length of the `id` field, when
+ * converted to a hex string.  It is generally `GIT_OID_HEXSZ`, unless this
+ * delta was created from reading a patch file, in which case it may be
+ * abbreviated to something reasonable, like 7 characters.
+ *
  * `path` is the NUL-terminated path to the entry relative to the working
  * directory of the repository.
  *
@@ -280,6 +285,7 @@ typedef enum {
  */
 typedef struct {
 	git_oid     id;
+	int         id_abbrev;
 	const char *path;
 	git_off_t   size;
 	uint32_t    flags;
diff --git a/src/diff.c b/src/diff.c
index 26c0b89..9c27511 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -151,11 +151,13 @@ static int diff_delta__from_one(
 		delta->old_file.size = entry->file_size;
 		delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
 		git_oid_cpy(&delta->old_file.id, &entry->id);
+		delta->old_file.id_abbrev = GIT_OID_HEXSZ;
 	} else /* ADDED, IGNORED, UNTRACKED */ {
 		delta->new_file.mode = entry->mode;
 		delta->new_file.size = entry->file_size;
 		delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
 		git_oid_cpy(&delta->new_file.id, &entry->id);
+		delta->new_file.id_abbrev = GIT_OID_HEXSZ;
 	}
 
 	delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
@@ -208,12 +210,14 @@ static int diff_delta__from_two(
 		delta->old_file.size = old_entry->file_size;
 		delta->old_file.mode = old_mode;
 		git_oid_cpy(&delta->old_file.id, old_id);
+		delta->old_file.id_abbrev = GIT_OID_HEXSZ;
 		delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID |
 			GIT_DIFF_FLAG_EXISTS;
 	}
 
 	if (!git_index_entry_is_conflict(new_entry)) {
 		git_oid_cpy(&delta->new_file.id, new_id);
+		delta->new_file.id_abbrev = GIT_OID_HEXSZ;
 		delta->new_file.size = new_entry->file_size;
 		delta->new_file.mode = new_mode;
 		delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
diff --git a/src/diff_file.c b/src/diff_file.c
index ecc34cf..8b945a5 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -149,12 +149,14 @@ int git_diff_file_content__init_from_src(
 		if (src->blob) {
 			fc->file->size = git_blob_rawsize(src->blob);
 			git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
+			fc->file->id_abbrev = GIT_OID_HEXSZ;
 
 			fc->map.len  = (size_t)fc->file->size;
 			fc->map.data = (char *)git_blob_rawcontent(src->blob);
 		} else {
 			fc->file->size = src->buflen;
 			git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);
+			fc->file->id_abbrev = GIT_OID_HEXSZ;
 
 			fc->map.len  = src->buflen;
 			fc->map.data = (char *)src->buf;
diff --git a/src/diff_print.c b/src/diff_print.c
index 09bf77a..7c9af24 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -208,6 +208,7 @@ static int diff_print_one_raw(
 {
 	diff_print_info *pi = data;
 	git_buf *out = pi->buf;
+	int id_abbrev;
 	char code = git_diff_status_char(delta->status);
 	char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
 
@@ -218,6 +219,16 @@ static int diff_print_one_raw(
 
 	git_buf_clear(out);
 
+	id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
+		delta->new_file.id_abbrev;
+
+	if (pi->oid_strlen - 1 > id_abbrev) {
+		giterr_set(GITERR_PATCH,
+			"The patch input contains %d id characters (cannot print %d)",
+			id_abbrev, pi->oid_strlen);
+		return -1;
+	}
+
 	git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id);
 	git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id);
 
@@ -252,6 +263,22 @@ static int diff_print_oid_range(
 {
 	char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
 
+	if (delta->old_file.mode &&
+			oid_strlen - 1 > delta->old_file.id_abbrev) {
+		giterr_set(GITERR_PATCH,
+			"The patch input contains %d id characters (cannot print %d)",
+			delta->old_file.id_abbrev, oid_strlen);
+		return -1;
+	}
+
+	if ((delta->new_file.mode &&
+			oid_strlen - 1 > delta->new_file.id_abbrev)) {
+		giterr_set(GITERR_PATCH,
+			"The patch input contains %d id characters (cannot print %d)",
+			delta->new_file.id_abbrev, oid_strlen);
+		return -1;
+	}
+
 	git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id);
 	git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id);
 
diff --git a/src/patch_parse.c b/src/patch_parse.c
index 11e2693..323f8dc 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -197,15 +197,11 @@ static int parse_header_oid(
 static int parse_header_git_index(
 	git_patch_parsed *patch, patch_parse_ctx *ctx)
 {
-	/*
-	* TODO: we read the prefix provided in the diff into the delta's id
-	* field, but do not mark is at an abbreviated id.
-	*/
-	size_t oid_len, nid_len;
-
-	if (parse_header_oid(&patch->base.delta->old_file.id, &oid_len, ctx) < 0 ||
+	if (parse_header_oid(&patch->base.delta->old_file.id,
+			&patch->base.delta->old_file.id_abbrev, ctx) < 0 ||
 		parse_advance_expected(ctx, "..", 2) < 0 ||
-		parse_header_oid(&patch->base.delta->new_file.id, &nid_len, ctx) < 0)
+		parse_header_oid(&patch->base.delta->new_file.id,
+			&patch->base.delta->new_file.id_abbrev, ctx) < 0)
 		return -1;
 
 	if (ctx->line_len > 0 && ctx->line[0] == ' ') {