mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-07 09:50:09 +00:00
Introduce git_apply_patch
The beginnings of patch application from an existing (diff-created) git_patch object: applies the hunks of a git_patch to a buffer.
This commit is contained in:
parent
784bb30300
commit
7cb904ba44
@ -98,7 +98,8 @@ typedef enum {
|
|||||||
GITERR_CHERRYPICK,
|
GITERR_CHERRYPICK,
|
||||||
GITERR_DESCRIBE,
|
GITERR_DESCRIBE,
|
||||||
GITERR_REBASE,
|
GITERR_REBASE,
|
||||||
GITERR_FILESYSTEM
|
GITERR_FILESYSTEM,
|
||||||
|
GITERR_PATCH,
|
||||||
} git_error_t;
|
} git_error_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
282
src/apply.c
Normal file
282
src/apply.c
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||||
|
* a Linking Exception. For full terms see the included COPYING file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "git2/patch.h"
|
||||||
|
#include "git2/filter.h"
|
||||||
|
#include "array.h"
|
||||||
|
#include "diff_patch.h"
|
||||||
|
#include "fileops.h"
|
||||||
|
#include "apply.h"
|
||||||
|
|
||||||
|
#define apply_err(...) \
|
||||||
|
( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* The lines that we allocate ourself are allocated out of the pool.
|
||||||
|
* (Lines may have been allocated out of the diff.)
|
||||||
|
*/
|
||||||
|
git_pool pool;
|
||||||
|
git_vector lines;
|
||||||
|
} patch_image;
|
||||||
|
|
||||||
|
static void patch_line_init(
|
||||||
|
git_diff_line *out,
|
||||||
|
const char *in,
|
||||||
|
size_t in_len,
|
||||||
|
size_t in_offset)
|
||||||
|
{
|
||||||
|
out->content = in;
|
||||||
|
out->content_len = in_len;
|
||||||
|
out->content_offset = in_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int patch_image_init(patch_image *out)
|
||||||
|
{
|
||||||
|
memset(out, 0x0, sizeof(patch_image));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int patch_image_init_fromstr(
|
||||||
|
patch_image *out, const char *in, size_t in_len)
|
||||||
|
{
|
||||||
|
git_diff_line *line;
|
||||||
|
const char *start, *end;
|
||||||
|
|
||||||
|
memset(out, 0x0, sizeof(patch_image));
|
||||||
|
|
||||||
|
git_pool_init(&out->pool, sizeof(git_diff_line));
|
||||||
|
|
||||||
|
for (start = in; start < in + in_len; start = end) {
|
||||||
|
end = memchr(start, '\n', in_len);
|
||||||
|
|
||||||
|
if (end < in + in_len)
|
||||||
|
end++;
|
||||||
|
|
||||||
|
line = git_pool_mallocz(&out->pool, 1);
|
||||||
|
GITERR_CHECK_ALLOC(line);
|
||||||
|
|
||||||
|
if (git_vector_insert(&out->lines, line) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
patch_line_init(line, start, (end - start), (start - in));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void patch_image_free(patch_image *image)
|
||||||
|
{
|
||||||
|
if (image == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
git_pool_clear(&image->pool);
|
||||||
|
git_vector_free(&image->lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool match_hunk(
|
||||||
|
patch_image *image,
|
||||||
|
patch_image *preimage,
|
||||||
|
size_t linenum)
|
||||||
|
{
|
||||||
|
bool match = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* Ensure this hunk is within the image boundaries. */
|
||||||
|
if (git_vector_length(&preimage->lines) + linenum >
|
||||||
|
git_vector_length(&image->lines))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
match = 1;
|
||||||
|
|
||||||
|
/* Check exact match. */
|
||||||
|
for (i = 0; i < git_vector_length(&preimage->lines); i++) {
|
||||||
|
git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
|
||||||
|
git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);
|
||||||
|
|
||||||
|
if (preimage_line->content_len != preimage_line->content_len ||
|
||||||
|
memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
|
||||||
|
match = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool find_hunk_linenum(
|
||||||
|
size_t *out,
|
||||||
|
patch_image *image,
|
||||||
|
patch_image *preimage,
|
||||||
|
size_t linenum)
|
||||||
|
{
|
||||||
|
size_t max = git_vector_length(&image->lines);
|
||||||
|
bool match;
|
||||||
|
|
||||||
|
if (linenum > max)
|
||||||
|
linenum = max;
|
||||||
|
|
||||||
|
match = match_hunk(image, preimage, linenum);
|
||||||
|
|
||||||
|
*out = linenum;
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int update_hunk(
|
||||||
|
patch_image *image,
|
||||||
|
unsigned int linenum,
|
||||||
|
patch_image *preimage,
|
||||||
|
patch_image *postimage)
|
||||||
|
{
|
||||||
|
size_t postlen = git_vector_length(&postimage->lines);
|
||||||
|
size_t prelen = git_vector_length(&preimage->lines);
|
||||||
|
size_t i;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if (postlen > prelen)
|
||||||
|
error = git_vector_grow_at(
|
||||||
|
&image->lines, linenum, (postlen - prelen));
|
||||||
|
else if (prelen > postlen)
|
||||||
|
error = git_vector_shrink_at(
|
||||||
|
&image->lines, linenum, (prelen - postlen));
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
giterr_set_oom();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < git_vector_length(&postimage->lines); i++) {
|
||||||
|
image->lines.contents[linenum + i] =
|
||||||
|
git_vector_get(&postimage->lines, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int apply_hunk(
|
||||||
|
patch_image *image,
|
||||||
|
git_patch *patch,
|
||||||
|
diff_patch_hunk *hunk)
|
||||||
|
{
|
||||||
|
patch_image preimage, postimage;
|
||||||
|
size_t line_num, i;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if ((error = patch_image_init(&preimage)) < 0 ||
|
||||||
|
(error = patch_image_init(&postimage)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
for (i = 0; i < hunk->line_count; i++) {
|
||||||
|
size_t linenum = hunk->line_start + i;
|
||||||
|
git_diff_line *line = git_array_get(patch->lines, linenum);
|
||||||
|
|
||||||
|
if (!line) {
|
||||||
|
error = apply_err("Preimage does not contain line %d", linenum);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
|
||||||
|
line->origin == GIT_DIFF_LINE_DELETION) {
|
||||||
|
if ((error = git_vector_insert(&preimage.lines, line)) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
|
||||||
|
line->origin == GIT_DIFF_LINE_ADDITION) {
|
||||||
|
if ((error = git_vector_insert(&postimage.lines, line)) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line_num = hunk->hunk.new_start ? hunk->hunk.new_start - 1 : 0;
|
||||||
|
|
||||||
|
if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
|
||||||
|
error = apply_err("Hunk at line %d did not apply",
|
||||||
|
hunk->hunk.new_start);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = update_hunk(image, line_num, &preimage, &postimage);
|
||||||
|
|
||||||
|
done:
|
||||||
|
patch_image_free(&preimage);
|
||||||
|
patch_image_free(&postimage);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int apply_hunks(
|
||||||
|
git_buf *out,
|
||||||
|
const char *source,
|
||||||
|
size_t source_len,
|
||||||
|
git_patch *patch)
|
||||||
|
{
|
||||||
|
diff_patch_hunk *hunk;
|
||||||
|
git_diff_line *line;
|
||||||
|
patch_image image;
|
||||||
|
size_t i;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
git_array_foreach(patch->hunks, i, hunk) {
|
||||||
|
if ((error = apply_hunk(&image, patch, hunk)) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
git_vector_foreach(&image.lines, i, line)
|
||||||
|
git_buf_put(out, line->content, line->content_len);
|
||||||
|
|
||||||
|
done:
|
||||||
|
patch_image_free(&image);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_apply__patch(
|
||||||
|
git_buf *contents_out,
|
||||||
|
char **filename_out,
|
||||||
|
unsigned int *mode_out,
|
||||||
|
const char *source,
|
||||||
|
size_t source_len,
|
||||||
|
git_patch *patch)
|
||||||
|
{
|
||||||
|
char *filename = NULL;
|
||||||
|
unsigned int mode = 0;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
assert(contents_out && filename_out && mode_out && (source || !source_len) && patch);
|
||||||
|
|
||||||
|
*filename_out = NULL;
|
||||||
|
*mode_out = 0;
|
||||||
|
|
||||||
|
if (patch->delta->status != GIT_DELTA_DELETED) {
|
||||||
|
filename = git__strdup(patch->nfile.file->path);
|
||||||
|
mode = patch->nfile.file->mode ?
|
||||||
|
patch->nfile.file->mode : GIT_FILEMODE_BLOB;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = apply_hunks(contents_out, source, source_len, patch)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (patch->delta->status == GIT_DELTA_DELETED &&
|
||||||
|
git_buf_len(contents_out) > 0) {
|
||||||
|
error = apply_err("removal patch leaves file contents");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
*filename_out = filename;
|
||||||
|
*mode_out = mode;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (error < 0)
|
||||||
|
git__free(filename);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
21
src/apply.h
Normal file
21
src/apply.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||||
|
* a Linking Exception. For full terms see the included COPYING file.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDE_apply_h__
|
||||||
|
#define INCLUDE_apply_h__
|
||||||
|
|
||||||
|
#include "git2/patch.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
extern int git_apply__patch(
|
||||||
|
git_buf *out,
|
||||||
|
char **filename,
|
||||||
|
unsigned int *mode,
|
||||||
|
const char *source,
|
||||||
|
size_t source_len,
|
||||||
|
git_patch *patch);
|
||||||
|
|
||||||
|
#endif
|
@ -85,7 +85,6 @@ on_oom:
|
|||||||
#define git_array_foreach(a, i, element) \
|
#define git_array_foreach(a, i, element) \
|
||||||
for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
|
for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
|
||||||
|
|
||||||
|
|
||||||
GIT_INLINE(int) git_array__search(
|
GIT_INLINE(int) git_array__search(
|
||||||
size_t *out,
|
size_t *out,
|
||||||
void *array_ptr,
|
void *array_ptr,
|
||||||
|
41
src/vector.c
41
src/vector.c
@ -330,6 +330,47 @@ int git_vector_resize_to(git_vector *v, size_t new_length)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_vector_grow_at(git_vector *v, size_t idx, size_t grow_len)
|
||||||
|
{
|
||||||
|
size_t new_length = v->length + grow_len;
|
||||||
|
size_t new_idx = idx + grow_len;
|
||||||
|
|
||||||
|
assert(grow_len > 0);
|
||||||
|
assert (idx <= v->length);
|
||||||
|
|
||||||
|
if (new_length < v->length ||
|
||||||
|
(new_length > v->_alloc_size && resize_vector(v, new_length) < 0))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memmove(&v->contents[new_idx], &v->contents[idx],
|
||||||
|
sizeof(void *) * (v->length - idx));
|
||||||
|
memset(&v->contents[idx], 0, sizeof(void *) * grow_len);
|
||||||
|
|
||||||
|
v->length = new_length;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_vector_shrink_at(git_vector *v, size_t idx, size_t shrink_len)
|
||||||
|
{
|
||||||
|
size_t new_length = v->length - shrink_len;
|
||||||
|
size_t end_idx = idx + shrink_len;
|
||||||
|
|
||||||
|
assert(shrink_len > 0 && shrink_len <= v->length);
|
||||||
|
assert(idx <= v->length);
|
||||||
|
|
||||||
|
if (new_length > v->length)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (idx > v->length)
|
||||||
|
memmove(&v->contents[idx], &v->contents[end_idx],
|
||||||
|
sizeof(void *) * (v->length - idx));
|
||||||
|
|
||||||
|
memset(&v->contents[new_length], 0, sizeof(void *) * shrink_len);
|
||||||
|
|
||||||
|
v->length = new_length;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int git_vector_set(void **old, git_vector *v, size_t position, void *value)
|
int git_vector_set(void **old, git_vector *v, size_t position, void *value)
|
||||||
{
|
{
|
||||||
if (position + 1 > v->length) {
|
if (position + 1 > v->length) {
|
||||||
|
@ -93,6 +93,9 @@ void git_vector_remove_matching(
|
|||||||
void *payload);
|
void *payload);
|
||||||
|
|
||||||
int git_vector_resize_to(git_vector *v, size_t new_length);
|
int git_vector_resize_to(git_vector *v, size_t new_length);
|
||||||
|
int git_vector_grow_at(git_vector *v, size_t idx, size_t grow_len);
|
||||||
|
int git_vector_shrink_at(git_vector *v, size_t idx, size_t shrink_len);
|
||||||
|
|
||||||
int git_vector_set(void **old, git_vector *v, size_t position, void *value);
|
int git_vector_set(void **old, git_vector *v, size_t position, void *value);
|
||||||
|
|
||||||
/** Check if vector is sorted */
|
/** Check if vector is sorted */
|
||||||
|
286
tests/apply/apply_common.h
Normal file
286
tests/apply/apply_common.h
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
/* The original file contents */
|
||||||
|
|
||||||
|
#define FILE_ORIGINAL \
|
||||||
|
"hey!\n" \
|
||||||
|
"this is some context!\n" \
|
||||||
|
"around some lines\n" \
|
||||||
|
"that will change\n" \
|
||||||
|
"yes it is!\n" \
|
||||||
|
"(this line is changed)\n" \
|
||||||
|
"and this\n" \
|
||||||
|
"is additional context\n" \
|
||||||
|
"below it!\n"
|
||||||
|
|
||||||
|
/* A change in the middle of the file (and the resultant patch) */
|
||||||
|
|
||||||
|
#define FILE_CHANGE_MIDDLE \
|
||||||
|
"hey!\n" \
|
||||||
|
"this is some context!\n" \
|
||||||
|
"around some lines\n" \
|
||||||
|
"that will change\n" \
|
||||||
|
"yes it is!\n" \
|
||||||
|
"(THIS line is changed!)\n" \
|
||||||
|
"and this\n" \
|
||||||
|
"is additional context\n" \
|
||||||
|
"below it!\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..cd8fd12 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -3,7 +3,7 @@ this is some context!\n" \
|
||||||
|
" around some lines\n" \
|
||||||
|
" that will change\n" \
|
||||||
|
" yes it is!\n" \
|
||||||
|
"-(this line is changed)\n" \
|
||||||
|
"+(THIS line is changed!)\n" \
|
||||||
|
" and this\n" \
|
||||||
|
" is additional context\n" \
|
||||||
|
" below it!\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..cd8fd12 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -6 +6 @@ yes it is!\n" \
|
||||||
|
"-(this line is changed)\n" \
|
||||||
|
"+(THIS line is changed!)\n"
|
||||||
|
|
||||||
|
/* A change of the first line (and the resultant patch) */
|
||||||
|
|
||||||
|
#define FILE_CHANGE_FIRSTLINE \
|
||||||
|
"hey, change in head!\n" \
|
||||||
|
"this is some context!\n" \
|
||||||
|
"around some lines\n" \
|
||||||
|
"that will change\n" \
|
||||||
|
"yes it is!\n" \
|
||||||
|
"(this line is changed)\n" \
|
||||||
|
"and this\n" \
|
||||||
|
"is additional context\n" \
|
||||||
|
"below it!\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..c81df1d 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -1,4 +1,4 @@\n" \
|
||||||
|
"-hey!\n" \
|
||||||
|
"+hey, change in head!\n" \
|
||||||
|
" this is some context!\n" \
|
||||||
|
" around some lines\n" \
|
||||||
|
" that will change\n"
|
||||||
|
|
||||||
|
/* A change of the last line (and the resultant patch) */
|
||||||
|
|
||||||
|
#define FILE_CHANGE_LASTLINE \
|
||||||
|
"hey!\n" \
|
||||||
|
"this is some context!\n" \
|
||||||
|
"around some lines\n" \
|
||||||
|
"that will change\n" \
|
||||||
|
"yes it is!\n" \
|
||||||
|
"(this line is changed)\n" \
|
||||||
|
"and this\n" \
|
||||||
|
"is additional context\n" \
|
||||||
|
"change to the last line.\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_CHANGE_LASTLINE \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..f70db1c 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -6,4 +6,4 @@ yes it is!\n" \
|
||||||
|
" (this line is changed)\n" \
|
||||||
|
" and this\n" \
|
||||||
|
" is additional context\n" \
|
||||||
|
"-below it!\n" \
|
||||||
|
"+change to the last line.\n"
|
||||||
|
|
||||||
|
/* An insertion at the beginning of the file (and the resultant patch) */
|
||||||
|
|
||||||
|
#define FILE_PREPEND \
|
||||||
|
"insert at front\n" \
|
||||||
|
"hey!\n" \
|
||||||
|
"this is some context!\n" \
|
||||||
|
"around some lines\n" \
|
||||||
|
"that will change\n" \
|
||||||
|
"yes it is!\n" \
|
||||||
|
"(this line is changed)\n" \
|
||||||
|
"and this\n" \
|
||||||
|
"is additional context\n" \
|
||||||
|
"below it!\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_PREPEND \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..0f39b9a 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -1,3 +1,4 @@\n" \
|
||||||
|
"+insert at front\n" \
|
||||||
|
" hey!\n" \
|
||||||
|
" this is some context!\n" \
|
||||||
|
" around some lines\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..0f39b9a 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -0,0 +1 @@\n" \
|
||||||
|
"+insert at front\n"
|
||||||
|
|
||||||
|
/* An insertion at the end of the file (and the resultant patch) */
|
||||||
|
|
||||||
|
#define FILE_APPEND \
|
||||||
|
"hey!\n" \
|
||||||
|
"this is some context!\n" \
|
||||||
|
"around some lines\n" \
|
||||||
|
"that will change\n" \
|
||||||
|
"yes it is!\n" \
|
||||||
|
"(this line is changed)\n" \
|
||||||
|
"and this\n" \
|
||||||
|
"is additional context\n" \
|
||||||
|
"below it!\n" \
|
||||||
|
"insert at end\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_APPEND \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..72788bb 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -7,3 +7,4 @@ yes it is!\n" \
|
||||||
|
" and this\n" \
|
||||||
|
" is additional context\n" \
|
||||||
|
" below it!\n" \
|
||||||
|
"+insert at end\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_APPEND_NOCONTEXT \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..72788bb 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -9,0 +10 @@ below it!\n" \
|
||||||
|
"+insert at end\n"
|
||||||
|
|
||||||
|
/* An insertion at the beginning and end of file (and the resultant patch) */
|
||||||
|
|
||||||
|
#define FILE_PREPEND_AND_APPEND \
|
||||||
|
"first and\n" \
|
||||||
|
"this is some context!\n" \
|
||||||
|
"around some lines\n" \
|
||||||
|
"that will change\n" \
|
||||||
|
"yes it is!\n" \
|
||||||
|
"(this line is changed)\n" \
|
||||||
|
"and this\n" \
|
||||||
|
"is additional context\n" \
|
||||||
|
"last lines\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_PREPEND_AND_APPEND \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..f282430 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -1,4 +1,4 @@\n" \
|
||||||
|
"-hey!\n" \
|
||||||
|
"+first and\n" \
|
||||||
|
" this is some context!\n" \
|
||||||
|
" around some lines\n" \
|
||||||
|
" that will change\n" \
|
||||||
|
"@@ -6,4 +6,4 @@ yes it is!\n" \
|
||||||
|
" (this line is changed)\n" \
|
||||||
|
" and this\n" \
|
||||||
|
" is additional context\n" \
|
||||||
|
"-below it!\n" \
|
||||||
|
"+last lines\n"
|
||||||
|
|
||||||
|
#define PATCH_ORIGINAL_TO_EMPTY_FILE \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index 9432026..e69de29 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -1,9 +0,0 @@\n" \
|
||||||
|
"-hey!\n" \
|
||||||
|
"-this is some context!\n" \
|
||||||
|
"-around some lines\n" \
|
||||||
|
"-that will change\n" \
|
||||||
|
"-yes it is!\n" \
|
||||||
|
"-(this line is changed)\n" \
|
||||||
|
"-and this\n" \
|
||||||
|
"-is additional context\n" \
|
||||||
|
"-below it!\n"
|
||||||
|
|
||||||
|
#define PATCH_EMPTY_FILE_TO_ORIGINAL \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"index e69de29..9432026 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -0,0 +1,9 @@\n" \
|
||||||
|
"+hey!\n" \
|
||||||
|
"+this is some context!\n" \
|
||||||
|
"+around some lines\n" \
|
||||||
|
"+that will change\n" \
|
||||||
|
"+yes it is!\n" \
|
||||||
|
"+(this line is changed)\n" \
|
||||||
|
"+and this\n" \
|
||||||
|
"+is additional context\n" \
|
||||||
|
"+below it!\n"
|
||||||
|
|
||||||
|
#define PATCH_ADD_ORIGINAL \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"new file mode 100644\n" \
|
||||||
|
"index 0000000..9432026\n" \
|
||||||
|
"--- /dev/null\n" \
|
||||||
|
"+++ b/file.txt\n" \
|
||||||
|
"@@ -0,0 +1,9 @@\n" \
|
||||||
|
"+hey!\n" \
|
||||||
|
"+this is some context!\n" \
|
||||||
|
"+around some lines\n" \
|
||||||
|
"+that will change\n" \
|
||||||
|
"+yes it is!\n" \
|
||||||
|
"+(this line is changed)\n" \
|
||||||
|
"+and this\n" \
|
||||||
|
"+is additional context\n" \
|
||||||
|
"+below it!\n"
|
||||||
|
|
||||||
|
#define PATCH_DELETE_ORIGINAL \
|
||||||
|
"diff --git a/file.txt b/file.txt\n" \
|
||||||
|
"deleted file mode 100644\n" \
|
||||||
|
"index 9432026..0000000\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ /dev/null\n" \
|
||||||
|
"@@ -1,9 +0,0 @@\n" \
|
||||||
|
"-hey!\n" \
|
||||||
|
"-this is some context!\n" \
|
||||||
|
"-around some lines\n" \
|
||||||
|
"-that will change\n" \
|
||||||
|
"-yes it is!\n" \
|
||||||
|
"-(this line is changed)\n" \
|
||||||
|
"-and this\n" \
|
||||||
|
"-is additional context\n" \
|
||||||
|
"-below it!\n"
|
||||||
|
|
||||||
|
#define PATCH_RENAME_EXACT \
|
||||||
|
"diff --git a/file.txt b/newfile.txt\n" \
|
||||||
|
"similarity index 100%\n" \
|
||||||
|
"rename from file.txt\n" \
|
||||||
|
"rename to newfile.txt\n"
|
||||||
|
|
||||||
|
#define PATCH_RENAME_SIMILAR \
|
||||||
|
"diff --git a/file.txt b/newfile.txt\n" \
|
||||||
|
"similarity index 77%\n" \
|
||||||
|
"rename from file.txt\n" \
|
||||||
|
"rename to newfile.txt\n" \
|
||||||
|
"index 9432026..cd8fd12 100644\n" \
|
||||||
|
"--- a/file.txt\n" \
|
||||||
|
"+++ b/newfile.txt\n" \
|
||||||
|
"@@ -3,7 +3,7 @@ this is some context!\n" \
|
||||||
|
" around some lines\n" \
|
||||||
|
" that will change\n" \
|
||||||
|
" yes it is!\n" \
|
||||||
|
"-(this line is changed)\n" \
|
||||||
|
"+(THIS line is changed!)\n" \
|
||||||
|
" and this\n" \
|
||||||
|
" is additional context\n" \
|
||||||
|
" below it!\n"
|
176
tests/apply/fromdiff.c
Normal file
176
tests/apply/fromdiff.c
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#include "clar_libgit2.h"
|
||||||
|
#include "git2/sys/repository.h"
|
||||||
|
|
||||||
|
#include "apply.h"
|
||||||
|
#include "repository.h"
|
||||||
|
#include "buf_text.h"
|
||||||
|
|
||||||
|
#include "apply_common.h"
|
||||||
|
|
||||||
|
static git_repository *repo = NULL;
|
||||||
|
|
||||||
|
void test_apply_fromdiff__initialize(void)
|
||||||
|
{
|
||||||
|
repo = cl_git_sandbox_init("renames");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__cleanup(void)
|
||||||
|
{
|
||||||
|
cl_git_sandbox_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int apply_buf(
|
||||||
|
const char *old,
|
||||||
|
const char *oldname,
|
||||||
|
const char *new,
|
||||||
|
const char *newname,
|
||||||
|
const char *patch_expected,
|
||||||
|
const git_diff_options *diff_opts)
|
||||||
|
{
|
||||||
|
git_patch *patch;
|
||||||
|
git_buf result = GIT_BUF_INIT;
|
||||||
|
git_buf patchbuf = GIT_BUF_INIT;
|
||||||
|
char *filename;
|
||||||
|
unsigned int mode;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
cl_git_pass(git_patch_from_buffers(&patch,
|
||||||
|
old, old ? strlen(old) : 0, oldname,
|
||||||
|
new, new ? strlen(new) : 0, newname,
|
||||||
|
diff_opts));
|
||||||
|
cl_git_pass(git_patch_to_buf(&patchbuf, patch));
|
||||||
|
|
||||||
|
cl_assert_equal_s(patch_expected, patchbuf.ptr);
|
||||||
|
|
||||||
|
error = git_apply__patch(&result, &filename, &mode, old, old ? strlen(old) : 0, patch);
|
||||||
|
|
||||||
|
if (error == 0 && new == NULL) {
|
||||||
|
cl_assert_equal_i(0, result.size);
|
||||||
|
cl_assert_equal_p(NULL, filename);
|
||||||
|
cl_assert_equal_i(0, mode);
|
||||||
|
} else {
|
||||||
|
cl_assert_equal_s(new, result.ptr);
|
||||||
|
cl_assert_equal_s("file.txt", filename);
|
||||||
|
cl_assert_equal_i(0100644, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
git__free(filename);
|
||||||
|
git_buf_free(&result);
|
||||||
|
git_buf_free(&patchbuf);
|
||||||
|
git_patch_free(patch);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__change_middle(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_CHANGE_MIDDLE, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_CHANGE_MIDDLE, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__change_middle_nocontext(void)
|
||||||
|
{
|
||||||
|
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||||
|
diff_opts.context_lines = 0;
|
||||||
|
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_CHANGE_MIDDLE, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT, &diff_opts));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__change_firstline(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_CHANGE_FIRSTLINE, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__lastline(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_CHANGE_LASTLINE, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_CHANGE_LASTLINE, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__prepend(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_PREPEND, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_PREPEND, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__prepend_nocontext(void)
|
||||||
|
{
|
||||||
|
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||||
|
diff_opts.context_lines = 0;
|
||||||
|
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_PREPEND, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT, &diff_opts));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__append(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_APPEND, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_APPEND, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__append_nocontext(void)
|
||||||
|
{
|
||||||
|
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||||
|
diff_opts.context_lines = 0;
|
||||||
|
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_APPEND, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_APPEND_NOCONTEXT, &diff_opts));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__prepend_and_append(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
FILE_PREPEND_AND_APPEND, "file.txt",
|
||||||
|
PATCH_ORIGINAL_TO_PREPEND_AND_APPEND, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__to_empty_file(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
"", NULL,
|
||||||
|
PATCH_ORIGINAL_TO_EMPTY_FILE, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__from_empty_file(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
"", NULL,
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
PATCH_EMPTY_FILE_TO_ORIGINAL, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__add(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
NULL, NULL,
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
PATCH_ADD_ORIGINAL, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_apply_fromdiff__delete(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(apply_buf(
|
||||||
|
FILE_ORIGINAL, "file.txt",
|
||||||
|
NULL, NULL,
|
||||||
|
PATCH_DELETE_ORIGINAL, NULL));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user