1 #include "git2/patch.h"
5 #define parse_err(...) \
6 ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
11 git_patch_options opts
;
13 /* the patch contents, which lines will point into. */
14 /* TODO: allow us to point somewhere refcounted. */
17 /* the paths from the `diff --git` header, these will be used if this is not
18 * a rename (and rename paths are specified) or if no `+++`/`---` line specify
21 char *header_old_path
, *header_new_path
;
23 /* renamed paths are precise and are not prefixed */
24 char *rename_old_path
, *rename_new_path
;
26 /* the paths given in `---` and `+++` lines */
27 char *old_path
, *new_path
;
29 /* the prefixes from the old/new paths */
30 char *old_prefix
, *new_prefix
;
45 static void parse_advance_line(patch_parse_ctx
*ctx
)
47 ctx
->line
+= ctx
->line_len
;
48 ctx
->remain
-= ctx
->line_len
;
49 ctx
->line_len
= git__linenlen(ctx
->line
, ctx
->remain
);
53 static void parse_advance_chars(patch_parse_ctx
*ctx
, size_t char_cnt
)
55 ctx
->line
+= char_cnt
;
56 ctx
->remain
-= char_cnt
;
57 ctx
->line_len
-= char_cnt
;
60 static int parse_advance_expected(
65 if (ctx
->line_len
< expected_len
)
68 if (memcmp(ctx
->line
, expected
, expected_len
) != 0)
71 parse_advance_chars(ctx
, expected_len
);
75 #define parse_advance_expected_s(ctx, str) \
76 parse_advance_expected(ctx, str, sizeof(str) - 1)
78 static int parse_advance_ws(patch_parse_ctx
*ctx
)
82 while (ctx
->line_len
> 0 &&
83 ctx
->line
[0] != '\n' &&
84 git__isspace(ctx
->line
[0])) {
94 static int parse_advance_nl(patch_parse_ctx
*ctx
)
96 if (ctx
->line_len
!= 1 || ctx
->line
[0] != '\n')
99 parse_advance_line(ctx
);
103 static int header_path_len(patch_parse_ctx
*ctx
)
106 bool quoted
= (ctx
->line_len
> 0 && ctx
->line
[0] == '"');
109 for (len
= quoted
; len
< ctx
->line_len
; len
++) {
110 if (!quoted
&& git__isspace(ctx
->line
[len
]))
112 else if (quoted
&& !inquote
&& ctx
->line
[len
] == '"') {
117 inquote
= (!inquote
&& ctx
->line
[len
] == '\\');
123 static int parse_header_path_buf(git_buf
*path
, patch_parse_ctx
*ctx
)
125 int path_len
, error
= 0;
127 path_len
= header_path_len(ctx
);
129 if ((error
= git_buf_put(path
, ctx
->line
, path_len
)) < 0)
132 parse_advance_chars(ctx
, path_len
);
136 if (path
->size
> 0 && path
->ptr
[0] == '"')
137 error
= git_buf_unquote(path
);
142 git_path_squash_slashes(path
);
148 static int parse_header_path(char **out
, patch_parse_ctx
*ctx
)
150 git_buf path
= GIT_BUF_INIT
;
151 int error
= parse_header_path_buf(&path
, ctx
);
153 *out
= git_buf_detach(&path
);
158 static int parse_header_git_oldpath(
159 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
161 return parse_header_path(&patch
->old_path
, ctx
);
164 static int parse_header_git_newpath(
165 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
167 return parse_header_path(&patch
->new_path
, ctx
);
170 static int parse_header_mode(uint16_t *mode
, patch_parse_ctx
*ctx
)
176 if (ctx
->line_len
< 1 || !git__isdigit(ctx
->line
[0]))
177 return parse_err("invalid file mode at line %d", ctx
->line_num
);
179 if ((ret
= git__strntol32(&m
, ctx
->line
, ctx
->line_len
, &end
, 8)) < 0)
187 parse_advance_chars(ctx
, (end
- ctx
->line
));
192 static int parse_header_oid(
195 patch_parse_ctx
*ctx
)
199 for (len
= 0; len
< ctx
->line_len
&& len
< GIT_OID_HEXSZ
; len
++) {
200 if (!git__isxdigit(ctx
->line
[len
]))
204 if (len
< GIT_OID_MINPREFIXLEN
||
205 git_oid_fromstrn(oid
, ctx
->line
, len
) < 0)
206 return parse_err("invalid hex formatted object id at line %d",
209 parse_advance_chars(ctx
, len
);
216 static int parse_header_git_index(
217 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
219 if (parse_header_oid(&patch
->base
.delta
->old_file
.id
,
220 &patch
->base
.delta
->old_file
.id_abbrev
, ctx
) < 0 ||
221 parse_advance_expected_s(ctx
, "..") < 0 ||
222 parse_header_oid(&patch
->base
.delta
->new_file
.id
,
223 &patch
->base
.delta
->new_file
.id_abbrev
, ctx
) < 0)
226 if (ctx
->line_len
> 0 && ctx
->line
[0] == ' ') {
229 parse_advance_chars(ctx
, 1);
231 if (parse_header_mode(&mode
, ctx
) < 0)
234 if (!patch
->base
.delta
->new_file
.mode
)
235 patch
->base
.delta
->new_file
.mode
= mode
;
237 if (!patch
->base
.delta
->old_file
.mode
)
238 patch
->base
.delta
->old_file
.mode
= mode
;
244 static int parse_header_git_oldmode(
245 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
247 return parse_header_mode(&patch
->base
.delta
->old_file
.mode
, ctx
);
250 static int parse_header_git_newmode(
251 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
253 return parse_header_mode(&patch
->base
.delta
->new_file
.mode
, ctx
);
256 static int parse_header_git_deletedfilemode(
257 git_patch_parsed
*patch
,
258 patch_parse_ctx
*ctx
)
260 git__free((char *)patch
->base
.delta
->old_file
.path
);
262 patch
->base
.delta
->old_file
.path
= NULL
;
263 patch
->base
.delta
->status
= GIT_DELTA_DELETED
;
264 patch
->base
.delta
->nfiles
= 1;
266 return parse_header_mode(&patch
->base
.delta
->old_file
.mode
, ctx
);
269 static int parse_header_git_newfilemode(
270 git_patch_parsed
*patch
,
271 patch_parse_ctx
*ctx
)
273 git__free((char *)patch
->base
.delta
->new_file
.path
);
275 patch
->base
.delta
->new_file
.path
= NULL
;
276 patch
->base
.delta
->status
= GIT_DELTA_ADDED
;
277 patch
->base
.delta
->nfiles
= 1;
279 return parse_header_mode(&patch
->base
.delta
->new_file
.mode
, ctx
);
282 static int parse_header_rename(
284 patch_parse_ctx
*ctx
)
286 git_buf path
= GIT_BUF_INIT
;
288 if (parse_header_path_buf(&path
, ctx
) < 0)
291 /* Note: the `rename from` and `rename to` lines include the literal
292 * filename. They do *not* include the prefix. (Who needs consistency?)
294 *out
= git_buf_detach(&path
);
298 static int parse_header_renamefrom(
299 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
301 patch
->base
.delta
->status
= GIT_DELTA_RENAMED
;
302 return parse_header_rename(&patch
->rename_old_path
, ctx
);
305 static int parse_header_renameto(
306 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
308 patch
->base
.delta
->status
= GIT_DELTA_RENAMED
;
309 return parse_header_rename(&patch
->rename_new_path
, ctx
);
312 static int parse_header_percent(uint16_t *out
, patch_parse_ctx
*ctx
)
317 if (ctx
->line_len
< 1 || !git__isdigit(ctx
->line
[0]) ||
318 git__strntol32(&val
, ctx
->line
, ctx
->line_len
, &end
, 10) < 0)
321 parse_advance_chars(ctx
, (end
- ctx
->line
));
323 if (parse_advance_expected_s(ctx
, "%") < 0)
333 static int parse_header_similarity(
334 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
336 if (parse_header_percent(&patch
->base
.delta
->similarity
, ctx
) < 0)
337 return parse_err("invalid similarity percentage at line %d",
343 static int parse_header_dissimilarity(
344 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
346 uint16_t dissimilarity
;
348 if (parse_header_percent(&dissimilarity
, ctx
) < 0)
349 return parse_err("invalid similarity percentage at line %d",
352 patch
->base
.delta
->similarity
= 100 - dissimilarity
;
359 int(*fn
)(git_patch_parsed
*, patch_parse_ctx
*);
362 static const header_git_op header_git_ops
[] = {
364 { "GIT binary patch", NULL
},
365 { "--- ", parse_header_git_oldpath
},
366 { "+++ ", parse_header_git_newpath
},
367 { "index ", parse_header_git_index
},
368 { "old mode ", parse_header_git_oldmode
},
369 { "new mode ", parse_header_git_newmode
},
370 { "deleted file mode ", parse_header_git_deletedfilemode
},
371 { "new file mode ", parse_header_git_newfilemode
},
372 { "rename from ", parse_header_renamefrom
},
373 { "rename to ", parse_header_renameto
},
374 { "rename old ", parse_header_renamefrom
},
375 { "rename new ", parse_header_renameto
},
376 { "similarity index ", parse_header_similarity
},
377 { "dissimilarity index ", parse_header_dissimilarity
},
380 static int parse_header_git(
381 git_patch_parsed
*patch
,
382 patch_parse_ctx
*ctx
)
387 /* Parse the diff --git line */
388 if (parse_advance_expected_s(ctx
, "diff --git ") < 0)
389 return parse_err("corrupt git diff header at line %d", ctx
->line_num
);
391 if (parse_header_path(&patch
->header_old_path
, ctx
) < 0)
392 return parse_err("corrupt old path in git diff header at line %d",
395 if (parse_advance_ws(ctx
) < 0 ||
396 parse_header_path(&patch
->header_new_path
, ctx
) < 0)
397 return parse_err("corrupt new path in git diff header at line %d",
400 /* Parse remaining header lines */
401 for (parse_advance_line(ctx
); ctx
->remain
> 0; parse_advance_line(ctx
)) {
402 if (ctx
->line_len
== 0 || ctx
->line
[ctx
->line_len
- 1] != '\n')
405 for (i
= 0; i
< ARRAY_SIZE(header_git_ops
); i
++) {
406 const header_git_op
*op
= &header_git_ops
[i
];
407 size_t op_len
= strlen(op
->str
);
409 if (memcmp(ctx
->line
, op
->str
, min(op_len
, ctx
->line_len
)) != 0)
412 /* Do not advance if this is the patch separator */
416 parse_advance_chars(ctx
, op_len
);
418 if ((error
= op
->fn(patch
, ctx
)) < 0)
421 parse_advance_ws(ctx
);
422 parse_advance_expected_s(ctx
, "\n");
424 if (ctx
->line_len
> 0) {
425 error
= parse_err("trailing data at line %d", ctx
->line_num
);
437 static int parse_number(git_off_t
*out
, patch_parse_ctx
*ctx
)
442 if (!git__isdigit(ctx
->line
[0]))
445 if (git__strntol64(&num
, ctx
->line
, ctx
->line_len
, &end
, 10) < 0)
452 parse_advance_chars(ctx
, (end
- ctx
->line
));
457 static int parse_int(int *out
, patch_parse_ctx
*ctx
)
461 if (parse_number(&num
, ctx
) < 0 || !git__is_int(num
))
468 static int parse_hunk_header(
469 git_patch_hunk
*hunk
,
470 patch_parse_ctx
*ctx
)
472 const char *header_start
= ctx
->line
;
474 hunk
->hunk
.old_lines
= 1;
475 hunk
->hunk
.new_lines
= 1;
477 if (parse_advance_expected_s(ctx
, "@@ -") < 0 ||
478 parse_int(&hunk
->hunk
.old_start
, ctx
) < 0)
481 if (ctx
->line_len
> 0 && ctx
->line
[0] == ',') {
482 if (parse_advance_expected_s(ctx
, ",") < 0 ||
483 parse_int(&hunk
->hunk
.old_lines
, ctx
) < 0)
487 if (parse_advance_expected_s(ctx
, " +") < 0 ||
488 parse_int(&hunk
->hunk
.new_start
, ctx
) < 0)
491 if (ctx
->line_len
> 0 && ctx
->line
[0] == ',') {
492 if (parse_advance_expected_s(ctx
, ",") < 0 ||
493 parse_int(&hunk
->hunk
.new_lines
, ctx
) < 0)
497 if (parse_advance_expected_s(ctx
, " @@") < 0)
500 parse_advance_line(ctx
);
502 if (!hunk
->hunk
.old_lines
&& !hunk
->hunk
.new_lines
)
505 hunk
->hunk
.header_len
= ctx
->line
- header_start
;
506 if (hunk
->hunk
.header_len
> (GIT_DIFF_HUNK_HEADER_SIZE
- 1))
507 return parse_err("oversized patch hunk header at line %d",
510 memcpy(hunk
->hunk
.header
, header_start
, hunk
->hunk
.header_len
);
511 hunk
->hunk
.header
[hunk
->hunk
.header_len
] = '\0';
516 giterr_set(GITERR_PATCH
, "invalid patch hunk header at line %d",
521 static int parse_hunk_body(
522 git_patch_parsed
*patch
,
523 git_patch_hunk
*hunk
,
524 patch_parse_ctx
*ctx
)
529 int oldlines
= hunk
->hunk
.old_lines
;
530 int newlines
= hunk
->hunk
.new_lines
;
533 ctx
->remain
> 4 && (oldlines
|| newlines
) &&
534 memcmp(ctx
->line
, "@@ -", 4) != 0;
535 parse_advance_line(ctx
)) {
540 if (ctx
->line_len
== 0 || ctx
->line
[ctx
->line_len
- 1] != '\n') {
541 error
= parse_err("invalid patch instruction at line %d",
546 switch (ctx
->line
[0]) {
551 origin
= GIT_DIFF_LINE_CONTEXT
;
557 origin
= GIT_DIFF_LINE_DELETION
;
562 origin
= GIT_DIFF_LINE_ADDITION
;
567 error
= parse_err("invalid patch hunk at line %d", ctx
->line_num
);
571 line
= git_array_alloc(patch
->base
.lines
);
572 GITERR_CHECK_ALLOC(line
);
574 memset(line
, 0x0, sizeof(git_diff_line
));
576 line
->content
= ctx
->line
+ prefix
;
577 line
->content_len
= ctx
->line_len
- prefix
;
578 line
->content_offset
= ctx
->content_len
- ctx
->remain
;
579 line
->origin
= origin
;
584 if (oldlines
|| newlines
) {
586 "invalid patch hunk, expected %d old lines and %d new lines",
587 hunk
->hunk
.old_lines
, hunk
->hunk
.new_lines
);
591 /* Handle "\ No newline at end of file". Only expect the leading
592 * backslash, though, because the rest of the string could be
593 * localized. Because `diff` optimizes for the case where you
594 * want to apply the patch by hand.
596 if (ctx
->line_len
>= 2 && memcmp(ctx
->line
, "\\ ", 2) == 0 &&
597 git_array_size(patch
->base
.lines
) > 0) {
599 line
= git_array_get(patch
->base
.lines
, git_array_size(patch
->base
.lines
) - 1);
601 if (line
->content_len
< 1) {
602 error
= parse_err("cannot trim trailing newline of empty line");
608 parse_advance_line(ctx
);
615 static int parsed_patch_header(
616 git_patch_parsed
*patch
,
617 patch_parse_ctx
*ctx
)
621 for (ctx
->line
= ctx
->content
; ctx
->remain
> 0; parse_advance_line(ctx
)) {
622 /* This line is too short to be a patch header. */
623 if (ctx
->line_len
< 6)
626 /* This might be a hunk header without a patch header, provide a
627 * sensible error message. */
628 if (memcmp(ctx
->line
, "@@ -", 4) == 0) {
629 size_t line_num
= ctx
->line_num
;
632 /* If this cannot be parsed as a hunk header, it's just leading
635 if (parse_hunk_header(&hunk
, ctx
) < 0) {
640 error
= parse_err("invalid hunk header outside patch at line %d",
645 /* This buffer is too short to contain a patch. */
646 if (ctx
->remain
< ctx
->line_len
+ 6)
649 /* A proper git patch */
650 if (ctx
->line_len
>= 11 && memcmp(ctx
->line
, "diff --git ", 11) == 0) {
651 error
= parse_header_git(patch
, ctx
);
659 error
= parse_err("no header in patch file");
665 static int parsed_patch_binary_side(
666 git_diff_binary_file
*binary
,
667 patch_parse_ctx
*ctx
)
669 git_diff_binary_t type
= GIT_DIFF_BINARY_NONE
;
670 git_buf base85
= GIT_BUF_INIT
, decoded
= GIT_BUF_INIT
;
674 if (ctx
->line_len
>= 8 && memcmp(ctx
->line
, "literal ", 8) == 0) {
675 type
= GIT_DIFF_BINARY_LITERAL
;
676 parse_advance_chars(ctx
, 8);
678 else if (ctx
->line_len
>= 6 && memcmp(ctx
->line
, "delta ", 6) == 0) {
679 type
= GIT_DIFF_BINARY_DELTA
;
680 parse_advance_chars(ctx
, 6);
683 error
= parse_err("unknown binary delta type at line %d", ctx
->line_num
);
687 if (parse_number(&len
, ctx
) < 0 || parse_advance_nl(ctx
) < 0 || len
< 0) {
688 error
= parse_err("invalid binary size at line %d", ctx
->line_num
);
692 while (ctx
->line_len
) {
693 char c
= ctx
->line
[0];
694 size_t encoded_len
, decoded_len
= 0, decoded_orig
= decoded
.size
;
698 else if (c
>= 'A' && c
<= 'Z')
699 decoded_len
= c
- 'A' + 1;
700 else if (c
>= 'a' && c
<= 'z')
701 decoded_len
= c
- 'a' + (('z' - 'a') + 1) + 1;
704 error
= parse_err("invalid binary length at line %d", ctx
->line_num
);
708 parse_advance_chars(ctx
, 1);
710 encoded_len
= ((decoded_len
/ 4) + !!(decoded_len
% 4)) * 5;
712 if (encoded_len
> ctx
->line_len
- 1) {
713 error
= parse_err("truncated binary data at line %d", ctx
->line_num
);
717 if ((error
= git_buf_decode_base85(
718 &decoded
, ctx
->line
, encoded_len
, decoded_len
)) < 0)
721 if (decoded
.size
- decoded_orig
!= decoded_len
) {
722 error
= parse_err("truncated binary data at line %d", ctx
->line_num
);
726 parse_advance_chars(ctx
, encoded_len
);
728 if (parse_advance_nl(ctx
) < 0) {
729 error
= parse_err("trailing data at line %d", ctx
->line_num
);
735 binary
->inflatedlen
= (size_t)len
;
736 binary
->datalen
= decoded
.size
;
737 binary
->data
= git_buf_detach(&decoded
);
740 git_buf_free(&base85
);
741 git_buf_free(&decoded
);
745 static int parsed_patch_binary(
746 git_patch_parsed
*patch
,
747 patch_parse_ctx
*ctx
)
751 if (parse_advance_expected_s(ctx
, "GIT binary patch") < 0 ||
752 parse_advance_nl(ctx
) < 0)
753 return parse_err("corrupt git binary header at line %d", ctx
->line_num
);
755 /* parse old->new binary diff */
756 if ((error
= parsed_patch_binary_side(
757 &patch
->base
.binary
.new_file
, ctx
)) < 0)
760 if (parse_advance_nl(ctx
) < 0)
761 return parse_err("corrupt git binary separator at line %d",
764 /* parse new->old binary diff */
765 if ((error
= parsed_patch_binary_side(
766 &patch
->base
.binary
.old_file
, ctx
)) < 0)
769 patch
->base
.delta
->flags
|= GIT_DIFF_FLAG_BINARY
;
773 static int parsed_patch_hunks(
774 git_patch_parsed
*patch
,
775 patch_parse_ctx
*ctx
)
777 git_patch_hunk
*hunk
;
780 for (; ctx
->line_len
> 4 && memcmp(ctx
->line
, "@@ -", 4) == 0; ) {
782 hunk
= git_array_alloc(patch
->base
.hunks
);
783 GITERR_CHECK_ALLOC(hunk
);
785 memset(hunk
, 0, sizeof(git_patch_hunk
));
787 hunk
->line_start
= git_array_size(patch
->base
.lines
);
788 hunk
->line_count
= 0;
790 if ((error
= parse_hunk_header(hunk
, ctx
)) < 0 ||
791 (error
= parse_hunk_body(patch
, hunk
, ctx
)) < 0)
799 static int parsed_patch_body(
800 git_patch_parsed
*patch
, patch_parse_ctx
*ctx
)
802 if (ctx
->line_len
>= 16 && memcmp(ctx
->line
, "GIT binary patch", 16) == 0)
803 return parsed_patch_binary(patch
, ctx
);
805 else if (ctx
->line_len
>= 4 && memcmp(ctx
->line
, "@@ -", 4) == 0)
806 return parsed_patch_hunks(patch
, ctx
);
811 int check_header_names(
814 const char *old_or_new
,
820 if (two_null
&& strcmp(two
, "/dev/null") != 0)
821 return parse_err("expected %s path of '/dev/null'", old_or_new
);
823 else if (!two_null
&& strcmp(one
, two
) != 0)
824 return parse_err("mismatched %s path names", old_or_new
);
829 static int check_prefix(
832 git_patch_parsed
*patch
,
833 const char *path_start
)
835 const char *path
= path_start
;
836 uint32_t remain
= patch
->opts
.prefix_len
;
841 if (patch
->opts
.prefix_len
== 0)
844 /* leading slashes do not count as part of the prefix in git apply */
848 while (*path
&& remain
) {
855 if (remain
|| !*path
)
856 return parse_err("header filename does not contain %d path components",
857 patch
->opts
.prefix_len
);
860 *out_len
= (path
- path_start
);
861 *out
= git__strndup(path_start
, *out_len
);
863 return (out
== NULL
) ? -1 : 0;
866 static int check_filenames(git_patch_parsed
*patch
)
868 const char *prefixed_new
, *prefixed_old
;
869 size_t old_prefixlen
= 0, new_prefixlen
= 0;
870 bool added
= (patch
->base
.delta
->status
== GIT_DELTA_ADDED
);
871 bool deleted
= (patch
->base
.delta
->status
== GIT_DELTA_DELETED
);
873 if (patch
->old_path
&& !patch
->new_path
)
874 return parse_err("missing new path");
876 if (!patch
->old_path
&& patch
->new_path
)
877 return parse_err("missing old path");
879 /* Ensure (non-renamed) paths match */
880 if (check_header_names(
881 patch
->header_old_path
, patch
->old_path
, "old", added
) < 0 ||
883 patch
->header_new_path
, patch
->new_path
, "new", deleted
) < 0)
886 prefixed_old
= (!added
&& patch
->old_path
) ? patch
->old_path
:
887 patch
->header_old_path
;
888 prefixed_new
= (!deleted
&& patch
->new_path
) ? patch
->new_path
:
889 patch
->header_new_path
;
892 &patch
->old_prefix
, &old_prefixlen
, patch
, prefixed_old
) < 0 ||
894 &patch
->new_prefix
, &new_prefixlen
, patch
, prefixed_new
) < 0)
897 /* Prefer the rename filenames as they are unambiguous and unprefixed */
898 if (patch
->rename_old_path
)
899 patch
->base
.delta
->old_file
.path
= patch
->rename_old_path
;
901 patch
->base
.delta
->old_file
.path
= prefixed_old
+ old_prefixlen
;
903 if (patch
->rename_new_path
)
904 patch
->base
.delta
->new_file
.path
= patch
->rename_new_path
;
906 patch
->base
.delta
->new_file
.path
= prefixed_new
+ new_prefixlen
;
908 if (!patch
->base
.delta
->old_file
.path
&&
909 !patch
->base
.delta
->new_file
.path
)
910 return parse_err("git diff header lacks old / new paths");
915 static int check_patch(git_patch_parsed
*patch
)
917 if (check_filenames(patch
) < 0)
920 if (patch
->base
.delta
->old_file
.path
&&
921 patch
->base
.delta
->status
!= GIT_DELTA_DELETED
&&
922 !patch
->base
.delta
->new_file
.mode
)
923 patch
->base
.delta
->new_file
.mode
= patch
->base
.delta
->old_file
.mode
;
925 if (patch
->base
.delta
->status
== GIT_DELTA_MODIFIED
&&
926 !(patch
->base
.delta
->flags
& GIT_DIFF_FLAG_BINARY
) &&
927 patch
->base
.delta
->new_file
.mode
== patch
->base
.delta
->old_file
.mode
&&
928 git_array_size(patch
->base
.hunks
) == 0)
929 return parse_err("patch with no hunks");
934 static void patch_parsed__free(git_patch
*p
)
936 git_patch_parsed
*patch
= (git_patch_parsed
*)p
;
941 git__free((char *)patch
->base
.binary
.old_file
.data
);
942 git__free((char *)patch
->base
.binary
.new_file
.data
);
943 git_array_clear(patch
->base
.hunks
);
944 git_array_clear(patch
->base
.lines
);
945 git__free(patch
->base
.delta
);
947 git__free(patch
->old_prefix
);
948 git__free(patch
->new_prefix
);
949 git__free(patch
->header_old_path
);
950 git__free(patch
->header_new_path
);
951 git__free(patch
->rename_old_path
);
952 git__free(patch
->rename_new_path
);
953 git__free(patch
->old_path
);
954 git__free(patch
->new_path
);
955 git__free(patch
->content
);
959 int git_patch_from_buffer(
963 git_patch_options
*opts
)
965 patch_parse_ctx ctx
= { 0 };
966 git_patch_parsed
*patch
;
967 git_patch_options default_opts
= GIT_PATCH_OPTIONS_INIT
;
972 patch
= git__calloc(1, sizeof(git_patch_parsed
));
973 GITERR_CHECK_ALLOC(patch
);
976 memcpy(&patch
->opts
, opts
, sizeof(git_patch_options
));
978 memcpy(&patch
->opts
, &default_opts
, sizeof(git_patch_options
));
980 patch
->base
.free_fn
= patch_parsed__free
;
982 patch
->base
.delta
= git__calloc(1, sizeof(git_diff_delta
));
983 GITERR_CHECK_ALLOC(patch
->base
.delta
);
985 patch
->base
.delta
->status
= GIT_DELTA_MODIFIED
;
986 patch
->base
.delta
->nfiles
= 2;
989 patch
->content
= git__malloc(content_len
);
990 GITERR_CHECK_ALLOC(patch
->content
);
992 memcpy(patch
->content
, content
, content_len
);
995 ctx
.content
= patch
->content
;
996 ctx
.content_len
= content_len
;
997 ctx
.remain
= content_len
;
999 if ((error
= parsed_patch_header(patch
, &ctx
)) < 0 ||
1000 (error
= parsed_patch_body(patch
, &ctx
)) < 0 ||
1001 (error
= check_patch(patch
)) < 0)
1004 patch
->base
.diff_opts
.old_prefix
= patch
->old_prefix
;
1005 patch
->base
.diff_opts
.new_prefix
= patch
->new_prefix
;
1006 patch
->base
.diff_opts
.flags
|= GIT_DIFF_SHOW_BINARY
;
1008 GIT_REFCOUNT_INC(patch
);
1009 *out
= &patch
->base
;
1013 patch_parsed__free(&patch
->base
);