1 #include "git2/patch.h"
5 #define parse_err(...) \
6 ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
11 git_patch_options opts
;
13 /* the paths from the `diff --git` header, these will be used if this is not
14 * a rename (and rename paths are specified) or if no `+++`/`---` line specify
17 char *header_old_path
, *header_new_path
;
19 /* renamed paths are precise and are not prefixed */
20 char *rename_old_path
, *rename_new_path
;
22 /* the paths given in `---` and `+++` lines */
23 char *old_path
, *new_path
;
25 /* the prefixes from the old/new paths */
26 char *old_prefix
, *new_prefix
;
41 static void parse_advance_line(patch_parse_ctx
*ctx
)
43 ctx
->line
+= ctx
->line_len
;
44 ctx
->remain
-= ctx
->line_len
;
45 ctx
->line_len
= git__linenlen(ctx
->line
, ctx
->remain
);
49 static void parse_advance_chars(patch_parse_ctx
*ctx
, size_t char_cnt
)
51 ctx
->line
+= char_cnt
;
52 ctx
->remain
-= char_cnt
;
53 ctx
->line_len
-= char_cnt
;
56 static int parse_advance_expected(
61 if (ctx
->line_len
< expected_len
)
64 if (memcmp(ctx
->line
, expected
, expected_len
) != 0)
67 parse_advance_chars(ctx
, expected_len
);
71 static int parse_advance_ws(patch_parse_ctx
*ctx
)
75 while (ctx
->line_len
> 0 &&
76 ctx
->line
[0] != '\n' &&
77 git__isspace(ctx
->line
[0])) {
87 static int parse_advance_nl(patch_parse_ctx
*ctx
)
89 if (ctx
->line_len
!= 1 || ctx
->line
[0] != '\n')
92 parse_advance_line(ctx
);
96 static int header_path_len(patch_parse_ctx
*ctx
)
99 bool quoted
= (ctx
->line_len
> 0 && ctx
->line
[0] == '"');
102 for (len
= quoted
; len
< ctx
->line_len
; len
++) {
103 if (!quoted
&& git__isspace(ctx
->line
[len
]))
105 else if (quoted
&& !inquote
&& ctx
->line
[len
] == '"') {
110 inquote
= (!inquote
&& ctx
->line
[len
] == '\\');
116 static int parse_header_path_buf(git_buf
*path
, patch_parse_ctx
*ctx
)
118 int path_len
, error
= 0;
120 path_len
= header_path_len(ctx
);
122 if ((error
= git_buf_put(path
, ctx
->line
, path_len
)) < 0)
125 parse_advance_chars(ctx
, path_len
);
129 if (path
->size
> 0 && path
->ptr
[0] == '"')
130 error
= git_buf_unquote(path
);
135 git_path_squash_slashes(path
);
141 static int parse_header_path(char **out
, patch_parse_ctx
*ctx
)
143 git_buf path
= GIT_BUF_INIT
;
144 int error
= parse_header_path_buf(&path
, ctx
);
146 *out
= git_buf_detach(&path
);
151 static int parse_header_git_oldpath(
152 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
154 return parse_header_path(&patch
->old_path
, ctx
);
157 static int parse_header_git_newpath(
158 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
160 return parse_header_path(&patch
->new_path
, ctx
);
163 static int parse_header_mode(uint16_t *mode
, patch_parse_ctx
*ctx
)
169 if (ctx
->line_len
< 1 || !git__isdigit(ctx
->line
[0]))
170 return parse_err("invalid file mode at line %d", ctx
->line_num
);
172 if ((ret
= git__strntol32(&m
, ctx
->line
, ctx
->line_len
, &end
, 8)) < 0)
180 parse_advance_chars(ctx
, (end
- ctx
->line
));
185 static int parse_header_oid(
188 patch_parse_ctx
*ctx
)
192 for (len
= 0; len
< ctx
->line_len
&& len
< GIT_OID_HEXSZ
; len
++) {
193 if (!git__isxdigit(ctx
->line
[len
]))
197 if (len
< GIT_OID_MINPREFIXLEN
||
198 git_oid_fromstrn(oid
, ctx
->line
, len
) < 0)
199 return parse_err("invalid hex formatted object id at line %d",
202 parse_advance_chars(ctx
, len
);
209 static int parse_header_git_index(
210 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
212 if (parse_header_oid(&patch
->base
.delta
->old_file
.id
,
213 &patch
->base
.delta
->old_file
.id_abbrev
, ctx
) < 0 ||
214 parse_advance_expected(ctx
, "..", 2) < 0 ||
215 parse_header_oid(&patch
->base
.delta
->new_file
.id
,
216 &patch
->base
.delta
->new_file
.id_abbrev
, ctx
) < 0)
219 if (ctx
->line_len
> 0 && ctx
->line
[0] == ' ') {
222 parse_advance_chars(ctx
, 1);
224 if (parse_header_mode(&mode
, ctx
) < 0)
227 if (!patch
->base
.delta
->new_file
.mode
)
228 patch
->base
.delta
->new_file
.mode
= mode
;
230 if (!patch
->base
.delta
->old_file
.mode
)
231 patch
->base
.delta
->old_file
.mode
= mode
;
237 static int parse_header_git_oldmode(
238 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
240 return parse_header_mode(&patch
->base
.delta
->old_file
.mode
, ctx
);
243 static int parse_header_git_newmode(
244 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
246 return parse_header_mode(&patch
->base
.delta
->new_file
.mode
, ctx
);
249 static int parse_header_git_deletedfilemode(
250 git_patch_parsed
*patch
,
251 patch_parse_ctx
*ctx
)
253 git__free((char *)patch
->base
.delta
->old_file
.path
);
255 patch
->base
.delta
->old_file
.path
= NULL
;
256 patch
->base
.delta
->status
= GIT_DELTA_DELETED
;
257 patch
->base
.delta
->nfiles
= 1;
259 return parse_header_mode(&patch
->base
.delta
->old_file
.mode
, ctx
);
262 static int parse_header_git_newfilemode(
263 git_patch_parsed
*patch
,
264 patch_parse_ctx
*ctx
)
266 git__free((char *)patch
->base
.delta
->new_file
.path
);
268 patch
->base
.delta
->new_file
.path
= NULL
;
269 patch
->base
.delta
->status
= GIT_DELTA_ADDED
;
270 patch
->base
.delta
->nfiles
= 1;
272 return parse_header_mode(&patch
->base
.delta
->new_file
.mode
, ctx
);
275 static int parse_header_rename(
277 patch_parse_ctx
*ctx
)
279 git_buf path
= GIT_BUF_INIT
;
281 if (parse_header_path_buf(&path
, ctx
) < 0)
284 /* Note: the `rename from` and `rename to` lines include the literal
285 * filename. They do *not* include the prefix. (Who needs consistency?)
287 *out
= git_buf_detach(&path
);
291 static int parse_header_renamefrom(
292 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
294 patch
->base
.delta
->status
= GIT_DELTA_RENAMED
;
295 return parse_header_rename(&patch
->rename_old_path
, ctx
);
298 static int parse_header_renameto(
299 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
301 patch
->base
.delta
->status
= GIT_DELTA_RENAMED
;
302 return parse_header_rename(&patch
->rename_new_path
, ctx
);
305 static int parse_header_percent(uint16_t *out
, patch_parse_ctx
*ctx
)
310 if (ctx
->line_len
< 1 || !git__isdigit(ctx
->line
[0]) ||
311 git__strntol32(&val
, ctx
->line
, ctx
->line_len
, &end
, 10) < 0)
314 parse_advance_chars(ctx
, (end
- ctx
->line
));
316 if (parse_advance_expected(ctx
, "%", 1) < 0)
326 static int parse_header_similarity(
327 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
329 if (parse_header_percent(&patch
->base
.delta
->similarity
, ctx
) < 0)
330 return parse_err("invalid similarity percentage at line %d",
336 static int parse_header_dissimilarity(
337 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
339 uint16_t dissimilarity
;
341 if (parse_header_percent(&dissimilarity
, ctx
) < 0)
342 return parse_err("invalid similarity percentage at line %d",
345 patch
->base
.delta
->similarity
= 100 - dissimilarity
;
352 int(*fn
)(git_patch_parsed
*, patch_parse_ctx
*);
355 static const header_git_op header_git_ops
[] = {
357 { "GIT binary patch", NULL
},
358 { "--- ", parse_header_git_oldpath
},
359 { "+++ ", parse_header_git_newpath
},
360 { "index ", parse_header_git_index
},
361 { "old mode ", parse_header_git_oldmode
},
362 { "new mode ", parse_header_git_newmode
},
363 { "deleted file mode ", parse_header_git_deletedfilemode
},
364 { "new file mode ", parse_header_git_newfilemode
},
365 { "rename from ", parse_header_renamefrom
},
366 { "rename to ", parse_header_renameto
},
367 { "rename old ", parse_header_renamefrom
},
368 { "rename new ", parse_header_renameto
},
369 { "similarity index ", parse_header_similarity
},
370 { "dissimilarity index ", parse_header_dissimilarity
},
373 static int parse_header_git(
374 git_patch_parsed
*patch
,
375 patch_parse_ctx
*ctx
)
380 /* Parse the diff --git line */
381 if (parse_advance_expected(ctx
, "diff --git ", 11) < 0)
382 return parse_err("corrupt git diff header at line %d", ctx
->line_num
);
384 if (parse_header_path(&patch
->header_old_path
, ctx
) < 0)
385 return parse_err("corrupt old path in git diff header at line %d",
388 if (parse_advance_ws(ctx
) < 0 ||
389 parse_header_path(&patch
->header_new_path
, ctx
) < 0)
390 return parse_err("corrupt new path in git diff header at line %d",
393 /* Parse remaining header lines */
394 for (parse_advance_line(ctx
); ctx
->remain
> 0; parse_advance_line(ctx
)) {
395 if (ctx
->line_len
== 0 || ctx
->line
[ctx
->line_len
- 1] != '\n')
398 for (i
= 0; i
< ARRAY_SIZE(header_git_ops
); i
++) {
399 const header_git_op
*op
= &header_git_ops
[i
];
400 size_t op_len
= strlen(op
->str
);
402 if (memcmp(ctx
->line
, op
->str
, min(op_len
, ctx
->line_len
)) != 0)
405 /* Do not advance if this is the patch separator */
409 parse_advance_chars(ctx
, op_len
);
411 if ((error
= op
->fn(patch
, ctx
)) < 0)
414 parse_advance_ws(ctx
);
415 parse_advance_expected(ctx
, "\n", 1);
417 if (ctx
->line_len
> 0) {
418 error
= parse_err("trailing data at line %d", ctx
->line_num
);
430 static int parse_number(git_off_t
*out
, patch_parse_ctx
*ctx
)
435 if (!git__isdigit(ctx
->line
[0]))
438 if (git__strntol64(&num
, ctx
->line
, ctx
->line_len
, &end
, 10) < 0)
445 parse_advance_chars(ctx
, (end
- ctx
->line
));
450 static int parse_int(int *out
, patch_parse_ctx
*ctx
)
454 if (parse_number(&num
, ctx
) < 0 || !git__is_int(num
))
461 static int parse_hunk_header(
462 git_patch_hunk
*hunk
,
463 patch_parse_ctx
*ctx
)
465 const char *header_start
= ctx
->line
;
467 hunk
->hunk
.old_lines
= 1;
468 hunk
->hunk
.new_lines
= 1;
470 if (parse_advance_expected(ctx
, "@@ -", 4) < 0 ||
471 parse_int(&hunk
->hunk
.old_start
, ctx
) < 0)
474 if (ctx
->line_len
> 0 && ctx
->line
[0] == ',') {
475 if (parse_advance_expected(ctx
, ",", 1) < 0 ||
476 parse_int(&hunk
->hunk
.old_lines
, ctx
) < 0)
480 if (parse_advance_expected(ctx
, " +", 2) < 0 ||
481 parse_int(&hunk
->hunk
.new_start
, ctx
) < 0)
484 if (ctx
->line_len
> 0 && ctx
->line
[0] == ',') {
485 if (parse_advance_expected(ctx
, ",", 1) < 0 ||
486 parse_int(&hunk
->hunk
.new_lines
, ctx
) < 0)
490 if (parse_advance_expected(ctx
, " @@", 3) < 0)
493 parse_advance_line(ctx
);
495 if (!hunk
->hunk
.old_lines
&& !hunk
->hunk
.new_lines
)
498 hunk
->hunk
.header_len
= ctx
->line
- header_start
;
499 if (hunk
->hunk
.header_len
> (GIT_DIFF_HUNK_HEADER_SIZE
- 1))
500 return parse_err("oversized patch hunk header at line %d",
503 memcpy(hunk
->hunk
.header
, header_start
, hunk
->hunk
.header_len
);
504 hunk
->hunk
.header
[hunk
->hunk
.header_len
] = '\0';
509 giterr_set(GITERR_PATCH
, "invalid patch hunk header at line %d",
514 static int parse_hunk_body(
515 git_patch_parsed
*patch
,
516 git_patch_hunk
*hunk
,
517 patch_parse_ctx
*ctx
)
522 int oldlines
= hunk
->hunk
.old_lines
;
523 int newlines
= hunk
->hunk
.new_lines
;
526 ctx
->remain
> 4 && (oldlines
|| newlines
) &&
527 memcmp(ctx
->line
, "@@ -", 4) != 0;
528 parse_advance_line(ctx
)) {
533 if (ctx
->line_len
== 0 || ctx
->line
[ctx
->line_len
- 1] != '\n') {
534 error
= parse_err("invalid patch instruction at line %d",
539 switch (ctx
->line
[0]) {
544 origin
= GIT_DIFF_LINE_CONTEXT
;
550 origin
= GIT_DIFF_LINE_DELETION
;
555 origin
= GIT_DIFF_LINE_ADDITION
;
560 error
= parse_err("invalid patch hunk at line %d", ctx
->line_num
);
564 line
= git_array_alloc(patch
->base
.lines
);
565 GITERR_CHECK_ALLOC(line
);
567 memset(line
, 0x0, sizeof(git_diff_line
));
569 line
->content
= ctx
->line
+ prefix
;
570 line
->content_len
= ctx
->line_len
- prefix
;
571 line
->content_offset
= ctx
->content_len
- ctx
->remain
;
572 line
->origin
= origin
;
577 if (oldlines
|| newlines
) {
579 "invalid patch hunk, expected %d old lines and %d new lines",
580 hunk
->hunk
.old_lines
, hunk
->hunk
.new_lines
);
584 /* Handle "\ No newline at end of file". Only expect the leading
585 * backslash, though, because the rest of the string could be
586 * localized. Because `diff` optimizes for the case where you
587 * want to apply the patch by hand.
589 if (ctx
->line_len
>= 2 && memcmp(ctx
->line
, "\\ ", 2) == 0 &&
590 git_array_size(patch
->base
.lines
) > 0) {
592 line
= git_array_get(patch
->base
.lines
, git_array_size(patch
->base
.lines
) - 1);
594 if (line
->content_len
< 1) {
595 error
= parse_err("cannot trim trailing newline of empty line");
601 parse_advance_line(ctx
);
608 static int parsed_patch_header(
609 git_patch_parsed
*patch
,
610 patch_parse_ctx
*ctx
)
614 for (ctx
->line
= ctx
->content
; ctx
->remain
> 0; parse_advance_line(ctx
)) {
615 /* This line is too short to be a patch header. */
616 if (ctx
->line_len
< 6)
619 /* This might be a hunk header without a patch header, provide a
620 * sensible error message. */
621 if (memcmp(ctx
->line
, "@@ -", 4) == 0) {
622 size_t line_num
= ctx
->line_num
;
625 /* If this cannot be parsed as a hunk header, it's just leading
628 if (parse_hunk_header(&hunk
, ctx
) < 0) {
633 error
= parse_err("invalid hunk header outside patch at line %d",
638 /* This buffer is too short to contain a patch. */
639 if (ctx
->remain
< ctx
->line_len
+ 6)
642 /* A proper git patch */
643 if (ctx
->line_len
>= 11 && memcmp(ctx
->line
, "diff --git ", 11) == 0) {
644 error
= parse_header_git(patch
, ctx
);
652 error
= parse_err("no header in patch file");
658 static int parsed_patch_binary_side(
659 git_diff_binary_file
*binary
,
660 patch_parse_ctx
*ctx
)
662 git_diff_binary_t type
= GIT_DIFF_BINARY_NONE
;
663 git_buf base85
= GIT_BUF_INIT
, decoded
= GIT_BUF_INIT
;
667 if (ctx
->line_len
>= 8 && memcmp(ctx
->line
, "literal ", 8) == 0) {
668 type
= GIT_DIFF_BINARY_LITERAL
;
669 parse_advance_chars(ctx
, 8);
671 else if (ctx
->line_len
>= 6 && memcmp(ctx
->line
, "delta ", 6) == 0) {
672 type
= GIT_DIFF_BINARY_DELTA
;
673 parse_advance_chars(ctx
, 6);
676 error
= parse_err("unknown binary delta type at line %d", ctx
->line_num
);
680 if (parse_number(&len
, ctx
) < 0 || parse_advance_nl(ctx
) < 0 || len
< 0) {
681 error
= parse_err("invalid binary size at line %d", ctx
->line_num
);
685 while (ctx
->line_len
) {
686 char c
= ctx
->line
[0];
687 size_t encoded_len
, decoded_len
= 0, decoded_orig
= decoded
.size
;
691 else if (c
>= 'A' && c
<= 'Z')
692 decoded_len
= c
- 'A' + 1;
693 else if (c
>= 'a' && c
<= 'z')
694 decoded_len
= c
- 'a' + (('z' - 'a') + 1) + 1;
697 error
= parse_err("invalid binary length at line %d", ctx
->line_num
);
701 parse_advance_chars(ctx
, 1);
703 encoded_len
= ((decoded_len
/ 4) + !!(decoded_len
% 4)) * 5;
705 if (encoded_len
> ctx
->line_len
- 1) {
706 error
= parse_err("truncated binary data at line %d", ctx
->line_num
);
710 if ((error
= git_buf_decode_base85(
711 &decoded
, ctx
->line
, encoded_len
, decoded_len
)) < 0)
714 if (decoded
.size
- decoded_orig
!= decoded_len
) {
715 error
= parse_err("truncated binary data at line %d", ctx
->line_num
);
719 parse_advance_chars(ctx
, encoded_len
);
721 if (parse_advance_nl(ctx
) < 0) {
722 error
= parse_err("trailing data at line %d", ctx
->line_num
);
728 binary
->inflatedlen
= (size_t)len
;
729 binary
->datalen
= decoded
.size
;
730 binary
->data
= git_buf_detach(&decoded
);
733 git_buf_free(&base85
);
734 git_buf_free(&decoded
);
738 static int parsed_patch_binary(
739 git_patch_parsed
*patch
,
740 patch_parse_ctx
*ctx
)
744 if (parse_advance_expected(ctx
, "GIT binary patch", 16) < 0 ||
745 parse_advance_nl(ctx
) < 0)
746 return parse_err("corrupt git binary header at line %d", ctx
->line_num
);
748 /* parse old->new binary diff */
749 if ((error
= parsed_patch_binary_side(
750 &patch
->base
.binary
.new_file
, ctx
)) < 0)
753 if (parse_advance_nl(ctx
) < 0)
754 return parse_err("corrupt git binary separator at line %d",
757 /* parse new->old binary diff */
758 if ((error
= parsed_patch_binary_side(
759 &patch
->base
.binary
.old_file
, ctx
)) < 0)
762 patch
->base
.delta
->flags
|= GIT_DIFF_FLAG_BINARY
;
766 static int parsed_patch_hunks(
767 git_patch_parsed
*patch
,
768 patch_parse_ctx
*ctx
)
770 git_patch_hunk
*hunk
;
773 for (; ctx
->line_len
> 4 && memcmp(ctx
->line
, "@@ -", 4) == 0; ) {
775 hunk
= git_array_alloc(patch
->base
.hunks
);
776 GITERR_CHECK_ALLOC(hunk
);
778 memset(hunk
, 0, sizeof(git_patch_hunk
));
780 hunk
->line_start
= git_array_size(patch
->base
.lines
);
781 hunk
->line_count
= 0;
783 if ((error
= parse_hunk_header(hunk
, ctx
)) < 0 ||
784 (error
= parse_hunk_body(patch
, hunk
, ctx
)) < 0)
792 static int parsed_patch_body(
793 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
795 if (ctx
->line_len
>= 16 && memcmp(ctx
->line
, "GIT binary patch", 16) == 0)
796 return parsed_patch_binary(patch
, ctx
);
798 else if (ctx
->line_len
>= 4 && memcmp(ctx
->line
, "@@ -", 4) == 0)
799 return parsed_patch_hunks(patch
, ctx
);
804 int check_header_names(
807 const char *old_or_new
,
813 if (two_null
&& strcmp(two
, "/dev/null") != 0)
814 return parse_err("expected %s path of '/dev/null'", old_or_new
);
816 else if (!two_null
&& strcmp(one
, two
) != 0)
817 return parse_err("mismatched %s path names", old_or_new
);
822 static int check_prefix(
825 git_patch_parsed
*patch
,
826 const char *path_start
)
828 const char *path
= path_start
;
829 uint32_t remain
= patch
->opts
.prefix_len
;
834 if (patch
->opts
.prefix_len
== 0)
837 /* leading slashes do not count as part of the prefix in git apply */
841 while (*path
&& remain
) {
848 if (remain
|| !*path
)
849 return parse_err("header filename does not contain %d path components",
850 patch
->opts
.prefix_len
);
853 *out_len
= (path
- path_start
);
854 *out
= git__strndup(path_start
, *out_len
);
856 return (out
== NULL
) ? -1 : 0;
859 static int check_filenames(git_patch_parsed
*patch
)
861 const char *prefixed_new
, *prefixed_old
;
862 size_t old_prefixlen
= 0, new_prefixlen
= 0;
863 bool added
= (patch
->base
.delta
->status
== GIT_DELTA_ADDED
);
864 bool deleted
= (patch
->base
.delta
->status
== GIT_DELTA_DELETED
);
866 if (patch
->old_path
&& !patch
->new_path
)
867 return parse_err("missing new path");
869 if (!patch
->old_path
&& patch
->new_path
)
870 return parse_err("missing old path");
872 /* Ensure (non-renamed) paths match */
873 if (check_header_names(
874 patch
->header_old_path
, patch
->old_path
, "old", added
) < 0 ||
876 patch
->header_new_path
, patch
->new_path
, "new", deleted
) < 0)
879 prefixed_old
= (!added
&& patch
->old_path
) ? patch
->old_path
:
880 patch
->header_old_path
;
881 prefixed_new
= (!deleted
&& patch
->new_path
) ? patch
->new_path
:
882 patch
->header_new_path
;
885 &patch
->old_prefix
, &old_prefixlen
, patch
, prefixed_old
) < 0 ||
887 &patch
->new_prefix
, &new_prefixlen
, patch
, prefixed_new
) < 0)
890 /* Prefer the rename filenames as they are unambiguous and unprefixed */
891 if (patch
->rename_old_path
)
892 patch
->base
.delta
->old_file
.path
= patch
->rename_old_path
;
894 patch
->base
.delta
->old_file
.path
= prefixed_old
+ old_prefixlen
;
896 if (patch
->rename_new_path
)
897 patch
->base
.delta
->new_file
.path
= patch
->rename_new_path
;
899 patch
->base
.delta
->new_file
.path
= prefixed_new
+ new_prefixlen
;
901 if (!patch
->base
.delta
->old_file
.path
&&
902 !patch
->base
.delta
->new_file
.path
)
903 return parse_err("git diff header lacks old / new paths");
908 static int check_patch(git_patch_parsed
*patch
)
910 if (check_filenames(patch
) < 0)
913 if (patch
->base
.delta
->old_file
.path
&&
914 patch
->base
.delta
->status
!= GIT_DELTA_DELETED
&&
915 !patch
->base
.delta
->new_file
.mode
)
916 patch
->base
.delta
->new_file
.mode
= patch
->base
.delta
->old_file
.mode
;
918 if (patch
->base
.delta
->status
== GIT_DELTA_MODIFIED
&&
919 !(patch
->base
.delta
->flags
& GIT_DIFF_FLAG_BINARY
) &&
920 patch
->base
.delta
->new_file
.mode
== patch
->base
.delta
->old_file
.mode
&&
921 git_array_size(patch
->base
.hunks
) == 0)
922 return parse_err("patch with no hunks");
927 static void patch_parsed__free(git_patch
*p
)
929 git_patch_parsed
*patch
= (git_patch_parsed
*)p
;
934 git__free(patch
->old_prefix
);
935 git__free(patch
->new_prefix
);
936 git__free(patch
->header_old_path
);
937 git__free(patch
->header_new_path
);
938 git__free(patch
->rename_old_path
);
939 git__free(patch
->rename_new_path
);
940 git__free(patch
->old_path
);
941 git__free(patch
->new_path
);
942 git_array_clear(patch
->base
.hunks
);
943 git_array_clear(patch
->base
.lines
);
944 git__free(patch
->base
.delta
);
948 int git_patch_from_patchfile(
952 git_patch_options
*opts
)
954 patch_parse_ctx ctx
= { 0 };
955 git_patch_parsed
*patch
;
956 git_patch_options default_opts
= GIT_PATCH_OPTIONS_INIT
;
961 patch
= git__calloc(1, sizeof(git_patch_parsed
));
962 GITERR_CHECK_ALLOC(patch
);
965 memcpy(&patch
->opts
, opts
, sizeof(git_patch_options
));
967 memcpy(&patch
->opts
, &default_opts
, sizeof(git_patch_options
));
969 patch
->base
.free_fn
= patch_parsed__free
;
971 patch
->base
.delta
= git__calloc(1, sizeof(git_diff_delta
));
972 patch
->base
.delta
->status
= GIT_DELTA_MODIFIED
;
973 patch
->base
.delta
->nfiles
= 2;
975 ctx
.content
= content
;
976 ctx
.content_len
= content_len
;
977 ctx
.remain
= content_len
;
979 if ((error
= parsed_patch_header(patch
, &ctx
)) < 0 ||
980 (error
= parsed_patch_body(patch
, &ctx
)) < 0 ||
981 (error
= check_patch(patch
)) < 0)
984 patch
->base
.diff_opts
.old_prefix
= patch
->old_prefix
;
985 patch
->base
.diff_opts
.new_prefix
= patch
->new_prefix
;
986 patch
->base
.diff_opts
.flags
|= GIT_DIFF_SHOW_BINARY
;
988 GIT_REFCOUNT_INC(patch
);
993 patch_parsed__free(&patch
->base
);