rugged 0.25.0b5 → 0.25.0b6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rugged/rugged_patch.c +77 -17
  3. data/lib/rugged/version.rb +1 -1
  4. data/vendor/libgit2/CMakeLists.txt +8 -6
  5. data/vendor/libgit2/include/git2/checkout.h +7 -0
  6. data/vendor/libgit2/include/git2/common.h +1 -0
  7. data/vendor/libgit2/include/git2/diff.h +54 -7
  8. data/vendor/libgit2/include/git2/errors.h +2 -1
  9. data/vendor/libgit2/include/git2/index.h +25 -0
  10. data/vendor/libgit2/include/git2/pack.h +4 -4
  11. data/vendor/libgit2/include/git2/repository.h +20 -1
  12. data/vendor/libgit2/include/git2/submodule.h +11 -3
  13. data/vendor/libgit2/include/git2/sys/odb_backend.h +11 -0
  14. data/vendor/libgit2/src/apply.c +369 -0
  15. data/vendor/libgit2/src/apply.h +21 -0
  16. data/vendor/libgit2/src/array.h +0 -1
  17. data/vendor/libgit2/src/blame_git.c +18 -8
  18. data/vendor/libgit2/src/buffer.c +252 -20
  19. data/vendor/libgit2/src/buffer.h +8 -0
  20. data/vendor/libgit2/src/checkout.c +7 -1
  21. data/vendor/libgit2/src/clone.c +0 -8
  22. data/vendor/libgit2/src/common.h +1 -1
  23. data/vendor/libgit2/src/crlf.c +1 -0
  24. data/vendor/libgit2/src/delta.c +238 -62
  25. data/vendor/libgit2/src/delta.h +79 -58
  26. data/vendor/libgit2/src/diff.c +32 -1547
  27. data/vendor/libgit2/src/diff.h +12 -122
  28. data/vendor/libgit2/src/diff_driver.c +1 -2
  29. data/vendor/libgit2/src/diff_file.c +3 -0
  30. data/vendor/libgit2/src/diff_generate.c +1611 -0
  31. data/vendor/libgit2/src/diff_generate.h +123 -0
  32. data/vendor/libgit2/src/diff_parse.c +105 -0
  33. data/vendor/libgit2/src/diff_print.c +259 -142
  34. data/vendor/libgit2/src/diff_stats.c +3 -2
  35. data/vendor/libgit2/src/diff_tform.c +1 -0
  36. data/vendor/libgit2/src/diff_tform.h +22 -0
  37. data/vendor/libgit2/src/diff_xdiff.c +9 -9
  38. data/vendor/libgit2/src/diff_xdiff.h +5 -5
  39. data/vendor/libgit2/src/fileops.c +13 -0
  40. data/vendor/libgit2/src/fileops.h +6 -0
  41. data/vendor/libgit2/src/global.c +9 -8
  42. data/vendor/libgit2/src/ignore.c +45 -16
  43. data/vendor/libgit2/src/index.c +161 -47
  44. data/vendor/libgit2/src/index.h +2 -0
  45. data/vendor/libgit2/src/merge.c +2 -0
  46. data/vendor/libgit2/src/mwindow.c +8 -19
  47. data/vendor/libgit2/src/mwindow.h +1 -2
  48. data/vendor/libgit2/src/odb.c +46 -10
  49. data/vendor/libgit2/src/odb_loose.c +19 -1
  50. data/vendor/libgit2/src/odb_pack.c +27 -4
  51. data/vendor/libgit2/src/pack-objects.c +106 -77
  52. data/vendor/libgit2/src/pack-objects.h +13 -12
  53. data/vendor/libgit2/src/pack.c +4 -3
  54. data/vendor/libgit2/src/pack.h +2 -0
  55. data/vendor/libgit2/src/patch.c +207 -0
  56. data/vendor/libgit2/src/patch.h +66 -0
  57. data/vendor/libgit2/src/{diff_patch.c → patch_generate.c} +171 -359
  58. data/vendor/libgit2/src/patch_generate.h +66 -0
  59. data/vendor/libgit2/src/patch_parse.c +1121 -0
  60. data/vendor/libgit2/src/patch_parse.h +54 -0
  61. data/vendor/libgit2/src/path.c +19 -0
  62. data/vendor/libgit2/src/path.h +6 -0
  63. data/vendor/libgit2/src/pool.h +5 -0
  64. data/vendor/libgit2/src/remote.c +15 -80
  65. data/vendor/libgit2/src/repository.c +227 -39
  66. data/vendor/libgit2/src/settings.c +11 -5
  67. data/vendor/libgit2/src/stash.c +1 -0
  68. data/vendor/libgit2/src/status.c +1 -0
  69. data/vendor/libgit2/src/stransport_stream.c +14 -9
  70. data/vendor/libgit2/src/submodule.c +16 -4
  71. data/vendor/libgit2/src/sysdir.c +41 -47
  72. data/vendor/libgit2/src/sysdir.h +0 -5
  73. data/vendor/libgit2/src/thread-utils.h +5 -51
  74. data/vendor/libgit2/src/transport.c +3 -5
  75. data/vendor/libgit2/src/transports/http.c +2 -3
  76. data/vendor/libgit2/src/transports/smart_pkt.c +1 -0
  77. data/vendor/libgit2/src/transports/smart_protocol.c +11 -0
  78. data/vendor/libgit2/src/transports/winhttp.c +16 -2
  79. data/vendor/libgit2/src/unix/pthread.h +54 -0
  80. data/vendor/libgit2/src/util.c +23 -5
  81. data/vendor/libgit2/src/util.h +10 -0
  82. data/vendor/libgit2/src/varint.c +44 -0
  83. data/vendor/libgit2/src/varint.h +15 -0
  84. data/vendor/libgit2/src/vector.c +42 -0
  85. data/vendor/libgit2/src/vector.h +3 -0
  86. data/vendor/libgit2/src/win32/precompiled.h +1 -1
  87. data/vendor/libgit2/src/win32/{pthread.c → thread.c} +50 -80
  88. data/vendor/libgit2/src/win32/thread.h +62 -0
  89. data/vendor/libgit2/src/zstream.c +36 -7
  90. data/vendor/libgit2/src/zstream.h +8 -1
  91. metadata +20 -9
  92. data/vendor/libgit2/src/delta-apply.c +0 -166
  93. data/vendor/libgit2/src/delta-apply.h +0 -62
  94. data/vendor/libgit2/src/diff_patch.h +0 -83
  95. data/vendor/libgit2/src/win32/pthread.h +0 -92
