Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f9c824c

Browse files
committed
Add patch from blobs API
This adds two new public APIs: git_diff_patch_from_blobs and git_diff_patch_from_blob_and_buffer, plus it refactors the code for git_diff_blobs and git_diff_blob_to_buffer so that they code is almost entirely shared between these APIs, and adds tests for the new APIs.
1 parent 54faddd commit f9c824c

File tree

3 files changed

+428
-52
lines changed

3 files changed

+428
-52
lines changed

include/git2/diff.h

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ GIT_EXTERN(size_t) git_diff_patch_num_hunks(
860860
* @param total_additions Count of addition lines in output, can be NULL.
861861
* @param total_deletions Count of deletion lines in output, can be NULL.
862862
* @param patch The git_diff_patch object
863-
* @return Number of lines in hunk or -1 if invalid hunk index
863+
* @return 0 on success, <0 on error
864864
*/
865865
GIT_EXTERN(int) git_diff_patch_line_stats(
866866
size_t *total_context,
@@ -1000,6 +1000,26 @@ GIT_EXTERN(int) git_diff_blobs(
10001000
git_diff_data_cb line_cb,
10011001
void *payload);
10021002

1003+
/**
1004+
* Directly generate a patch from the difference between two blobs.
1005+
*
1006+
* This is just like `git_diff_blobs()` except it generates a patch object
1007+
* for the difference instead of directly making callbacks. You can use the
1008+
* standard `git_diff_patch` accessor functions to read the patch data, and
1009+
* you must call `git_diff_patch_free()` on the patch when done.
1010+
*
1011+
* @param out The generated patch; NULL on error
1012+
* @param old_blob Blob for old side of diff, or NULL for empty blob
1013+
* @param new_blob Blob for new side of diff, or NULL for empty blob
1014+
* @param options Options for diff, or NULL for default options
1015+
* @return 0 on success or error code < 0
1016+
*/
1017+
GIT_EXTERN(int) git_diff_patch_from_blobs(
1018+
git_diff_patch **out,
1019+
const git_blob *old_blob,
1020+
const git_blob *new_blob,
1021+
const git_diff_options *opts);
1022+
10031023
/**
10041024
* Directly run a diff between a blob and a buffer.
10051025
*
@@ -1013,7 +1033,7 @@ GIT_EXTERN(int) git_diff_blobs(
10131033
* the reverse, with GIT_DELTA_REMOVED and blob content removed.
10141034
*
10151035
* @param old_blob Blob for old side of diff, or NULL for empty blob
1016-
* @param buffer Raw data for new side of diff
1036+
* @param buffer Raw data for new side of diff, or NULL for empty
10171037
* @param buffer_len Length of raw data for new side of diff
10181038
* @param options Options for diff, or NULL for default options
10191039
* @param file_cb Callback for "file"; made once if there is a diff; can be NULL
@@ -1032,6 +1052,29 @@ GIT_EXTERN(int) git_diff_blob_to_buffer(
10321052
git_diff_data_cb data_cb,
10331053
void *payload);
10341054

1055+
/**
1056+
* Directly generate a patch from the difference between a blob and a buffer.
1057+
*
1058+
* This is just like `git_diff_blob_to_buffer()` except it generates a patch
1059+
* object for the difference instead of directly making callbacks. You can
1060+
* use the standard `git_diff_patch` accessor functions to read the patch
1061+
* data, and you must call `git_diff_patch_free()` on the patch when done.
1062+
*
1063+
* @param out The generated patch; NULL on error
1064+
* @param old_blob Blob for old side of diff, or NULL for empty blob
1065+
* @param buffer Raw data for new side of diff, or NULL for empty
1066+
* @param buffer_len Length of raw data for new side of diff
1067+
* @param options Options for diff, or NULL for default options
1068+
* @return 0 on success or error code < 0
1069+
*/
1070+
GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer(
1071+
git_diff_patch **out,
1072+
const git_blob *old_blob,
1073+
const char *buf,
1074+
size_t buflen,
1075+
const git_diff_options *opts);
1076+
1077+
10351078
GIT_END_DECL
10361079

10371080
/** @} */

src/diff_patch.c

Lines changed: 145 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -265,58 +265,56 @@ int git_diff_foreach(
265265
}
266266

267267
typedef struct {
268-
git_xdiff_output xo;
269268
git_diff_patch patch;
270269
git_diff_delta delta;
271-
} diff_single_info;
270+
} diff_patch_with_delta;
272271

273-
static int diff_single_generate(diff_single_info *info)
272+
static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
274273
{
275274
int error = 0;
276-
git_diff_patch *patch = &info->patch;
275+
git_diff_patch *patch = &pd->patch;
277276
bool has_old = ((patch->ofile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
278277
bool has_new = ((patch->nfile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
279278

280-
info->delta.status = has_new ?
279+
pd->delta.status = has_new ?
281280
(has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
282281
(has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
283282

284283
if (git_oid_equal(&patch->nfile.file.oid, &patch->ofile.file.oid))
285-
info->delta.status = GIT_DELTA_UNMODIFIED;
284+
pd->delta.status = GIT_DELTA_UNMODIFIED;
286285

287-
patch->delta = &info->delta;
286+
patch->delta = &pd->delta;
288287

289288
diff_patch_init_common(patch);
290289

291-
error = diff_patch_file_callback(patch, (git_diff_output *)&info->xo);
290+
error = diff_patch_file_callback(patch, (git_diff_output *)xo);
292291

293292
if (!error)
294-
error = diff_patch_generate(patch, (git_diff_output *)&info->xo);
293+
error = diff_patch_generate(patch, (git_diff_output *)xo);
295294

296295
if (error == GIT_EUSER)
297296
giterr_clear(); /* don't leave error message set invalidly */
298297

299298
return error;
300299
}
301300

302-
int git_diff_blobs(
301+
static int diff_patch_from_blobs(
302+
diff_patch_with_delta *pd,
303+
git_xdiff_output *xo,
303304
const git_blob *old_blob,
304305
const git_blob *new_blob,
305-
const git_diff_options *opts,
306-
git_diff_file_cb file_cb,
307-
git_diff_hunk_cb hunk_cb,
308-
git_diff_data_cb data_cb,
309-
void *payload)
306+
const git_diff_options *opts)
310307
{
311308
int error = 0;
312-
diff_single_info info;
313309
git_repository *repo =
314310
new_blob ? git_object_owner((const git_object *)new_blob) :
315311
old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
316312

317313
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
318314

319-
if (!repo) /* Hmm, given two NULL blobs, silently do no callbacks? */
315+
pd->patch.delta = &pd->delta;
316+
317+
if (!repo) /* return two NULL items as UNMODIFIED delta */
320318
return 0;
321319

322320
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
@@ -325,64 +323,163 @@ int git_diff_blobs(
325323
new_blob = swap;
326324
}
327325

328-
memset(&info, 0, sizeof(info));
326+
if ((error = diff_file_content_init_from_blob(
327+
&pd->patch.ofile, repo, opts, old_blob)) < 0 ||
328+
(error = diff_file_content_init_from_blob(
329+
&pd->patch.nfile, repo, opts, new_blob)) < 0)
330+
return error;
329331

330-
diff_output_init((git_diff_output *)&info.xo,
331-
opts, file_cb, hunk_cb, data_cb, payload);
332-
git_xdiff_init(&info.xo, opts);
332+
return diff_single_generate(pd, xo);
333+
}
333334

334-
if (!(error = diff_file_content_init_from_blob(
335-
&info.patch.ofile, repo, opts, old_blob)) &&
336-
!(error = diff_file_content_init_from_blob(
337-
&info.patch.nfile, repo, opts, new_blob)))
338-
error = diff_single_generate(&info);
335+
int git_diff_blobs(
336+
const git_blob *old_blob,
337+
const git_blob *new_blob,
338+
const git_diff_options *opts,
339+
git_diff_file_cb file_cb,
340+
git_diff_hunk_cb hunk_cb,
341+
git_diff_data_cb data_cb,
342+
void *payload)
343+
{
344+
int error = 0;
345+
diff_patch_with_delta pd;
346+
git_xdiff_output xo;
339347

340-
git_diff_patch_free(&info.patch);
348+
memset(&pd, 0, sizeof(pd));
349+
memset(&xo, 0, sizeof(xo));
350+
351+
diff_output_init(
352+
(git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
353+
git_xdiff_init(&xo, opts);
354+
355+
error = diff_patch_from_blobs(&pd, &xo, old_blob, new_blob, opts);
356+
357+
git_diff_patch_free((git_diff_patch *)&pd);
341358

342359
return error;
343360
}
344361

345-
int git_diff_blob_to_buffer(
362+
int git_diff_patch_from_blobs(
363+
git_diff_patch **out,
364+
const git_blob *old_blob,
365+
const git_blob *new_blob,
366+
const git_diff_options *opts)
367+
{
368+
int error = 0;
369+
diff_patch_with_delta *pd;
370+
git_xdiff_output xo;
371+
372+
assert(out);
373+
*out = NULL;
374+
375+
pd = git__calloc(1, sizeof(*pd));
376+
GITERR_CHECK_ALLOC(pd);
377+
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
378+
379+
memset(&xo, 0, sizeof(xo));
380+
381+
diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
382+
git_xdiff_init(&xo, opts);
383+
384+
if (!(error = diff_patch_from_blobs(pd, &xo, old_blob, new_blob, opts)))
385+
*out = (git_diff_patch *)pd;
386+
else
387+
git_diff_patch_free((git_diff_patch *)pd);
388+
389+
return error;
390+
}
391+
392+
static int diff_patch_from_blob_and_buffer(
393+
diff_patch_with_delta *pd,
394+
git_xdiff_output *xo,
346395
const git_blob *old_blob,
347396
const char *buf,
348397
size_t buflen,
349-
const git_diff_options *opts,
350-
git_diff_file_cb file_cb,
351-
git_diff_hunk_cb hunk_cb,
352-
git_diff_data_cb data_cb,
353-
void *payload)
398+
const git_diff_options *opts)
354399
{
355400
int error = 0;
356-
diff_single_info info;
357401
git_repository *repo =
358402
old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
359403

360404
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
361405

362-
if (!repo && !buf) /* Hmm, given NULLs, silently do no callbacks? */
363-
return 0;
364-
365-
memset(&info, 0, sizeof(info));
406+
pd->patch.delta = &pd->delta;
366407

367-
diff_output_init((git_diff_output *)&info.xo,
368-
opts, file_cb, hunk_cb, data_cb, payload);
369-
git_xdiff_init(&info.xo, opts);
408+
if (!repo && !buf) /* return two NULL items as UNMODIFIED delta */
409+
return 0;
370410

371411
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
372412
if (!(error = diff_file_content_init_from_raw(
373-
&info.patch.ofile, repo, opts, buf, buflen)))
413+
&pd->patch.ofile, repo, opts, buf, buflen)))
374414
error = diff_file_content_init_from_blob(
375-
&info.patch.nfile, repo, opts, old_blob);
415+
&pd->patch.nfile, repo, opts, old_blob);
376416
} else {
377417
if (!(error = diff_file_content_init_from_blob(
378-
&info.patch.ofile, repo, opts, old_blob)))
418+
&pd->patch.ofile, repo, opts, old_blob)))
379419
error = diff_file_content_init_from_raw(
380-
&info.patch.nfile, repo, opts, buf, buflen);
420+
&pd->patch.nfile, repo, opts, buf, buflen);
381421
}
382422

383-
error = diff_single_generate(&info);
423+
return diff_single_generate(pd, xo);
424+
}
425+
426+
int git_diff_blob_to_buffer(
427+
const git_blob *old_blob,
428+
const char *buf,
429+
size_t buflen,
430+
const git_diff_options *opts,
431+
git_diff_file_cb file_cb,
432+
git_diff_hunk_cb hunk_cb,
433+
git_diff_data_cb data_cb,
434+
void *payload)
435+
{
436+
int error = 0;
437+
diff_patch_with_delta pd;
438+
git_xdiff_output xo;
384439

385-
git_diff_patch_free(&info.patch);
440+
memset(&pd, 0, sizeof(pd));
441+
memset(&xo, 0, sizeof(xo));
442+
443+
diff_output_init(
444+
(git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
445+
git_xdiff_init(&xo, opts);
446+
447+
error = diff_patch_from_blob_and_buffer(
448+
&pd, &xo, old_blob, buf, buflen, opts);
449+
450+
git_diff_patch_free((git_diff_patch *)&pd);
451+
452+
return error;
453+
}
454+
455+
int git_diff_patch_from_blob_and_buffer(
456+
git_diff_patch **out,
457+
const git_blob *old_blob,
458+
const char *buf,
459+
size_t buflen,
460+
const git_diff_options *opts)
461+
{
462+
int error = 0;
463+
diff_patch_with_delta *pd;
464+
git_xdiff_output xo;
465+
466+
assert(out);
467+
*out = NULL;
468+
469+
pd = git__calloc(1, sizeof(*pd));
470+
GITERR_CHECK_ALLOC(pd);
471+
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
472+
473+
memset(&xo, 0, sizeof(xo));
474+
475+
diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
476+
git_xdiff_init(&xo, opts);
477+
478+
if (!(error = diff_patch_from_blob_and_buffer(
479+
pd, &xo, old_blob, buf, buflen, opts)))
480+
*out = (git_diff_patch *)pd;
481+
else
482+
git_diff_patch_free((git_diff_patch *)pd);
386483

387484
return error;
388485
}
@@ -599,9 +696,7 @@ static int diff_patch_file_cb(
599696
float progress,
600697
void *payload)
601698
{
602-
GIT_UNUSED(delta);
603-
GIT_UNUSED(progress);
604-
GIT_UNUSED(payload);
699+
GIT_UNUSED(delta); GIT_UNUSED(progress); GIT_UNUSED(payload);
605700
return 0;
606701
}
607702

0 commit comments

Comments
 (0)