@@ -0,0 +1,66 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #ifndef INCLUDE_patch_generate_h__
8
+ #define INCLUDE_patch_generate_h__
9
+
10
+ #include "common.h"
11
+ #include "diff.h"
12
+ #include "diff_file.h"
13
+ #include "patch.h"
14
+
15
+ enum {
16
+ GIT_PATCH_GENERATED_ALLOCATED = (1 << 0),
17
+ GIT_PATCH_GENERATED_INITIALIZED = (1 << 1),
18
+ GIT_PATCH_GENERATED_LOADED = (1 << 2),
19
+ /* the two sides are different */
20
+ GIT_PATCH_GENERATED_DIFFABLE = (1 << 3),
21
+ /* the difference between the two sides has been computed */
22
+ GIT_PATCH_GENERATED_DIFFED = (1 << 4),
23
+ GIT_PATCH_GENERATED_FLATTENED = (1 << 5),
24
+ };
25
+
26
+ struct git_patch_generated {
27
+ struct git_patch base;
28
+
29
+ git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
30
+ size_t delta_index;
31
+ git_diff_file_content ofile;
32
+ git_diff_file_content nfile;
33
+ uint32_t flags;
34
+ git_pool flattened;
35
+ };
36
+
37
+ typedef struct git_patch_generated git_patch_generated;
38
+
39
+ extern git_diff_driver *git_patch_generated_driver(git_patch_generated *);
40
+
41
+ extern void git_patch_generated_old_data(
42
+ char **, size_t *, git_patch_generated *);
43
+ extern void git_patch_generated_new_data(
44
+ char **, size_t *, git_patch_generated *);
45
+
46
+ typedef struct git_patch_generated_output git_patch_generated_output;
47
+
48
+ struct git_patch_generated_output {
49
+ /* these callbacks are issued with the diff data */
50
+ git_diff_file_cb file_cb;
51
+ git_diff_binary_cb binary_cb;
52
+ git_diff_hunk_cb hunk_cb;
53
+ git_diff_line_cb data_cb;
54
+ void *payload;
55
+
56
+ /* this records the actual error in cases where it may be obscured */
57
+ int error;
58
+
59
+ /* this callback is used to do the diff and drive the other callbacks.
60
+ * see diff_xdiff.h for how to use this in practice for now.
61
+ */
62
+ int (*diff_cb)(git_patch_generated_output *output,
63
+ git_patch_generated *patch);
64
+ };
65
+
66
+ #endif
@@ -0,0 +1,1121 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #include "git2/patch.h"
8
+ #include "patch.h"
9
+ #include "patch_parse.h"
10
+ #include "path.h"
11
+
12
+ #define parse_err(...) \
13
+ ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
14
+
15
+ typedef struct {
16
+ git_patch base;
17
+
18
+ git_patch_parse_ctx *ctx;
19
+
20
+ /* the paths from the `diff --git` header, these will be used if this is not
21
+ * a rename (and rename paths are specified) or if no `+++`/`---` line specify
22
+ * the paths.
23
+ */
24
+ char *header_old_path, *header_new_path;
25
+
26
+ /* renamed paths are precise and are not prefixed */
27
+ char *rename_old_path, *rename_new_path;
28
+
29
+ /* the paths given in `---` and `+++` lines */
30
+ char *old_path, *new_path;
31
+
32
+ /* the prefixes from the old/new paths */
33
+ char *old_prefix, *new_prefix;
34
+ } git_patch_parsed;
35
+
36
+
37
+ GIT_INLINE(bool) parse_ctx_contains(
38
+ git_patch_parse_ctx *ctx, const char *str, size_t len)
39
+ {
40
+ return (ctx->line_len >= len && memcmp(ctx->line, str, len) == 0);
41
+ }
42
+
43
+ #define parse_ctx_contains_s(ctx, str) \
44
+ parse_ctx_contains(ctx, str, sizeof(str) - 1)
45
+
46
+ static void parse_advance_line(git_patch_parse_ctx *ctx)
47
+ {
48
+ ctx->line += ctx->line_len;
49
+ ctx->remain_len -= ctx->line_len;
50
+ ctx->line_len = git__linenlen(ctx->line, ctx->remain_len);
51
+ ctx->line_num++;
52
+ }
53
+
54
+ static void parse_advance_chars(git_patch_parse_ctx *ctx, size_t char_cnt)
55
+ {
56
+ ctx->line += char_cnt;
57
+ ctx->remain_len -= char_cnt;
58
+ ctx->line_len -= char_cnt;
59
+ }
60
+
61
+ static int parse_advance_expected(
62
+ git_patch_parse_ctx *ctx,
63
+ const char *expected,
64
+ size_t expected_len)
65
+ {
66
+ if (ctx->line_len < expected_len)
67
+ return -1;
68
+
69
+ if (memcmp(ctx->line, expected, expected_len) != 0)
70
+ return -1;
71
+
72
+ parse_advance_chars(ctx, expected_len);
73
+ return 0;
74
+ }
75
+
76
+ #define parse_advance_expected_s(ctx, str) \
77
+ parse_advance_expected(ctx, str, sizeof(str) - 1)
78
+
79
+ static int parse_advance_ws(git_patch_parse_ctx *ctx)
80
+ {
81
+ int ret = -1;
82
+
83
+ while (ctx->line_len > 0 &&
84
+ ctx->line[0] != '\n' &&
85
+ git__isspace(ctx->line[0])) {
86
+ ctx->line++;
87
+ ctx->line_len--;
88
+ ctx->remain_len--;
89
+ ret = 0;
90
+ }
91
+
92
+ return ret;
93
+ }
94
+
95
+ static int parse_advance_nl(git_patch_parse_ctx *ctx)
96
+ {
97
+ if (ctx->line_len != 1 || ctx->line[0] != '\n')
98
+ return -1;
99
+
100
+ parse_advance_line(ctx);
101
+ return 0;
102
+ }
103
+
104
+ static int header_path_len(git_patch_parse_ctx *ctx)
105
+ {
106
+ bool inquote = 0;
107
+ bool quoted = (ctx->line_len > 0 && ctx->line[0] == '"');
108
+ size_t len;
109
+
110
+ for (len = quoted; len < ctx->line_len; len++) {
111
+ if (!quoted && git__isspace(ctx->line[len]))
112
+ break;
113
+ else if (quoted && !inquote && ctx->line[len] == '"') {
114
+ len++;
115
+ break;
116
+ }
117
+
118
+ inquote = (!inquote && ctx->line[len] == '\\');
119
+ }
120
+
121
+ return len;
122
+ }
123
+
124
+ static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx)
125
+ {
126
+ int path_len, error = 0;
127
+
128
+ path_len = header_path_len(ctx);
129
+
130
+ if ((error = git_buf_put(path, ctx->line, path_len)) < 0)
131
+ goto done;
132
+
133
+ parse_advance_chars(ctx, path_len);
134
+
135
+ git_buf_rtrim(path);
136
+
137
+ if (path->size > 0 && path->ptr[0] == '"')
138
+ error = git_buf_unquote(path);
139
+
140
+ if (error < 0)
141
+ goto done;
142
+
143
+ git_path_squash_slashes(path);
144
+
145
+ done:
146
+ return error;
147
+ }
148
+
149
+ static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
150
+ {
151
+ git_buf path = GIT_BUF_INIT;
152
+ int error = parse_header_path_buf(&path, ctx);
153
+
154
+ *out = git_buf_detach(&path);
155
+
156
+ return error;
157
+ }
158
+
159
+ static int parse_header_git_oldpath(
160
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
161
+ {
162
+ return parse_header_path(&patch->old_path, ctx);
163
+ }
164
+
165
+ static int parse_header_git_newpath(
166
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
167
+ {
168
+ return parse_header_path(&patch->new_path, ctx);
169
+ }
170
+
171
+ static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx)
172
+ {
173
+ const char *end;
174
+ int32_t m;
175
+ int ret;
176
+
177
+ if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))
178
+ return parse_err("invalid file mode at line %d", ctx->line_num);
179
+
180
+ if ((ret = git__strntol32(&m, ctx->line, ctx->line_len, &end, 8)) < 0)
181
+ return ret;
182
+
183
+ if (m > UINT16_MAX)
184
+ return -1;
185
+
186
+ *mode = (uint16_t)m;
187
+
188
+ parse_advance_chars(ctx, (end - ctx->line));
189
+
190
+ return ret;
191
+ }
192
+
193
+ static int parse_header_oid(
194
+ git_oid *oid,
195
+ uint16_t *oid_len,
196
+ git_patch_parse_ctx *ctx)
197
+ {
198
+ size_t len;
199
+
200
+ for (len = 0; len < ctx->line_len && len < GIT_OID_HEXSZ; len++) {
201
+ if (!git__isxdigit(ctx->line[len]))
202
+ break;
203
+ }
204
+
205
+ if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ ||
206
+ git_oid_fromstrn(oid, ctx->line, len) < 0)
207
+ return parse_err("invalid hex formatted object id at line %d",
208
+ ctx->line_num);
209
+
210
+ parse_advance_chars(ctx, len);
211
+
212
+ *oid_len = (uint16_t)len;
213
+
214
+ return 0;
215
+ }
216
+
217
+ static int parse_header_git_index(
218
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
219
+ {
220
+ if (parse_header_oid(&patch->base.delta->old_file.id,
221
+ &patch->base.delta->old_file.id_abbrev, ctx) < 0 ||
222
+ parse_advance_expected_s(ctx, "..") < 0 ||
223
+ parse_header_oid(&patch->base.delta->new_file.id,
224
+ &patch->base.delta->new_file.id_abbrev, ctx) < 0)
225
+ return -1;
226
+
227
+ if (ctx->line_len > 0 && ctx->line[0] == ' ') {
228
+ uint16_t mode;
229
+
230
+ parse_advance_chars(ctx, 1);
231
+
232
+ if (parse_header_mode(&mode, ctx) < 0)
233
+ return -1;
234
+
235
+ if (!patch->base.delta->new_file.mode)
236
+ patch->base.delta->new_file.mode = mode;
237
+
238
+ if (!patch->base.delta->old_file.mode)
239
+ patch->base.delta->old_file.mode = mode;
240
+ }
241
+
242
+ return 0;
243
+ }
244
+
245
+ static int parse_header_git_oldmode(
246
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
247
+ {
248
+ return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
249
+ }
250
+
251
+ static int parse_header_git_newmode(
252
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
253
+ {
254
+ return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
255
+ }
256
+
257
+ static int parse_header_git_deletedfilemode(
258
+ git_patch_parsed *patch,
259
+ git_patch_parse_ctx *ctx)
260
+ {
261
+ git__free((char *)patch->base.delta->old_file.path);
262
+
263
+ patch->base.delta->old_file.path = NULL;
264
+ patch->base.delta->status = GIT_DELTA_DELETED;
265
+ patch->base.delta->nfiles = 1;
266
+
267
+ return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
268
+ }
269
+
270
+ static int parse_header_git_newfilemode(
271
+ git_patch_parsed *patch,
272
+ git_patch_parse_ctx *ctx)
273
+ {
274
+ git__free((char *)patch->base.delta->new_file.path);
275
+
276
+ patch->base.delta->new_file.path = NULL;
277
+ patch->base.delta->status = GIT_DELTA_ADDED;
278
+ patch->base.delta->nfiles = 1;
279
+
280
+ return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
281
+ }
282
+
283
+ static int parse_header_rename(
284
+ char **out,
285
+ git_patch_parse_ctx *ctx)
286
+ {
287
+ git_buf path = GIT_BUF_INIT;
288
+
289
+ if (parse_header_path_buf(&path, ctx) < 0)
290
+ return -1;
291
+
292
+ /* Note: the `rename from` and `rename to` lines include the literal
293
+ * filename. They do *not* include the prefix. (Who needs consistency?)
294
+ */
295
+ *out = git_buf_detach(&path);
296
+ return 0;
297
+ }
298
+
299
+ static int parse_header_renamefrom(
300
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
301
+ {
302
+ patch->base.delta->status = GIT_DELTA_RENAMED;
303
+ return parse_header_rename(&patch->rename_old_path, ctx);
304
+ }
305
+
306
+ static int parse_header_renameto(
307
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
308
+ {
309
+ patch->base.delta->status = GIT_DELTA_RENAMED;
310
+ return parse_header_rename(&patch->rename_new_path, ctx);
311
+ }
312
+
313
+ static int parse_header_copyfrom(
314
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
315
+ {
316
+ patch->base.delta->status = GIT_DELTA_COPIED;
317
+ return parse_header_rename(&patch->rename_old_path, ctx);
318
+ }
319
+
320
+ static int parse_header_copyto(
321
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
322
+ {
323
+ patch->base.delta->status = GIT_DELTA_COPIED;
324
+ return parse_header_rename(&patch->rename_new_path, ctx);
325
+ }
326
+
327
+ static int parse_header_percent(uint16_t *out, git_patch_parse_ctx *ctx)
328
+ {
329
+ int32_t val;
330
+ const char *end;
331
+
332
+ if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]) ||
333
+ git__strntol32(&val, ctx->line, ctx->line_len, &end, 10) < 0)
334
+ return -1;
335
+
336
+ parse_advance_chars(ctx, (end - ctx->line));
337
+
338
+ if (parse_advance_expected_s(ctx, "%") < 0)
339
+ return -1;
340
+
341
+ if (val > 100)
342
+ return -1;
343
+
344
+ *out = val;
345
+ return 0;
346
+ }
347
+
348
+ static int parse_header_similarity(
349
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
350
+ {
351
+ if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0)
352
+ return parse_err("invalid similarity percentage at line %d",
353
+ ctx->line_num);
354
+
355
+ return 0;
356
+ }
357
+
358
+ static int parse_header_dissimilarity(
359
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
360
+ {
361
+ uint16_t dissimilarity;
362
+
363
+ if (parse_header_percent(&dissimilarity, ctx) < 0)
364
+ return parse_err("invalid similarity percentage at line %d",
365
+ ctx->line_num);
366
+
367
+ patch->base.delta->similarity = 100 - dissimilarity;
368
+
369
+ return 0;
370
+ }
371
+
372
+ typedef struct {
373
+ const char *str;
374
+ int(*fn)(git_patch_parsed *, git_patch_parse_ctx *);
375
+ } header_git_op;
376
+
377
+ static const header_git_op header_git_ops[] = {
378
+ { "diff --git ", NULL },
379
+ { "@@ -", NULL },
380
+ { "GIT binary patch", NULL },
381
+ { "--- ", parse_header_git_oldpath },
382
+ { "+++ ", parse_header_git_newpath },
383
+ { "index ", parse_header_git_index },
384
+ { "old mode ", parse_header_git_oldmode },
385
+ { "new mode ", parse_header_git_newmode },
386
+ { "deleted file mode ", parse_header_git_deletedfilemode },
387
+ { "new file mode ", parse_header_git_newfilemode },
388
+ { "rename from ", parse_header_renamefrom },
389
+ { "rename to ", parse_header_renameto },
390
+ { "rename old ", parse_header_renamefrom },
391
+ { "rename new ", parse_header_renameto },
392
+ { "copy from ", parse_header_copyfrom },
393
+ { "copy to ", parse_header_copyto },
394
+ { "similarity index ", parse_header_similarity },
395
+ { "dissimilarity index ", parse_header_dissimilarity },
396
+ };
397
+
398
+ static int parse_header_git(
399
+ git_patch_parsed *patch,
400
+ git_patch_parse_ctx *ctx)
401
+ {
402
+ size_t i;
403
+ int error = 0;
404
+
405
+ /* Parse the diff --git line */
406
+ if (parse_advance_expected_s(ctx, "diff --git ") < 0)
407
+ return parse_err("corrupt git diff header at line %d", ctx->line_num);
408
+
409
+ if (parse_header_path(&patch->header_old_path, ctx) < 0)
410
+ return parse_err("corrupt old path in git diff header at line %d",
411
+ ctx->line_num);
412
+
413
+ if (parse_advance_ws(ctx) < 0 ||
414
+ parse_header_path(&patch->header_new_path, ctx) < 0)
415
+ return parse_err("corrupt new path in git diff header at line %d",
416
+ ctx->line_num);
417
+
418
+ /* Parse remaining header lines */
419
+ for (parse_advance_line(ctx);
420
+ ctx->remain_len > 0;
421
+ parse_advance_line(ctx)) {
422
+
423
+ bool found = false;
424
+
425
+ if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n')
426
+ break;
427
+
428
+ for (i = 0; i < ARRAY_SIZE(header_git_ops); i++) {
429
+ const header_git_op *op = &header_git_ops[i];
430
+ size_t op_len = strlen(op->str);
431
+
432
+ if (memcmp(ctx->line, op->str, min(op_len, ctx->line_len)) != 0)
433
+ continue;
434
+
435
+ /* Do not advance if this is the patch separator */
436
+ if (op->fn == NULL)
437
+ goto done;
438
+
439
+ parse_advance_chars(ctx, op_len);
440
+
441
+ if ((error = op->fn(patch, ctx)) < 0)
442
+ goto done;
443
+
444
+ parse_advance_ws(ctx);
445
+ parse_advance_expected_s(ctx, "\n");
446
+
447
+ if (ctx->line_len > 0) {
448
+ error = parse_err("trailing data at line %d", ctx->line_num);
449
+ goto done;
450
+ }
451
+
452
+ found = true;
453
+ break;
454
+ }
455
+
456
+ if (!found) {
457
+ error = parse_err("invalid patch header at line %d",
458
+ ctx->line_num);
459
+ goto done;
460
+ }
461
+ }
462
+
463
+ done:
464
+ return error;
465
+ }
466
+
467
+ static int parse_number(git_off_t *out, git_patch_parse_ctx *ctx)
468
+ {
469
+ const char *end;
470
+ int64_t num;
471
+
472
+ if (!git__isdigit(ctx->line[0]))
473
+ return -1;
474
+
475
+ if (git__strntol64(&num, ctx->line, ctx->line_len, &end, 10) < 0)
476
+ return -1;
477
+
478
+ if (num < 0)
479
+ return -1;
480
+
481
+ *out = num;
482
+ parse_advance_chars(ctx, (end - ctx->line));
483
+
484
+ return 0;
485
+ }
486
+
487
+ static int parse_int(int *out, git_patch_parse_ctx *ctx)
488
+ {
489
+ git_off_t num;
490
+
491
+ if (parse_number(&num, ctx) < 0 || !git__is_int(num))
492
+ return -1;
493
+
494
+ *out = (int)num;
495
+ return 0;
496
+ }
497
+
498
+ static int parse_hunk_header(
499
+ git_patch_hunk *hunk,
500
+ git_patch_parse_ctx *ctx)
501
+ {
502
+ const char *header_start = ctx->line;
503
+
504
+ hunk->hunk.old_lines = 1;
505
+ hunk->hunk.new_lines = 1;
506
+
507
+ if (parse_advance_expected_s(ctx, "@@ -") < 0 ||
508
+ parse_int(&hunk->hunk.old_start, ctx) < 0)
509
+ goto fail;
510
+
511
+ if (ctx->line_len > 0 && ctx->line[0] == ',') {
512
+ if (parse_advance_expected_s(ctx, ",") < 0 ||
513
+ parse_int(&hunk->hunk.old_lines, ctx) < 0)
514
+ goto fail;
515
+ }
516
+
517
+ if (parse_advance_expected_s(ctx, " +") < 0 ||
518
+ parse_int(&hunk->hunk.new_start, ctx) < 0)
519
+ goto fail;
520
+
521
+ if (ctx->line_len > 0 && ctx->line[0] == ',') {
522
+ if (parse_advance_expected_s(ctx, ",") < 0 ||
523
+ parse_int(&hunk->hunk.new_lines, ctx) < 0)
524
+ goto fail;
525
+ }
526
+
527
+ if (parse_advance_expected_s(ctx, " @@") < 0)
528
+ goto fail;
529
+
530
+ parse_advance_line(ctx);
531
+
532
+ if (!hunk->hunk.old_lines && !hunk->hunk.new_lines)
533
+ goto fail;
534
+
535
+ hunk->hunk.header_len = ctx->line - header_start;
536
+ if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))
537
+ return parse_err("oversized patch hunk header at line %d",
538
+ ctx->line_num);
539
+
540
+ memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len);
541
+ hunk->hunk.header[hunk->hunk.header_len] = '\0';
542
+
543
+ return 0;
544
+
545
+ fail:
546
+ giterr_set(GITERR_PATCH, "invalid patch hunk header at line %d",
547
+ ctx->line_num);
548
+ return -1;
549
+ }
550
+
551
+ static int parse_hunk_body(
552
+ git_patch_parsed *patch,
553
+ git_patch_hunk *hunk,
554
+ git_patch_parse_ctx *ctx)
555
+ {
556
+ git_diff_line *line;
557
+ int error = 0;
558
+
559
+ int oldlines = hunk->hunk.old_lines;
560
+ int newlines = hunk->hunk.new_lines;
561
+
562
+ for (;
563
+ ctx->remain_len > 4 && (oldlines || newlines) &&
564
+ memcmp(ctx->line, "@@ -", 4) != 0;
565
+ parse_advance_line(ctx)) {
566
+
567
+ int origin;
568
+ int prefix = 1;
569
+
570
+ if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n') {
571
+ error = parse_err("invalid patch instruction at line %d",
572
+ ctx->line_num);
573
+ goto done;
574
+ }
575
+
576
+ switch (ctx->line[0]) {
577
+ case '\n':
578
+ prefix = 0;
579
+
580
+ case ' ':
581
+ origin = GIT_DIFF_LINE_CONTEXT;
582
+ oldlines--;
583
+ newlines--;
584
+ break;
585
+
586
+ case '-':
587
+ origin = GIT_DIFF_LINE_DELETION;
588
+ oldlines--;
589
+ break;
590
+
591
+ case '+':
592
+ origin = GIT_DIFF_LINE_ADDITION;
593
+ newlines--;
594
+ break;
595
+
596
+ default:
597
+ error = parse_err("invalid patch hunk at line %d", ctx->line_num);
598
+ goto done;
599
+ }
600
+
601
+ line = git_array_alloc(patch->base.lines);
602
+ GITERR_CHECK_ALLOC(line);
603
+
604
+ memset(line, 0x0, sizeof(git_diff_line));
605
+
606
+ line->content = ctx->line + prefix;
607
+ line->content_len = ctx->line_len - prefix;
608
+ line->content_offset = ctx->content_len - ctx->remain_len;
609
+ line->origin = origin;
610
+
611
+ hunk->line_count++;
612
+ }
613
+
614
+ if (oldlines || newlines) {
615
+ error = parse_err(
616
+ "invalid patch hunk, expected %d old lines and %d new lines",
617
+ hunk->hunk.old_lines, hunk->hunk.new_lines);
618
+ goto done;
619
+ }
620
+
621
+ /* Handle "". Only expect the leading
622
+ * backslash, though, because the rest of the string could be
623
+ * localized. Because `diff` optimizes for the case where you
624
+ * want to apply the patch by hand.
625
+ */
626
+ if (parse_ctx_contains_s(ctx, "\\ ") &&
627
+ git_array_size(patch->base.lines) > 0) {
628
+
629
+ line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);
630
+
631
+ if (line->content_len < 1) {
632
+ error = parse_err("cannot trim trailing newline of empty line");
633
+ goto done;
634
+ }
635
+
636
+ line->content_len--;
637
+
638
+ parse_advance_line(ctx);
639
+ }
640
+
641
+ done:
642
+ return error;
643
+ }
644
+
645
+ static int parse_patch_header(
646
+ git_patch_parsed *patch,
647
+ git_patch_parse_ctx *ctx)
648
+ {
649
+ int error = 0;
650
+
651
+ for (ctx->line = ctx->remain;
652
+ ctx->remain_len > 0;
653
+ parse_advance_line(ctx)) {
654
+
655
+ /* This line is too short to be a patch header. */
656
+ if (ctx->line_len < 6)
657
+ continue;
658
+
659
+ /* This might be a hunk header without a patch header, provide a
660
+ * sensible error message. */
661
+ if (parse_ctx_contains_s(ctx, "@@ -")) {
662
+ size_t line_num = ctx->line_num;
663
+ git_patch_hunk hunk;
664
+
665
+ /* If this cannot be parsed as a hunk header, it's just leading
666
+ * noise, continue.
667
+ */
668
+ if (parse_hunk_header(&hunk, ctx) < 0) {
669
+ giterr_clear();
670
+ continue;
671
+ }
672
+
673
+ error = parse_err("invalid hunk header outside patch at line %d",
674
+ line_num);
675
+ goto done;
676
+ }
677
+
678
+ /* This buffer is too short to contain a patch. */
679
+ if (ctx->remain_len < ctx->line_len + 6)
680
+ break;
681
+
682
+ /* A proper git patch */
683
+ if (parse_ctx_contains_s(ctx, "diff --git ")) {
684
+ error = parse_header_git(patch, ctx);
685
+ goto done;
686
+ }
687
+
688
+ error = 0;
689
+ continue;
690
+ }
691
+
692
+ giterr_set(GITERR_PATCH, "no patch found");
693
+ error = GIT_ENOTFOUND;
694
+
695
+ done:
696
+ return error;
697
+ }
698
+
699
+ static int parse_patch_binary_side(
700
+ git_diff_binary_file *binary,
701
+ git_patch_parse_ctx *ctx)
702
+ {
703
+ git_diff_binary_t type = GIT_DIFF_BINARY_NONE;
704
+ git_buf base85 = GIT_BUF_INIT, decoded = GIT_BUF_INIT;
705
+ git_off_t len;
706
+ int error = 0;
707
+
708
+ if (parse_ctx_contains_s(ctx, "literal ")) {
709
+ type = GIT_DIFF_BINARY_LITERAL;
710
+ parse_advance_chars(ctx, 8);
711
+ } else if (parse_ctx_contains_s(ctx, "delta ")) {
712
+ type = GIT_DIFF_BINARY_DELTA;
713
+ parse_advance_chars(ctx, 6);
714
+ } else {
715
+ error = parse_err(
716
+ "unknown binary delta type at line %d", ctx->line_num);
717
+ goto done;
718
+ }
719
+
720
+ if (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) {
721
+ error = parse_err("invalid binary size at line %d", ctx->line_num);
722
+ goto done;
723
+ }
724
+
725
+ while (ctx->line_len) {
726
+ char c = ctx->line[0];
727
+ size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size;
728
+
729
+ if (c == '\n')
730
+ break;
731
+ else if (c >= 'A' && c <= 'Z')
732
+ decoded_len = c - 'A' + 1;
733
+ else if (c >= 'a' && c <= 'z')
734
+ decoded_len = c - 'a' + (('z' - 'a') + 1) + 1;
735
+
736
+ if (!decoded_len) {
737
+ error = parse_err("invalid binary length at line %d", ctx->line_num);
738
+ goto done;
739
+ }
740
+
741
+ parse_advance_chars(ctx, 1);
742
+
743
+ encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5;
744
+
745
+ if (encoded_len > ctx->line_len - 1) {
746
+ error = parse_err("truncated binary data at line %d", ctx->line_num);
747
+ goto done;
748
+ }
749
+
750
+ if ((error = git_buf_decode_base85(
751
+ &decoded, ctx->line, encoded_len, decoded_len)) < 0)
752
+ goto done;
753
+
754
+ if (decoded.size - decoded_orig != decoded_len) {
755
+ error = parse_err("truncated binary data at line %d", ctx->line_num);
756
+ goto done;
757
+ }
758
+
759
+ parse_advance_chars(ctx, encoded_len);
760
+
761
+ if (parse_advance_nl(ctx) < 0) {
762
+ error = parse_err("trailing data at line %d", ctx->line_num);
763
+ goto done;
764
+ }
765
+ }
766
+
767
+ binary->type = type;
768
+ binary->inflatedlen = (size_t)len;
769
+ binary->datalen = decoded.size;
770
+ binary->data = git_buf_detach(&decoded);
771
+
772
+ done:
773
+ git_buf_free(&base85);
774
+ git_buf_free(&decoded);
775
+ return error;
776
+ }
777
+
778
+ static int parse_patch_binary(
779
+ git_patch_parsed *patch,
780
+ git_patch_parse_ctx *ctx)
781
+ {
782
+ int error;
783
+
784
+ if (parse_advance_expected_s(ctx, "GIT binary patch") < 0 ||
785
+ parse_advance_nl(ctx) < 0)
786
+ return parse_err("corrupt git binary header at line %d", ctx->line_num);
787
+
788
+ /* parse old->new binary diff */
789
+ if ((error = parse_patch_binary_side(
790
+ &patch->base.binary.new_file, ctx)) < 0)
791
+ return error;
792
+
793
+ if (parse_advance_nl(ctx) < 0)
794
+ return parse_err("corrupt git binary separator at line %d",
795
+ ctx->line_num);
796
+
797
+ /* parse new->old binary diff */
798
+ if ((error = parse_patch_binary_side(
799
+ &patch->base.binary.old_file, ctx)) < 0)
800
+ return error;
801
+
802
+ if (parse_advance_nl(ctx) < 0)
803
+ return parse_err("corrupt git binary patch separator at line %d",
804
+ ctx->line_num);
805
+
806
+ patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
807
+ return 0;
808
+ }
809
+
810
+ static int parse_patch_hunks(
811
+ git_patch_parsed *patch,
812
+ git_patch_parse_ctx *ctx)
813
+ {
814
+ git_patch_hunk *hunk;
815
+ int error = 0;
816
+
817
+ while (parse_ctx_contains_s(ctx, "@@ -")) {
818
+ hunk = git_array_alloc(patch->base.hunks);
819
+ GITERR_CHECK_ALLOC(hunk);
820
+
821
+ memset(hunk, 0, sizeof(git_patch_hunk));
822
+
823
+ hunk->line_start = git_array_size(patch->base.lines);
824
+ hunk->line_count = 0;
825
+
826
+ if ((error = parse_hunk_header(hunk, ctx)) < 0 ||
827
+ (error = parse_hunk_body(patch, hunk, ctx)) < 0)
828
+ goto done;
829
+ }
830
+
831
+ patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
832
+
833
+ done:
834
+ return error;
835
+ }
836
+
837
+ static int parse_patch_body(
838
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
839
+ {
840
+ if (parse_ctx_contains_s(ctx, "GIT binary patch"))
841
+ return parse_patch_binary(patch, ctx);
842
+ else
843
+ return parse_patch_hunks(patch, ctx);
844
+ }
845
+
846
+ int check_header_names(
847
+ const char *one,
848
+ const char *two,
849
+ const char *old_or_new,
850
+ bool two_null)
851
+ {
852
+ if (!one || !two)
853
+ return 0;
854
+
855
+ if (two_null && strcmp(two, "/dev/null") != 0)
856
+ return parse_err("expected %s path of '/dev/null'", old_or_new);
857
+
858
+ else if (!two_null && strcmp(one, two) != 0)
859
+ return parse_err("mismatched %s path names", old_or_new);
860
+
861
+ return 0;
862
+ }
863
+
864
+ static int check_prefix(
865
+ char **out,
866
+ size_t *out_len,
867
+ git_patch_parsed *patch,
868
+ const char *path_start)
869
+ {
870
+ const char *path = path_start;
871
+ size_t prefix_len = patch->ctx->opts.prefix_len;
872
+ size_t remain_len = prefix_len;
873
+
874
+ *out = NULL;
875
+ *out_len = 0;
876
+
877
+ if (prefix_len == 0)
878
+ goto done;
879
+
880
+ /* leading slashes do not count as part of the prefix in git apply */
881
+ while (*path == '/')
882
+ path++;
883
+
884
+ while (*path && remain_len) {
885
+ if (*path == '/')
886
+ remain_len--;
887
+
888
+ path++;
889
+ }
890
+
891
+ if (remain_len || !*path)
892
+ return parse_err(
893
+ "header filename does not contain %d path components",
894
+ prefix_len);
895
+
896
+ done:
897
+ *out_len = (path - path_start);
898
+ *out = git__strndup(path_start, *out_len);
899
+
900
+ return (*out == NULL) ? -1 : 0;
901
+ }
902
+
903
+ static int check_filenames(git_patch_parsed *patch)
904
+ {
905
+ const char *prefixed_new, *prefixed_old;
906
+ size_t old_prefixlen = 0, new_prefixlen = 0;
907
+ bool added = (patch->base.delta->status == GIT_DELTA_ADDED);
908
+ bool deleted = (patch->base.delta->status == GIT_DELTA_DELETED);
909
+
910
+ if (patch->old_path && !patch->new_path)
911
+ return parse_err("missing new path");
912
+
913
+ if (!patch->old_path && patch->new_path)
914
+ return parse_err("missing old path");
915
+
916
+ /* Ensure (non-renamed) paths match */
917
+ if (check_header_names(
918
+ patch->header_old_path, patch->old_path, "old", added) < 0 ||
919
+ check_header_names(
920
+ patch->header_new_path, patch->new_path, "new", deleted) < 0)
921
+ return -1;
922
+
923
+ prefixed_old = (!added && patch->old_path) ? patch->old_path :
924
+ patch->header_old_path;
925
+ prefixed_new = (!deleted && patch->new_path) ? patch->new_path :
926
+ patch->header_new_path;
927
+
928
+ if (check_prefix(
929
+ &patch->old_prefix, &old_prefixlen, patch, prefixed_old) < 0 ||
930
+ check_prefix(
931
+ &patch->new_prefix, &new_prefixlen, patch, prefixed_new) < 0)
932
+ return -1;
933
+
934
+ /* Prefer the rename filenames as they are unambiguous and unprefixed */
935
+ if (patch->rename_old_path)
936
+ patch->base.delta->old_file.path = patch->rename_old_path;
937
+ else
938
+ patch->base.delta->old_file.path = prefixed_old + old_prefixlen;
939
+
940
+ if (patch->rename_new_path)
941
+ patch->base.delta->new_file.path = patch->rename_new_path;
942
+ else
943
+ patch->base.delta->new_file.path = prefixed_new + new_prefixlen;
944
+
945
+ if (!patch->base.delta->old_file.path &&
946
+ !patch->base.delta->new_file.path)
947
+ return parse_err("git diff header lacks old / new paths");
948
+
949
+ return 0;
950
+ }
951
+
952
+ static int check_patch(git_patch_parsed *patch)
953
+ {
954
+ git_diff_delta *delta = patch->base.delta;
955
+
956
+ if (check_filenames(patch) < 0)
957
+ return -1;
958
+
959
+ if (delta->old_file.path &&
960
+ delta->status != GIT_DELTA_DELETED &&
961
+ !delta->new_file.mode)
962
+ delta->new_file.mode = delta->old_file.mode;
963
+
964
+ if (delta->status == GIT_DELTA_MODIFIED &&
965
+ !(delta->flags & GIT_DIFF_FLAG_BINARY) &&
966
+ delta->new_file.mode == delta->old_file.mode &&
967
+ git_array_size(patch->base.hunks) == 0)
968
+ return parse_err("patch with no hunks");
969
+
970
+ if (delta->status == GIT_DELTA_ADDED) {
971
+ memset(&delta->old_file.id, 0x0, sizeof(git_oid));
972
+ delta->old_file.id_abbrev = 0;
973
+ }
974
+
975
+ if (delta->status == GIT_DELTA_DELETED) {
976
+ memset(&delta->new_file.id, 0x0, sizeof(git_oid));
977
+ delta->new_file.id_abbrev = 0;
978
+ }
979
+
980
+ return 0;
981
+ }
982
+
983
+ git_patch_parse_ctx *git_patch_parse_ctx_init(
984
+ const char *content,
985
+ size_t content_len,
986
+ const git_patch_options *opts)
987
+ {
988
+ git_patch_parse_ctx *ctx;
989
+ git_patch_options default_opts = GIT_PATCH_OPTIONS_INIT;
990
+
991
+ if ((ctx = git__calloc(1, sizeof(git_patch_parse_ctx))) == NULL)
992
+ return NULL;
993
+
994
+ if (content_len) {
995
+ if ((ctx->content = git__malloc(content_len)) == NULL)
996
+ return NULL;
997
+
998
+ memcpy((char *)ctx->content, content, content_len);
999
+ }
1000
+
1001
+ ctx->content_len = content_len;
1002
+ ctx->remain = ctx->content;
1003
+ ctx->remain_len = ctx->content_len;
1004
+
1005
+ if (opts)
1006
+ memcpy(&ctx->opts, opts, sizeof(git_patch_options));
1007
+ else
1008
+ memcpy(&ctx->opts, &default_opts, sizeof(git_patch_options));
1009
+
1010
+ GIT_REFCOUNT_INC(ctx);
1011
+ return ctx;
1012
+ }
1013
+
1014
+ static void patch_parse_ctx_free(git_patch_parse_ctx *ctx)
1015
+ {
1016
+ if (!ctx)
1017
+ return;
1018
+
1019
+ git__free((char *)ctx->content);
1020
+ git__free(ctx);
1021
+ }
1022
+
1023
+ void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx)
1024
+ {
1025
+ GIT_REFCOUNT_DEC(ctx, patch_parse_ctx_free);
1026
+ }
1027
+
1028
+ static void patch_parsed__free(git_patch *p)
1029
+ {
1030
+ git_patch_parsed *patch = (git_patch_parsed *)p;
1031
+
1032
+ if (!patch)
1033
+ return;
1034
+
1035
+ git_patch_parse_ctx_free(patch->ctx);
1036
+
1037
+ git__free((char *)patch->base.binary.old_file.data);
1038
+ git__free((char *)patch->base.binary.new_file.data);
1039
+ git_array_clear(patch->base.hunks);
1040
+ git_array_clear(patch->base.lines);
1041
+ git__free(patch->base.delta);
1042
+
1043
+ git__free(patch->old_prefix);
1044
+ git__free(patch->new_prefix);
1045
+ git__free(patch->header_old_path);
1046
+ git__free(patch->header_new_path);
1047
+ git__free(patch->rename_old_path);
1048
+ git__free(patch->rename_new_path);
1049
+ git__free(patch->old_path);
1050
+ git__free(patch->new_path);
1051
+ git__free(patch);
1052
+ }
1053
+
1054
+ int git_patch_parse(
1055
+ git_patch **out,
1056
+ git_patch_parse_ctx *ctx)
1057
+ {
1058
+ git_patch_parsed *patch;
1059
+ size_t start, used;
1060
+ int error = 0;
1061
+
1062
+ assert(out && ctx);
1063
+
1064
+ *out = NULL;
1065
+
1066
+ patch = git__calloc(1, sizeof(git_patch_parsed));
1067
+ GITERR_CHECK_ALLOC(patch);
1068
+
1069
+ patch->ctx = ctx;
1070
+ GIT_REFCOUNT_INC(patch->ctx);
1071
+
1072
+ patch->base.free_fn = patch_parsed__free;
1073
+
1074
+ patch->base.delta = git__calloc(1, sizeof(git_diff_delta));
1075
+ GITERR_CHECK_ALLOC(patch->base.delta);
1076
+
1077
+ patch->base.delta->status = GIT_DELTA_MODIFIED;
1078
+ patch->base.delta->nfiles = 2;
1079
+
1080
+ start = ctx->remain_len;
1081
+
1082
+ if ((error = parse_patch_header(patch, ctx)) < 0 ||
1083
+ (error = parse_patch_body(patch, ctx)) < 0 ||
1084
+ (error = check_patch(patch)) < 0)
1085
+ goto done;
1086
+
1087
+ used = start - ctx->remain_len;
1088
+ ctx->remain += used;
1089
+
1090
+ patch->base.diff_opts.old_prefix = patch->old_prefix;
1091
+ patch->base.diff_opts.new_prefix = patch->new_prefix;
1092
+ patch->base.diff_opts.flags |= GIT_DIFF_SHOW_BINARY;
1093
+
1094
+ GIT_REFCOUNT_INC(patch);
1095
+ *out = &patch->base;
1096
+
1097
+ done:
1098
+ if (error < 0)
1099
+ patch_parsed__free(&patch->base);
1100
+
1101
+ return error;
1102
+ }
1103
+
1104
+ int git_patch_from_buffer(
1105
+ git_patch **out,
1106
+ const char *content,
1107
+ size_t content_len,
1108
+ const git_patch_options *opts)
1109
+ {
1110
+ git_patch_parse_ctx *ctx;
1111
+ int error;
1112
+
1113
+ ctx = git_patch_parse_ctx_init(content, content_len, opts);
1114
+ GITERR_CHECK_ALLOC(ctx);
1115
+
1116
+ error = git_patch_parse(out, ctx);
1117
+
1118
+ git_patch_parse_ctx_free(ctx);
1119
+ return error;
1120
+ }
1121
+