mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-04 09:18:48 +00:00
commit
20302aa437
@ -264,10 +264,15 @@ typedef enum {
|
||||
* link, a submodule commit id, or even a tree (although that only if you
|
||||
* are tracking type changes or ignored/untracked directories).
|
||||
*
|
||||
* The `oid` is the `git_oid` of the item. If the entry represents an
|
||||
* The `id` is the `git_oid` of the item. If the entry represents an
|
||||
* absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
|
||||
* then the oid will be zeroes.
|
||||
*
|
||||
* The `id_abbrev` represents the known length of the `id` field, when
|
||||
* converted to a hex string. It is generally `GIT_OID_HEXSZ`, unless this
|
||||
* delta was created from reading a patch file, in which case it may be
|
||||
* abbreviated to something reasonable, like 7 characters.
|
||||
*
|
||||
* `path` is the NUL-terminated path to the entry relative to the working
|
||||
* directory of the repository.
|
||||
*
|
||||
@ -280,6 +285,7 @@ typedef enum {
|
||||
*/
|
||||
typedef struct {
|
||||
git_oid id;
|
||||
int id_abbrev;
|
||||
const char *path;
|
||||
git_off_t size;
|
||||
uint32_t flags;
|
||||
@ -448,6 +454,8 @@ typedef int (*git_diff_file_cb)(
|
||||
float progress,
|
||||
void *payload);
|
||||
|
||||
#define GIT_DIFF_HUNK_HEADER_SIZE 128
|
||||
|
||||
/**
|
||||
* When producing a binary diff, the binary data returned will be
|
||||
* either the deflated full ("literal") contents of the file, or
|
||||
@ -499,12 +507,12 @@ typedef int(*git_diff_binary_cb)(
|
||||
* Structure describing a hunk of a diff.
|
||||
*/
|
||||
typedef struct {
|
||||
int old_start; /**< Starting line number in old_file */
|
||||
int old_lines; /**< Number of lines in old_file */
|
||||
int new_start; /**< Starting line number in new_file */
|
||||
int new_lines; /**< Number of lines in new_file */
|
||||
size_t header_len; /**< Number of bytes in header text */
|
||||
char header[128]; /**< Header text, NUL-byte terminated */
|
||||
int old_start; /** Starting line number in old_file */
|
||||
int old_lines; /** Number of lines in old_file */
|
||||
int new_start; /** Starting line number in new_file */
|
||||
int new_lines; /** Number of lines in new_file */
|
||||
size_t header_len; /** Number of bytes in header text */
|
||||
char header[GIT_DIFF_HUNK_HEADER_SIZE]; /** Header text, NUL-byte terminated */
|
||||
} git_diff_hunk;
|
||||
|
||||
/**
|
||||
@ -1046,6 +1054,21 @@ GIT_EXTERN(int) git_diff_print(
|
||||
git_diff_line_cb print_cb,
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Produce the complete formatted text output from a diff into a
|
||||
* buffer.
|
||||
*
|
||||
* @param out A pointer to a user-allocated git_buf that will
|
||||
* contain the diff text
|
||||
* @param diff A git_diff generated by one of the above functions.
|
||||
* @param format A git_diff_format_t value to pick the text format.
|
||||
* @return 0 on success or error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_to_buf(
|
||||
git_buf *out,
|
||||
git_diff *diff,
|
||||
git_diff_format_t format);
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
||||
@ -1166,6 +1189,11 @@ GIT_EXTERN(int) git_diff_buffers(
|
||||
git_diff_line_cb line_cb,
|
||||
void *payload);
|
||||
|
||||
GIT_EXTERN(int) git_diff_from_buffer(
|
||||
git_diff **out,
|
||||
const char *content,
|
||||
size_t content_len);
|
||||
|
||||
/**
|
||||
* This is an opaque structure which is allocated by `git_diff_get_stats`.
|
||||
* You are responsible for releasing the object memory when done, using the
|
||||
|
@ -98,7 +98,8 @@ typedef enum {
|
||||
GITERR_CHERRYPICK,
|
||||
GITERR_DESCRIBE,
|
||||
GITERR_REBASE,
|
||||
GITERR_FILESYSTEM
|
||||
GITERR_FILESYSTEM,
|
||||
GITERR_PATCH,
|
||||
} git_error_t;
|
||||
|
||||
/**
|
||||
|
366
src/apply.c
Normal file
366
src/apply.c
Normal file
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* 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 "patch.h"
|
||||
#include "fileops.h"
|
||||
#include "apply.h"
|
||||
#include "delta.h"
|
||||
#include "zstream.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;
|
||||
}
|
||||
|
||||
#define PATCH_IMAGE_INIT { {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_insert_null(
|
||||
&image->lines, linenum, (postlen - prelen));
|
||||
else if (prelen > postlen)
|
||||
error = git_vector_remove_range(
|
||||
&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,
|
||||
git_patch_hunk *hunk)
|
||||
{
|
||||
patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
|
||||
size_t line_num, i;
|
||||
int error = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
git_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;
|
||||
}
|
||||
|
||||
static int apply_binary_delta(
|
||||
git_buf *out,
|
||||
const char *source,
|
||||
size_t source_len,
|
||||
git_diff_binary_file *binary_file)
|
||||
{
|
||||
git_buf inflated = GIT_BUF_INIT;
|
||||
int error = 0;
|
||||
|
||||
/* no diff means identical contents */
|
||||
if (binary_file->datalen == 0)
|
||||
return git_buf_put(out, source, source_len);
|
||||
|
||||
error = git_zstream_inflatebuf(&inflated,
|
||||
binary_file->data, binary_file->datalen);
|
||||
|
||||
if (!error && inflated.size != binary_file->inflatedlen) {
|
||||
error = apply_err("inflated delta does not match expected length");
|
||||
git_buf_free(out);
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
goto done;
|
||||
|
||||
if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
|
||||
void *data;
|
||||
size_t data_len;
|
||||
|
||||
error = git_delta_apply(&data, &data_len, (void *)source, source_len,
|
||||
(void *)inflated.ptr, inflated.size);
|
||||
|
||||
out->ptr = data;
|
||||
out->size = data_len;
|
||||
out->asize = data_len;
|
||||
}
|
||||
else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
|
||||
git_buf_swap(out, &inflated);
|
||||
}
|
||||
else {
|
||||
error = apply_err("unknown binary delta type");
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
git_buf_free(&inflated);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int apply_binary(
|
||||
git_buf *out,
|
||||
const char *source,
|
||||
size_t source_len,
|
||||
git_patch *patch)
|
||||
{
|
||||
git_buf reverse = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
/* first, apply the new_file delta to the given source */
|
||||
if ((error = apply_binary_delta(out, source, source_len,
|
||||
&patch->binary.new_file)) < 0)
|
||||
goto done;
|
||||
|
||||
/* second, apply the old_file delta to sanity check the result */
|
||||
if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
|
||||
&patch->binary.old_file)) < 0)
|
||||
goto done;
|
||||
|
||||
if (source_len != reverse.size ||
|
||||
memcmp(source, reverse.ptr, source_len) != 0) {
|
||||
error = apply_err("binary patch did not apply cleanly");
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (error < 0)
|
||||
git_buf_free(out);
|
||||
|
||||
git_buf_free(&reverse);
|
||||
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) {
|
||||
const git_diff_file *newfile = &patch->delta->new_file;
|
||||
|
||||
filename = git__strdup(newfile->path);
|
||||
mode = newfile->mode ?
|
||||
newfile->mode : GIT_FILEMODE_BLOB;
|
||||
}
|
||||
|
||||
if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
|
||||
error = apply_binary(contents_out, source, source_len, patch);
|
||||
else if (patch->hunks.size)
|
||||
error = apply_hunks(contents_out, source, source_len, patch);
|
||||
else
|
||||
error = git_buf_put(contents_out, source, source_len);
|
||||
|
||||
if (error)
|
||||
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) \
|
||||
for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
|
||||
|
||||
|
||||
GIT_INLINE(int) git_array__search(
|
||||
size_t *out,
|
||||
void *array_ptr,
|
||||
|
272
src/buffer.c
272
src/buffer.c
@ -273,42 +273,51 @@ int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The inverse of base64_encode, offset by '+' == 43. */
|
||||
/* The inverse of base64_encode */
|
||||
static const int8_t base64_decode[] = {
|
||||
62,
|
||||
-1, -1, -1,
|
||||
63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
|
||||
-1, -1, -1, 0, -1, -1, -1,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||||
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||||
-1, -1, -1, -1, -1, -1,
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
#define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43])
|
||||
|
||||
int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
int8_t a, b, c, d;
|
||||
size_t orig_size = buf->size, new_size;
|
||||
|
||||
if (len % 4) {
|
||||
giterr_set(GITERR_INVALID, "invalid base64 input");
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(len % 4 == 0);
|
||||
GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
|
||||
GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
|
||||
ENSURE_SIZE(buf, new_size);
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
|
||||
(b = BASE64_DECODE_VALUE(base64[i+1])) < 0 ||
|
||||
(c = BASE64_DECODE_VALUE(base64[i+2])) < 0 ||
|
||||
(d = BASE64_DECODE_VALUE(base64[i+3])) < 0) {
|
||||
if ((a = base64_decode[(unsigned char)base64[i]]) < 0 ||
|
||||
(b = base64_decode[(unsigned char)base64[i+1]]) < 0 ||
|
||||
(c = base64_decode[(unsigned char)base64[i+2]]) < 0 ||
|
||||
(d = base64_decode[(unsigned char)base64[i+3]]) < 0) {
|
||||
buf->size = orig_size;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
|
||||
giterr_set(GITERR_INVALID, "Invalid base64 input");
|
||||
giterr_set(GITERR_INVALID, "invalid base64 input");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -321,7 +330,7 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char b85str[] =
|
||||
static const char base85_encode[] =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
|
||||
|
||||
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
|
||||
@ -351,7 +360,7 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
|
||||
int val = acc % 85;
|
||||
acc /= 85;
|
||||
|
||||
b85[i] = b85str[val];
|
||||
b85[i] = base85_encode[val];
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
@ -363,6 +372,88 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The inverse of base85_encode */
|
||||
static const int8_t base85_decode[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 63, -1, 64, 65, 66, 67, -1, 68, 69, 70, 71, -1, 72, -1, -1,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 73, 74, 75, 76, 77,
|
||||
78, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, 79, 80,
|
||||
81, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
int git_buf_decode_base85(
|
||||
git_buf *buf,
|
||||
const char *base85,
|
||||
size_t base85_len,
|
||||
size_t output_len)
|
||||
{
|
||||
size_t orig_size = buf->size, new_size;
|
||||
|
||||
if (base85_len % 5 ||
|
||||
output_len > base85_len * 4 / 5) {
|
||||
giterr_set(GITERR_INVALID, "invalid base85 input");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GITERR_CHECK_ALLOC_ADD(&new_size, output_len, buf->size);
|
||||
GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
|
||||
ENSURE_SIZE(buf, new_size);
|
||||
|
||||
while (output_len) {
|
||||
unsigned acc = 0;
|
||||
int de, cnt = 4;
|
||||
unsigned char ch;
|
||||
do {
|
||||
ch = *base85++;
|
||||
de = base85_decode[ch];
|
||||
if (--de < 0)
|
||||
goto on_error;
|
||||
|
||||
acc = acc * 85 + de;
|
||||
} while (--cnt);
|
||||
ch = *base85++;
|
||||
de = base85_decode[ch];
|
||||
if (--de < 0)
|
||||
goto on_error;
|
||||
|
||||
/* Detect overflow. */
|
||||
if (0xffffffff / 85 < acc ||
|
||||
0xffffffff - de < (acc *= 85))
|
||||
goto on_error;
|
||||
|
||||
acc += de;
|
||||
|
||||
cnt = (output_len < 4) ? output_len : 4;
|
||||
output_len -= cnt;
|
||||
do {
|
||||
acc = (acc << 8) | (acc >> 24);
|
||||
buf->ptr[buf->size++] = acc;
|
||||
} while (--cnt);
|
||||
}
|
||||
|
||||
buf->ptr[buf->size] = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
buf->size = orig_size;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
|
||||
giterr_set(GITERR_INVALID, "invalid base85 input");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
|
||||
{
|
||||
size_t expected_size, new_size;
|
||||
@ -766,3 +857,144 @@ int git_buf_splice(
|
||||
buf->ptr[buf->size] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
|
||||
int git_buf_quote(git_buf *buf)
|
||||
{
|
||||
const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
|
||||
git_buf quoted = GIT_BUF_INIT;
|
||||
size_t i = 0;
|
||||
bool quote = false;
|
||||
int error = 0;
|
||||
|
||||
/* walk to the first char that needs quoting */
|
||||
if (buf->size && buf->ptr[0] == '!')
|
||||
quote = true;
|
||||
|
||||
for (i = 0; !quote && i < buf->size; i++) {
|
||||
if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
|
||||
buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
|
||||
quote = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!quote)
|
||||
goto done;
|
||||
|
||||
git_buf_putc("ed, '"');
|
||||
git_buf_put("ed, buf->ptr, i);
|
||||
|
||||
for (; i < buf->size; i++) {
|
||||
/* whitespace - use the map above, which is ordered by ascii value */
|
||||
if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
|
||||
git_buf_putc("ed, '\\');
|
||||
git_buf_putc("ed, whitespace[buf->ptr[i] - '\a']);
|
||||
}
|
||||
|
||||
/* double quote and backslash must be escaped */
|
||||
else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
|
||||
git_buf_putc("ed, '\\');
|
||||
git_buf_putc("ed, buf->ptr[i]);
|
||||
}
|
||||
|
||||
/* escape anything unprintable as octal */
|
||||
else if (buf->ptr[i] != ' ' &&
|
||||
(buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
|
||||
git_buf_printf("ed, "\\%03o", (unsigned char)buf->ptr[i]);
|
||||
}
|
||||
|
||||
/* yay, printable! */
|
||||
else {
|
||||
git_buf_putc("ed, buf->ptr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
git_buf_putc("ed, '"');
|
||||
|
||||
if (git_buf_oom("ed)) {
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
git_buf_swap("ed, buf);
|
||||
|
||||
done:
|
||||
git_buf_free("ed);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
|
||||
int git_buf_unquote(git_buf *buf)
|
||||
{
|
||||
size_t i, j;
|
||||
char ch;
|
||||
|
||||
git_buf_rtrim(buf);
|
||||
|
||||
if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
|
||||
goto invalid;
|
||||
|
||||
for (i = 0, j = 1; j < buf->size-1; i++, j++) {
|
||||
ch = buf->ptr[j];
|
||||
|
||||
if (ch == '\\') {
|
||||
if (j == buf->size-2)
|
||||
goto invalid;
|
||||
|
||||
ch = buf->ptr[++j];
|
||||
|
||||
switch (ch) {
|
||||
/* \" or \\ simply copy the char in */
|
||||
case '"': case '\\':
|
||||
break;
|
||||
|
||||
/* add the appropriate escaped char */
|
||||
case 'a': ch = '\a'; break;
|
||||
case 'b': ch = '\b'; break;
|
||||
case 'f': ch = '\f'; break;
|
||||
case 'n': ch = '\n'; break;
|
||||
case 'r': ch = '\r'; break;
|
||||
case 't': ch = '\t'; break;
|
||||
case 'v': ch = '\v'; break;
|
||||
|
||||
/* \xyz digits convert to the char*/
|
||||
case '0': case '1': case '2': case '3':
|
||||
if (j == buf->size-3) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"Truncated quoted character \\%c", ch);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
|
||||
buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"Truncated quoted character \\%c%c%c",
|
||||
buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ch = ((buf->ptr[j] - '0') << 6) |
|
||||
((buf->ptr[j+1] - '0') << 3) |
|
||||
(buf->ptr[j+2] - '0');
|
||||
j += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
giterr_set(GITERR_INVALID, "Invalid quoted character \\%c", ch);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
buf->ptr[i] = ch;
|
||||
}
|
||||
|
||||
buf->ptr[i] = '\0';
|
||||
buf->size = i;
|
||||
|
||||
return 0;
|
||||
|
||||
invalid:
|
||||
giterr_set(GITERR_INVALID, "Invalid quoted line");
|
||||
return -1;
|
||||
}
|
||||
|
@ -173,6 +173,12 @@ void git_buf_rtrim(git_buf *buf);
|
||||
|
||||
int git_buf_cmp(const git_buf *a, const git_buf *b);
|
||||
|
||||
/* Quote and unquote a buffer as specified in
|
||||
* http://marc.info/?l=git&m=112927316408690&w=2
|
||||
*/
|
||||
int git_buf_quote(git_buf *buf);
|
||||
int git_buf_unquote(git_buf *buf);
|
||||
|
||||
/* Write data as base64 encoded in buffer */
|
||||
int git_buf_encode_base64(git_buf *buf, const char *data, size_t len);
|
||||
/* Decode the given bas64 and write the result to the buffer */
|
||||
@ -180,6 +186,8 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len);
|
||||
|
||||
/* Write data as "base85" encoded in buffer */
|
||||
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len);
|
||||
/* Decode the given "base85" and write the result to the buffer */
|
||||
int git_buf_decode_base85(git_buf *buf, const char *base64, size_t len, size_t output_len);
|
||||
|
||||
/*
|
||||
* Insert, remove or replace a portion of the buffer.
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "filter.h"
|
||||
#include "blob.h"
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
#include "pathspec.h"
|
||||
#include "buf_text.h"
|
||||
#include "diff_xdiff.h"
|
||||
|
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
#include "git2/odb.h"
|
||||
#include "delta-apply.h"
|
||||
|
||||
/*
|
||||
* This file was heavily cribbed from BinaryDelta.java in JGit, which
|
||||
* itself was heavily cribbed from <code>patch-delta.c</code> in the
|
||||
* GIT project. The original delta patching code was written by
|
||||
* Nicolas Pitre <nico@cam.org>.
|
||||
*/
|
||||
|
||||
static int hdr_sz(
|
||||
size_t *size,
|
||||
const unsigned char **delta,
|
||||
const unsigned char *end)
|
||||
{
|
||||
const unsigned char *d = *delta;
|
||||
size_t r = 0;
|
||||
unsigned int c, shift = 0;
|
||||
|
||||
do {
|
||||
if (d == end)
|
||||
return -1;
|
||||
c = *d++;
|
||||
r |= (c & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while (c & 0x80);
|
||||
*delta = d;
|
||||
*size = r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git__delta_read_header(
|
||||
const unsigned char *delta,
|
||||
size_t delta_len,
|
||||
size_t *base_sz,
|
||||
size_t *res_sz)
|
||||
{
|
||||
const unsigned char *delta_end = delta + delta_len;
|
||||
if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
|
||||
(hdr_sz(res_sz, &delta, delta_end) < 0))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DELTA_HEADER_BUFFER_LEN 16
|
||||
int git__delta_read_header_fromstream(size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
|
||||
{
|
||||
static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
|
||||
unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
|
||||
const unsigned char *delta, *delta_end;
|
||||
size_t len;
|
||||
ssize_t read;
|
||||
|
||||
len = read = 0;
|
||||
while (len < buffer_len) {
|
||||
read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
|
||||
|
||||
if (read == 0)
|
||||
break;
|
||||
|
||||
if (read == GIT_EBUFS)
|
||||
continue;
|
||||
|
||||
len += read;
|
||||
}
|
||||
|
||||
delta = buffer;
|
||||
delta_end = delta + len;
|
||||
if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
|
||||
(hdr_sz(res_sz, &delta, delta_end) < 0))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git__delta_apply(
|
||||
git_rawobj *out,
|
||||
const unsigned char *base,
|
||||
size_t base_len,
|
||||
const unsigned char *delta,
|
||||
size_t delta_len)
|
||||
{
|
||||
const unsigned char *delta_end = delta + delta_len;
|
||||
size_t base_sz, res_sz, alloc_sz;
|
||||
unsigned char *res_dp;
|
||||
|
||||
/* Check that the base size matches the data we were given;
|
||||
* if not we would underflow while accessing data from the
|
||||
* base object, resulting in data corruption or segfault.
|
||||
*/
|
||||
if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GITERR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1);
|
||||
res_dp = git__malloc(alloc_sz);
|
||||
GITERR_CHECK_ALLOC(res_dp);
|
||||
|
||||
res_dp[res_sz] = '\0';
|
||||
out->data = res_dp;
|
||||
out->len = res_sz;
|
||||
|
||||
while (delta < delta_end) {
|
||||
unsigned char cmd = *delta++;
|
||||
if (cmd & 0x80) {
|
||||
/* cmd is a copy instruction; copy from the base.
|
||||
*/
|
||||
size_t off = 0, len = 0;
|
||||
|
||||
if (cmd & 0x01) off = *delta++;
|
||||
if (cmd & 0x02) off |= *delta++ << 8UL;
|
||||
if (cmd & 0x04) off |= *delta++ << 16UL;
|
||||
if (cmd & 0x08) off |= *delta++ << 24UL;
|
||||
|
||||
if (cmd & 0x10) len = *delta++;
|
||||
if (cmd & 0x20) len |= *delta++ << 8UL;
|
||||
if (cmd & 0x40) len |= *delta++ << 16UL;
|
||||
if (!len) len = 0x10000;
|
||||
|
||||
if (base_len < off + len || res_sz < len)
|
||||
goto fail;
|
||||
memcpy(res_dp, base + off, len);
|
||||
res_dp += len;
|
||||
res_sz -= len;
|
||||
|
||||
} else if (cmd) {
|
||||
/* cmd is a literal insert instruction; copy from
|
||||
* the delta stream itself.
|
||||
*/
|
||||
if (delta_end - delta < cmd || res_sz < cmd)
|
||||
goto fail;
|
||||
memcpy(res_dp, delta, cmd);
|
||||
delta += cmd;
|
||||
res_dp += cmd;
|
||||
res_sz -= cmd;
|
||||
|
||||
} else {
|
||||
/* cmd == 0 is reserved for future encodings.
|
||||
*/
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (delta != delta_end || res_sz)
|
||||
goto fail;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
git__free(out->data);
|
||||
out->data = NULL;
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta");
|
||||
return -1;
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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_delta_apply_h__
|
||||
#define INCLUDE_delta_apply_h__
|
||||
|
||||
#include "odb.h"
|
||||
#include "pack.h"
|
||||
|
||||
/**
|
||||
* Apply a git binary delta to recover the original content.
|
||||
*
|
||||
* @param out the output buffer to receive the original data.
|
||||
* Only out->data and out->len are populated, as this is
|
||||
* the only information available in the delta.
|
||||
* @param base the base to copy from during copy instructions.
|
||||
* @param base_len number of bytes available at base.
|
||||
* @param delta the delta to execute copy/insert instructions from.
|
||||
* @param delta_len total number of bytes in the delta.
|
||||
* @return
|
||||
* - 0 on a successful delta unpack.
|
||||
* - GIT_ERROR if the delta is corrupt or doesn't match the base.
|
||||
*/
|
||||
extern int git__delta_apply(
|
||||
git_rawobj *out,
|
||||
const unsigned char *base,
|
||||
size_t base_len,
|
||||
const unsigned char *delta,
|
||||
size_t delta_len);
|
||||
|
||||
/**
|
||||
* Read the header of a git binary delta.
|
||||
*
|
||||
* @param delta the delta to execute copy/insert instructions from.
|
||||
* @param delta_len total number of bytes in the delta.
|
||||
* @param base_sz pointer to store the base size field.
|
||||
* @param res_sz pointer to store the result size field.
|
||||
* @return
|
||||
* - 0 on a successful decoding the header.
|
||||
* - GIT_ERROR if the delta is corrupt.
|
||||
*/
|
||||
extern int git__delta_read_header(
|
||||
const unsigned char *delta,
|
||||
size_t delta_len,
|
||||
size_t *base_sz,
|
||||
size_t *res_sz);
|
||||
|
||||
/**
|
||||
* Read the header of a git binary delta
|
||||
*
|
||||
* This variant reads just enough from the packfile stream to read the
|
||||
* delta header.
|
||||
*/
|
||||
extern int git__delta_read_header_fromstream(
|
||||
size_t *base_sz,
|
||||
size_t *res_sz,
|
||||
git_packfile_stream *stream);
|
||||
|
||||
#endif
|
300
src/delta.c
300
src/delta.c
@ -114,7 +114,7 @@ struct index_entry {
|
||||
struct git_delta_index {
|
||||
unsigned long memsize;
|
||||
const void *src_buf;
|
||||
unsigned long src_size;
|
||||
size_t src_size;
|
||||
unsigned int hash_mask;
|
||||
struct index_entry *hash[GIT_FLEX_ARRAY];
|
||||
};
|
||||
@ -142,8 +142,8 @@ static int lookup_index_alloc(
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct git_delta_index *
|
||||
git_delta_create_index(const void *buf, unsigned long bufsize)
|
||||
int git_delta_index_init(
|
||||
git_delta_index **out, const void *buf, size_t bufsize)
|
||||
{
|
||||
unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
|
||||
const unsigned char *data, *buffer = buf;
|
||||
@ -152,8 +152,10 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
|
||||
void *mem;
|
||||
unsigned long memsize;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
if (!buf || !bufsize)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
/* Determine index hash size. Note that indexing skips the
|
||||
first byte to allow for optimizing the rabin polynomial
|
||||
@ -172,7 +174,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
|
||||
hmask = hsize - 1;
|
||||
|
||||
if (lookup_index_alloc(&mem, &memsize, entries, hsize) < 0)
|
||||
return NULL;
|
||||
return -1;
|
||||
|
||||
index = mem;
|
||||
mem = index->hash;
|
||||
@ -190,7 +192,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
|
||||
hash_count = git__calloc(hsize, sizeof(*hash_count));
|
||||
if (!hash_count) {
|
||||
git__free(index);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* then populate the index */
|
||||
@ -243,20 +245,20 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
|
||||
}
|
||||
git__free(hash_count);
|
||||
|
||||
return index;
|
||||
*out = index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_delta_free_index(struct git_delta_index *index)
|
||||
void git_delta_index_free(git_delta_index *index)
|
||||
{
|
||||
git__free(index);
|
||||
}
|
||||
|
||||
unsigned long git_delta_sizeof_index(struct git_delta_index *index)
|
||||
size_t git_delta_index_size(git_delta_index *index)
|
||||
{
|
||||
if (index)
|
||||
return index->memsize;
|
||||
else
|
||||
return 0;
|
||||
assert(index);
|
||||
|
||||
return index->memsize;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -265,55 +267,57 @@ unsigned long git_delta_sizeof_index(struct git_delta_index *index)
|
||||
*/
|
||||
#define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7)
|
||||
|
||||
void *
|
||||
git_delta_create(
|
||||
int git_delta_create_from_index(
|
||||
void **out,
|
||||
size_t *out_len,
|
||||
const struct git_delta_index *index,
|
||||
const void *trg_buf,
|
||||
unsigned long trg_size,
|
||||
unsigned long *delta_size,
|
||||
unsigned long max_size)
|
||||
size_t trg_size,
|
||||
size_t max_size)
|
||||
{
|
||||
unsigned int i, outpos, outsize, moff, msize, val;
|
||||
unsigned int i, bufpos, bufsize, moff, msize, val;
|
||||
int inscnt;
|
||||
const unsigned char *ref_data, *ref_top, *data, *top;
|
||||
unsigned char *out;
|
||||
unsigned char *buf;
|
||||
|
||||
*out = NULL;
|
||||
*out_len = 0;
|
||||
|
||||
if (!trg_buf || !trg_size)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
outpos = 0;
|
||||
outsize = 8192;
|
||||
if (max_size && outsize >= max_size)
|
||||
outsize = (unsigned int)(max_size + MAX_OP_SIZE + 1);
|
||||
out = git__malloc(outsize);
|
||||
if (!out)
|
||||
return NULL;
|
||||
bufpos = 0;
|
||||
bufsize = 8192;
|
||||
if (max_size && bufsize >= max_size)
|
||||
bufsize = (unsigned int)(max_size + MAX_OP_SIZE + 1);
|
||||
buf = git__malloc(bufsize);
|
||||
GITERR_CHECK_ALLOC(buf);
|
||||
|
||||
/* store reference buffer size */
|
||||
i = index->src_size;
|
||||
while (i >= 0x80) {
|
||||
out[outpos++] = i | 0x80;
|
||||
buf[bufpos++] = i | 0x80;
|
||||
i >>= 7;
|
||||
}
|
||||
out[outpos++] = i;
|
||||
buf[bufpos++] = i;
|
||||
|
||||
/* store target buffer size */
|
||||
i = trg_size;
|
||||
while (i >= 0x80) {
|
||||
out[outpos++] = i | 0x80;
|
||||
buf[bufpos++] = i | 0x80;
|
||||
i >>= 7;
|
||||
}
|
||||
out[outpos++] = i;
|
||||
buf[bufpos++] = i;
|
||||
|
||||
ref_data = index->src_buf;
|
||||
ref_top = ref_data + index->src_size;
|
||||
data = trg_buf;
|
||||
top = (const unsigned char *) trg_buf + trg_size;
|
||||
|
||||
outpos++;
|
||||
bufpos++;
|
||||
val = 0;
|
||||
for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) {
|
||||
out[outpos++] = *data;
|
||||
buf[bufpos++] = *data;
|
||||
val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
|
||||
}
|
||||
inscnt = i;
|
||||
@ -350,11 +354,11 @@ git_delta_create(
|
||||
|
||||
if (msize < 4) {
|
||||
if (!inscnt)
|
||||
outpos++;
|
||||
out[outpos++] = *data++;
|
||||
bufpos++;
|
||||
buf[bufpos++] = *data++;
|
||||
inscnt++;
|
||||
if (inscnt == 0x7f) {
|
||||
out[outpos - inscnt - 1] = inscnt;
|
||||
buf[bufpos - inscnt - 1] = inscnt;
|
||||
inscnt = 0;
|
||||
}
|
||||
msize = 0;
|
||||
@ -368,14 +372,14 @@ git_delta_create(
|
||||
msize++;
|
||||
moff--;
|
||||
data--;
|
||||
outpos--;
|
||||
bufpos--;
|
||||
if (--inscnt)
|
||||
continue;
|
||||
outpos--; /* remove count slot */
|
||||
bufpos--; /* remove count slot */
|
||||
inscnt--; /* make it -1 */
|
||||
break;
|
||||
}
|
||||
out[outpos - inscnt - 1] = inscnt;
|
||||
buf[bufpos - inscnt - 1] = inscnt;
|
||||
inscnt = 0;
|
||||
}
|
||||
|
||||
@ -383,22 +387,22 @@ git_delta_create(
|
||||
left = (msize < 0x10000) ? 0 : (msize - 0x10000);
|
||||
msize -= left;
|
||||
|
||||
op = out + outpos++;
|
||||
op = buf + bufpos++;
|
||||
i = 0x80;
|
||||
|
||||
if (moff & 0x000000ff)
|
||||
out[outpos++] = moff >> 0, i |= 0x01;
|
||||
buf[bufpos++] = moff >> 0, i |= 0x01;
|
||||
if (moff & 0x0000ff00)
|
||||
out[outpos++] = moff >> 8, i |= 0x02;
|
||||
buf[bufpos++] = moff >> 8, i |= 0x02;
|
||||
if (moff & 0x00ff0000)
|
||||
out[outpos++] = moff >> 16, i |= 0x04;
|
||||
buf[bufpos++] = moff >> 16, i |= 0x04;
|
||||
if (moff & 0xff000000)
|
||||
out[outpos++] = moff >> 24, i |= 0x08;
|
||||
buf[bufpos++] = moff >> 24, i |= 0x08;
|
||||
|
||||
if (msize & 0x00ff)
|
||||
out[outpos++] = msize >> 0, i |= 0x10;
|
||||
buf[bufpos++] = msize >> 0, i |= 0x10;
|
||||
if (msize & 0xff00)
|
||||
out[outpos++] = msize >> 8, i |= 0x20;
|
||||
buf[bufpos++] = msize >> 8, i |= 0x20;
|
||||
|
||||
*op = i;
|
||||
|
||||
@ -415,29 +419,201 @@ git_delta_create(
|
||||
}
|
||||
}
|
||||
|
||||
if (outpos >= outsize - MAX_OP_SIZE) {
|
||||
void *tmp = out;
|
||||
outsize = outsize * 3 / 2;
|
||||
if (max_size && outsize >= max_size)
|
||||
outsize = max_size + MAX_OP_SIZE + 1;
|
||||
if (max_size && outpos > max_size)
|
||||
if (bufpos >= bufsize - MAX_OP_SIZE) {
|
||||
void *tmp = buf;
|
||||
bufsize = bufsize * 3 / 2;
|
||||
if (max_size && bufsize >= max_size)
|
||||
bufsize = max_size + MAX_OP_SIZE + 1;
|
||||
if (max_size && bufpos > max_size)
|
||||
break;
|
||||
out = git__realloc(out, outsize);
|
||||
if (!out) {
|
||||
buf = git__realloc(buf, bufsize);
|
||||
if (!buf) {
|
||||
git__free(tmp);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inscnt)
|
||||
out[outpos - inscnt - 1] = inscnt;
|
||||
buf[bufpos - inscnt - 1] = inscnt;
|
||||
|
||||
if (max_size && outpos > max_size) {
|
||||
git__free(out);
|
||||
return NULL;
|
||||
if (max_size && bufpos > max_size) {
|
||||
giterr_set(GITERR_NOMEMORY, "delta would be larger than maximum size");
|
||||
git__free(buf);
|
||||
return GIT_EBUFS;
|
||||
}
|
||||
|
||||
*delta_size = outpos;
|
||||
return out;
|
||||
*out_len = bufpos;
|
||||
*out = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delta application was heavily cribbed from BinaryDelta.java in JGit, which
|
||||
* itself was heavily cribbed from <code>patch-delta.c</code> in the
|
||||
* GIT project. The original delta patching code was written by
|
||||
* Nicolas Pitre <nico@cam.org>.
|
||||
*/
|
||||
|
||||
static int hdr_sz(
|
||||
size_t *size,
|
||||
const unsigned char **delta,
|
||||
const unsigned char *end)
|
||||
{
|
||||
const unsigned char *d = *delta;
|
||||
size_t r = 0;
|
||||
unsigned int c, shift = 0;
|
||||
|
||||
do {
|
||||
if (d == end) {
|
||||
giterr_set(GITERR_INVALID, "truncated delta");
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = *d++;
|
||||
r |= (c & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while (c & 0x80);
|
||||
*delta = d;
|
||||
*size = r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_delta_read_header(
|
||||
size_t *base_out,
|
||||
size_t *result_out,
|
||||
const unsigned char *delta,
|
||||
size_t delta_len)
|
||||
{
|
||||
const unsigned char *delta_end = delta + delta_len;
|
||||
if ((hdr_sz(base_out, &delta, delta_end) < 0) ||
|
||||
(hdr_sz(result_out, &delta, delta_end) < 0))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DELTA_HEADER_BUFFER_LEN 16
|
||||
int git_delta_read_header_fromstream(
|
||||
size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
|
||||
{
|
||||
static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
|
||||
unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
|
||||
const unsigned char *delta, *delta_end;
|
||||
size_t len;
|
||||
ssize_t read;
|
||||
|
||||
len = read = 0;
|
||||
while (len < buffer_len) {
|
||||
read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
|
||||
|
||||
if (read == 0)
|
||||
break;
|
||||
|
||||
if (read == GIT_EBUFS)
|
||||
continue;
|
||||
|
||||
len += read;
|
||||
}
|
||||
|
||||
delta = buffer;
|
||||
delta_end = delta + len;
|
||||
if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
|
||||
(hdr_sz(res_sz, &delta, delta_end) < 0))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_delta_apply(
|
||||
void **out,
|
||||
size_t *out_len,
|
||||
const unsigned char *base,
|
||||
size_t base_len,
|
||||
const unsigned char *delta,
|
||||
size_t delta_len)
|
||||
{
|
||||
const unsigned char *delta_end = delta + delta_len;
|
||||
size_t base_sz, res_sz, alloc_sz;
|
||||
unsigned char *res_dp;
|
||||
|
||||
*out = NULL;
|
||||
*out_len = 0;
|
||||
|
||||
/* Check that the base size matches the data we were given;
|
||||
* if not we would underflow while accessing data from the
|
||||
* base object, resulting in data corruption or segfault.
|
||||
*/
|
||||
if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GITERR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1);
|
||||
res_dp = git__malloc(alloc_sz);
|
||||
GITERR_CHECK_ALLOC(res_dp);
|
||||
|
||||
res_dp[res_sz] = '\0';
|
||||
*out = res_dp;
|
||||
*out_len = res_sz;
|
||||
|
||||
while (delta < delta_end) {
|
||||
unsigned char cmd = *delta++;
|
||||
if (cmd & 0x80) {
|
||||
/* cmd is a copy instruction; copy from the base.
|
||||
*/
|
||||
size_t off = 0, len = 0;
|
||||
|
||||
if (cmd & 0x01) off = *delta++;
|
||||
if (cmd & 0x02) off |= *delta++ << 8UL;
|
||||
if (cmd & 0x04) off |= *delta++ << 16UL;
|
||||
if (cmd & 0x08) off |= *delta++ << 24UL;
|
||||
|
||||
if (cmd & 0x10) len = *delta++;
|
||||
if (cmd & 0x20) len |= *delta++ << 8UL;
|
||||
if (cmd & 0x40) len |= *delta++ << 16UL;
|
||||
if (!len) len = 0x10000;
|
||||
|
||||
if (base_len < off + len || res_sz < len)
|
||||
goto fail;
|
||||
memcpy(res_dp, base + off, len);
|
||||
res_dp += len;
|
||||
res_sz -= len;
|
||||
|
||||
}
|
||||
else if (cmd) {
|
||||
/* cmd is a literal insert instruction; copy from
|
||||
* the delta stream itself.
|
||||
*/
|
||||
if (delta_end - delta < cmd || res_sz < cmd)
|
||||
goto fail;
|
||||
memcpy(res_dp, delta, cmd);
|
||||
delta += cmd;
|
||||
res_dp += cmd;
|
||||
res_sz -= cmd;
|
||||
|
||||
}
|
||||
else {
|
||||
/* cmd == 0 is reserved for future encodings.
|
||||
*/
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (delta != delta_end || res_sz)
|
||||
goto fail;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
git__free(*out);
|
||||
|
||||
*out = NULL;
|
||||
*out_len = 0;
|
||||
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta");
|
||||
return -1;
|
||||
}
|
||||
|
141
src/delta.h
141
src/delta.h
@ -6,12 +6,12 @@
|
||||
#define INCLUDE_git_delta_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "pack.h"
|
||||
|
||||
/* opaque object for delta index */
|
||||
struct git_delta_index;
|
||||
typedef struct git_delta_index git_delta_index;
|
||||
|
||||
/*
|
||||
* create_delta_index: compute index data from given buffer
|
||||
* git_delta_index_init: compute index data from given buffer
|
||||
*
|
||||
* This returns a pointer to a struct delta_index that should be passed to
|
||||
* subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
|
||||
@ -19,22 +19,18 @@ struct git_delta_index;
|
||||
* before free_delta_index() is called. The returned pointer must be freed
|
||||
* using free_delta_index().
|
||||
*/
|
||||
extern struct git_delta_index *
|
||||
git_delta_create_index(const void *buf, unsigned long bufsize);
|
||||
extern int git_delta_index_init(
|
||||
git_delta_index **out, const void *buf, size_t bufsize);
|
||||
|
||||
/*
|
||||
* free_delta_index: free the index created by create_delta_index()
|
||||
*
|
||||
* Given pointer must be what create_delta_index() returned, or NULL.
|
||||
* Free the index created by git_delta_index_init()
|
||||
*/
|
||||
extern void git_delta_free_index(struct git_delta_index *index);
|
||||
extern void git_delta_index_free(git_delta_index *index);
|
||||
|
||||
/*
|
||||
* sizeof_delta_index: returns memory usage of delta index
|
||||
*
|
||||
* Given pointer must be what create_delta_index() returned, or NULL.
|
||||
* Returns memory usage of delta index.
|
||||
*/
|
||||
extern unsigned long git_delta_sizeof_index(struct git_delta_index *index);
|
||||
extern size_t git_delta_index_size(git_delta_index *index);
|
||||
|
||||
/*
|
||||
* create_delta: create a delta from given index for the given buffer
|
||||
@ -46,69 +42,94 @@ extern unsigned long git_delta_sizeof_index(struct git_delta_index *index);
|
||||
* returned and *delta_size is updated with its size. The returned buffer
|
||||
* must be freed by the caller.
|
||||
*/
|
||||
extern void *git_delta_create(
|
||||
extern int git_delta_create_from_index(
|
||||
void **out,
|
||||
size_t *out_size,
|
||||
const struct git_delta_index *index,
|
||||
const void *buf,
|
||||
unsigned long bufsize,
|
||||
unsigned long *delta_size,
|
||||
unsigned long max_delta_size);
|
||||
size_t bufsize,
|
||||
size_t max_delta_size);
|
||||
|
||||
/*
|
||||
* diff_delta: create a delta from source buffer to target buffer
|
||||
*
|
||||
* If max_delta_size is non-zero and the resulting delta is to be larger
|
||||
* than max_delta_size then NULL is returned. On success, a non-NULL
|
||||
* than max_delta_size then GIT_EBUFS is returned. On success, a non-NULL
|
||||
* pointer to the buffer with the delta data is returned and *delta_size is
|
||||
* updated with its size. The returned buffer must be freed by the caller.
|
||||
*/
|
||||
GIT_INLINE(void *) git_delta(
|
||||
const void *src_buf, unsigned long src_bufsize,
|
||||
const void *trg_buf, unsigned long trg_bufsize,
|
||||
unsigned long *delta_size,
|
||||
unsigned long max_delta_size)
|
||||
GIT_INLINE(int) git_delta(
|
||||
void **out, size_t *out_len,
|
||||
const void *src_buf, size_t src_bufsize,
|
||||
const void *trg_buf, size_t trg_bufsize,
|
||||
size_t max_delta_size)
|
||||
{
|
||||
struct git_delta_index *index = git_delta_create_index(src_buf, src_bufsize);
|
||||
if (index) {
|
||||
void *delta = git_delta_create(
|
||||
index, trg_buf, trg_bufsize, delta_size, max_delta_size);
|
||||
git_delta_free_index(index);
|
||||
return delta;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
git_delta_index *index;
|
||||
int error = 0;
|
||||
|
||||
/*
|
||||
* patch_delta: recreate target buffer given source buffer and delta data
|
||||
*
|
||||
* On success, a non-NULL pointer to the target buffer is returned and
|
||||
* *trg_bufsize is updated with its size. On failure a NULL pointer is
|
||||
* returned. The returned buffer must be freed by the caller.
|
||||
*/
|
||||
extern void *git_delta_patch(
|
||||
const void *src_buf, unsigned long src_size,
|
||||
const void *delta_buf, unsigned long delta_size,
|
||||
unsigned long *dst_size);
|
||||
*out = NULL;
|
||||
*out_len = 0;
|
||||
|
||||
if ((error = git_delta_index_init(&index, src_buf, src_bufsize)) < 0)
|
||||
return error;
|
||||
|
||||
if (index) {
|
||||
error = git_delta_create_from_index(out, out_len,
|
||||
index, trg_buf, trg_bufsize, max_delta_size);
|
||||
|
||||
git_delta_index_free(index);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* the smallest possible delta size is 4 bytes */
|
||||
#define GIT_DELTA_SIZE_MIN 4
|
||||
|
||||
/*
|
||||
* This must be called twice on the delta data buffer, first to get the
|
||||
* expected source buffer size, and again to get the target buffer size.
|
||||
/**
|
||||
* Apply a git binary delta to recover the original content.
|
||||
* The caller is responsible for freeing the returned buffer.
|
||||
*
|
||||
* @param out the output buffer
|
||||
* @param out_len the length of the output buffer
|
||||
* @param base the base to copy from during copy instructions.
|
||||
* @param base_len number of bytes available at base.
|
||||
* @param delta the delta to execute copy/insert instructions from.
|
||||
* @param delta_len total number of bytes in the delta.
|
||||
* @return 0 on success or an error code
|
||||
*/
|
||||
extern int git_delta_apply(
|
||||
void **out,
|
||||
size_t *out_len,
|
||||
const unsigned char *base,
|
||||
size_t base_len,
|
||||
const unsigned char *delta,
|
||||
size_t delta_len);
|
||||
|
||||
/**
|
||||
* Read the header of a git binary delta.
|
||||
*
|
||||
* @param base_out pointer to store the base size field.
|
||||
* @param result_out pointer to store the result size field.
|
||||
* @param delta the delta to execute copy/insert instructions from.
|
||||
* @param delta_len total number of bytes in the delta.
|
||||
* @return 0 on success or an error code
|
||||
*/
|
||||
extern int git_delta_read_header(
|
||||
size_t *base_out,
|
||||
size_t *result_out,
|
||||
const unsigned char *delta,
|
||||
size_t delta_len);
|
||||
|
||||
/**
|
||||
* Read the header of a git binary delta
|
||||
*
|
||||
* This variant reads just enough from the packfile stream to read the
|
||||
* delta header.
|
||||
*/
|
||||
GIT_INLINE(unsigned long) git_delta_get_hdr_size(
|
||||
const unsigned char **datap, const unsigned char *top)
|
||||
{
|
||||
const unsigned char *data = *datap;
|
||||
unsigned long cmd, size = 0;
|
||||
int i = 0;
|
||||
do {
|
||||
cmd = *data++;
|
||||
size |= (cmd & 0x7f) << i;
|
||||
i += 7;
|
||||
} while (cmd & 0x80 && data < top);
|
||||
*datap = data;
|
||||
return size;
|
||||
}
|
||||
extern int git_delta_read_header_fromstream(
|
||||
size_t *base_out,
|
||||
size_t *result_out,
|
||||
git_packfile_stream *stream);
|
||||
|
||||
#endif
|
||||
|
1579
src/diff.c
1579
src/diff.c
File diff suppressed because it is too large
Load Diff
134
src/diff.h
134
src/diff.h
@ -22,68 +22,31 @@
|
||||
#define DIFF_OLD_PREFIX_DEFAULT "a/"
|
||||
#define DIFF_NEW_PREFIX_DEFAULT "b/"
|
||||
|
||||
enum {
|
||||
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
|
||||
GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */
|
||||
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
|
||||
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
|
||||
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
|
||||
};
|
||||
|
||||
#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
|
||||
#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
|
||||
|
||||
enum {
|
||||
GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
|
||||
GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
|
||||
GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
|
||||
GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
|
||||
GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
|
||||
GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */
|
||||
|
||||
GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */
|
||||
GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */
|
||||
GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18),
|
||||
GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19),
|
||||
GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20),
|
||||
};
|
||||
|
||||
#define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF)
|
||||
|
||||
#define GIT_DIFF__VERBOSE (1 << 30)
|
||||
typedef enum {
|
||||
GIT_DIFF_TYPE_UNKNOWN = 0,
|
||||
GIT_DIFF_TYPE_GENERATED = 1,
|
||||
GIT_DIFF_TYPE_PARSED = 2,
|
||||
} git_diff_origin_t;
|
||||
|
||||
struct git_diff {
|
||||
git_refcount rc;
|
||||
git_repository *repo;
|
||||
git_diff_origin_t type;
|
||||
git_diff_options opts;
|
||||
git_vector pathspec;
|
||||
git_vector deltas; /* vector of git_diff_delta */
|
||||
git_pool pool;
|
||||
git_iterator_type_t old_src;
|
||||
git_iterator_type_t new_src;
|
||||
uint32_t diffcaps;
|
||||
git_diff_perfdata perf;
|
||||
bool index_updated;
|
||||
|
||||
int (*strcomp)(const char *, const char *);
|
||||
int (*strncomp)(const char *, const char *, size_t);
|
||||
int (*pfxcomp)(const char *str, const char *pfx);
|
||||
int (*entrycomp)(const void *a, const void *b);
|
||||
|
||||
void (*free_fn)(git_diff *diff);
|
||||
};
|
||||
|
||||
extern void git_diff__cleanup_modes(
|
||||
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
|
||||
|
||||
extern void git_diff_addref(git_diff *diff);
|
||||
|
||||
extern int git_diff_delta__cmp(const void *a, const void *b);
|
||||
extern int git_diff_delta__casecmp(const void *a, const void *b);
|
||||
|
||||
extern const char *git_diff_delta__path(const git_diff_delta *delta);
|
||||
|
||||
extern bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta);
|
||||
|
||||
extern int git_diff_delta__format_file_header(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta,
|
||||
@ -91,84 +54,11 @@ extern int git_diff_delta__format_file_header(
|
||||
const char *newpfx,
|
||||
int oid_strlen);
|
||||
|
||||
extern int git_diff__oid_for_file(
|
||||
git_oid *out, git_diff *, const char *, uint16_t, git_off_t);
|
||||
extern int git_diff__oid_for_entry(
|
||||
git_oid *out, git_diff *, const git_index_entry *, uint16_t, const git_oid *update);
|
||||
extern int git_diff_delta__cmp(const void *a, const void *b);
|
||||
extern int git_diff_delta__casecmp(const void *a, const void *b);
|
||||
|
||||
extern int git_diff__from_iterators(
|
||||
git_diff **diff_ptr,
|
||||
git_repository *repo,
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter,
|
||||
const git_diff_options *opts);
|
||||
|
||||
extern int git_diff__paired_foreach(
|
||||
git_diff *idx2head,
|
||||
git_diff *wd2idx,
|
||||
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
||||
void *payload);
|
||||
|
||||
extern int git_diff_find_similar__hashsig_for_file(
|
||||
void **out, const git_diff_file *f, const char *path, void *p);
|
||||
|
||||
extern int git_diff_find_similar__hashsig_for_buf(
|
||||
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
|
||||
|
||||
extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
|
||||
|
||||
extern int git_diff_find_similar__calc_similarity(
|
||||
int *score, void *siga, void *sigb, void *payload);
|
||||
|
||||
extern int git_diff__commit(
|
||||
git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
|
||||
|
||||
/* Merge two `git_diff`s according to the callback given by `cb`. */
|
||||
|
||||
typedef git_diff_delta *(*git_diff__merge_cb)(
|
||||
const git_diff_delta *left,
|
||||
const git_diff_delta *right,
|
||||
git_pool *pool);
|
||||
|
||||
extern int git_diff__merge(
|
||||
git_diff *onto, const git_diff *from, git_diff__merge_cb cb);
|
||||
|
||||
extern git_diff_delta *git_diff__merge_like_cgit(
|
||||
const git_diff_delta *a,
|
||||
const git_diff_delta *b,
|
||||
git_pool *pool);
|
||||
|
||||
/* Duplicate a `git_diff_delta` out of the `git_pool` */
|
||||
extern git_diff_delta *git_diff__delta_dup(
|
||||
const git_diff_delta *d, git_pool *pool);
|
||||
|
||||
/*
|
||||
* Sometimes a git_diff_file will have a zero size; this attempts to
|
||||
* fill in the size without loading the blob if possible. If that is
|
||||
* not possible, then it will return the git_odb_object that had to be
|
||||
* loaded and the caller can use it or dispose of it as needed.
|
||||
*/
|
||||
GIT_INLINE(int) git_diff_file__resolve_zero_size(
|
||||
git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
|
||||
{
|
||||
int error;
|
||||
git_odb *odb;
|
||||
size_t len;
|
||||
git_otype type;
|
||||
|
||||
if ((error = git_repository_odb(&odb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
error = git_odb__read_header_or_object(
|
||||
odb_obj, &len, &type, odb, &file->id);
|
||||
|
||||
git_odb_free(odb);
|
||||
|
||||
if (!error)
|
||||
file->size = (git_off_t)len;
|
||||
|
||||
return error;
|
||||
}
|
||||
extern int git_diff__entry_cmp(const void *a, const void *b);
|
||||
extern int git_diff__entry_icmp(const void *a, const void *b);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "git2/attr.h"
|
||||
|
||||
#include "diff.h"
|
||||
#include "diff_patch.h"
|
||||
#include "diff_driver.h"
|
||||
#include "strmap.h"
|
||||
#include "map.h"
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "git2/blob.h"
|
||||
#include "git2/submodule.h"
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
#include "diff_file.h"
|
||||
#include "odb.h"
|
||||
#include "fileops.h"
|
||||
@ -149,12 +150,14 @@ int git_diff_file_content__init_from_src(
|
||||
if (src->blob) {
|
||||
fc->file->size = git_blob_rawsize(src->blob);
|
||||
git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
|
||||
fc->file->id_abbrev = GIT_OID_HEXSZ;
|
||||
|
||||
fc->map.len = (size_t)fc->file->size;
|
||||
fc->map.data = (char *)git_blob_rawcontent(src->blob);
|
||||
} else {
|
||||
fc->file->size = src->buflen;
|
||||
git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);
|
||||
fc->file->id_abbrev = GIT_OID_HEXSZ;
|
||||
|
||||
fc->map.len = src->buflen;
|
||||
fc->map.data = (char *)src->buf;
|
||||
|
1611
src/diff_generate.c
Normal file
1611
src/diff_generate.c
Normal file
File diff suppressed because it is too large
Load Diff
123
src/diff_generate.h
Normal file
123
src/diff_generate.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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_diff_generate_h__
|
||||
#define INCLUDE_diff_generate_h__
|
||||
|
||||
enum {
|
||||
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
|
||||
GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */
|
||||
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
|
||||
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
|
||||
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
|
||||
};
|
||||
|
||||
#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
|
||||
#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
|
||||
|
||||
enum {
|
||||
GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
|
||||
GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
|
||||
GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
|
||||
GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
|
||||
GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
|
||||
GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */
|
||||
|
||||
GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */
|
||||
GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */
|
||||
GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18),
|
||||
GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19),
|
||||
GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20),
|
||||
};
|
||||
|
||||
#define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF)
|
||||
|
||||
#define GIT_DIFF__VERBOSE (1 << 30)
|
||||
|
||||
extern void git_diff_addref(git_diff *diff);
|
||||
|
||||
extern bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta);
|
||||
|
||||
extern int git_diff__from_iterators(
|
||||
git_diff **diff_ptr,
|
||||
git_repository *repo,
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter,
|
||||
const git_diff_options *opts);
|
||||
|
||||
extern int git_diff__commit(
|
||||
git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
|
||||
|
||||
extern int git_diff__paired_foreach(
|
||||
git_diff *idx2head,
|
||||
git_diff *wd2idx,
|
||||
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
||||
void *payload);
|
||||
|
||||
/* Merge two `git_diff`s according to the callback given by `cb`. */
|
||||
|
||||
typedef git_diff_delta *(*git_diff__merge_cb)(
|
||||
const git_diff_delta *left,
|
||||
const git_diff_delta *right,
|
||||
git_pool *pool);
|
||||
|
||||
extern int git_diff__merge(
|
||||
git_diff *onto, const git_diff *from, git_diff__merge_cb cb);
|
||||
|
||||
extern git_diff_delta *git_diff__merge_like_cgit(
|
||||
const git_diff_delta *a,
|
||||
const git_diff_delta *b,
|
||||
git_pool *pool);
|
||||
|
||||
/* Duplicate a `git_diff_delta` out of the `git_pool` */
|
||||
extern git_diff_delta *git_diff__delta_dup(
|
||||
const git_diff_delta *d, git_pool *pool);
|
||||
|
||||
extern int git_diff__oid_for_file(
|
||||
git_oid *out,
|
||||
git_diff *diff,
|
||||
const char *path,
|
||||
uint16_t mode,
|
||||
git_off_t size);
|
||||
|
||||
extern int git_diff__oid_for_entry(
|
||||
git_oid *out,
|
||||
git_diff *diff,
|
||||
const git_index_entry *src,
|
||||
uint16_t mode,
|
||||
const git_oid *update_match);
|
||||
|
||||
/*
|
||||
* Sometimes a git_diff_file will have a zero size; this attempts to
|
||||
* fill in the size without loading the blob if possible. If that is
|
||||
* not possible, then it will return the git_odb_object that had to be
|
||||
* loaded and the caller can use it or dispose of it as needed.
|
||||
*/
|
||||
GIT_INLINE(int) git_diff_file__resolve_zero_size(
|
||||
git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
|
||||
{
|
||||
int error;
|
||||
git_odb *odb;
|
||||
size_t len;
|
||||
git_otype type;
|
||||
|
||||
if ((error = git_repository_odb(&odb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
error = git_odb__read_header_or_object(
|
||||
odb_obj, &len, &type, odb, &file->id);
|
||||
|
||||
git_odb_free(odb);
|
||||
|
||||
if (!error)
|
||||
file->size = (git_off_t)len;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
105
src/diff_parse.c
Normal file
105
src/diff_parse.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
#include "diff.h"
|
||||
#include "patch.h"
|
||||
#include "patch_parse.h"
|
||||
|
||||
typedef struct {
|
||||
struct git_diff base;
|
||||
|
||||
git_vector patches;
|
||||
} git_diff_parsed;
|
||||
|
||||
static void diff_parsed_free(git_diff *d)
|
||||
{
|
||||
git_diff_parsed *diff = (git_diff_parsed *)d;
|
||||
git_patch *patch;
|
||||
size_t i;
|
||||
|
||||
git_vector_foreach(&diff->patches, i, patch)
|
||||
git_patch_free(patch);
|
||||
|
||||
git_vector_free(&diff->patches);
|
||||
|
||||
git_vector_free(&diff->base.deltas);
|
||||
git_pool_clear(&diff->base.pool);
|
||||
|
||||
git__memzero(diff, sizeof(*diff));
|
||||
git__free(diff);
|
||||
}
|
||||
|
||||
static git_diff_parsed *diff_parsed_alloc(void)
|
||||
{
|
||||
git_diff_parsed *diff;
|
||||
|
||||
if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL)
|
||||
return NULL;
|
||||
|
||||
GIT_REFCOUNT_INC(diff);
|
||||
diff->base.type = GIT_DIFF_TYPE_PARSED;
|
||||
diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
|
||||
diff->base.strcomp = git__strcmp;
|
||||
diff->base.strncomp = git__strncmp;
|
||||
diff->base.pfxcomp = git__prefixcmp;
|
||||
diff->base.entrycomp = git_diff__entry_cmp;
|
||||
diff->base.free_fn = diff_parsed_free;
|
||||
|
||||
git_pool_init(&diff->base.pool, 1);
|
||||
|
||||
if (git_vector_init(&diff->patches, 0, NULL) < 0 ||
|
||||
git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
|
||||
git_diff_free(&diff->base);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
git_vector_set_cmp(&diff->base.deltas, git_diff_delta__cmp);
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
int git_diff_from_buffer(
|
||||
git_diff **out,
|
||||
const char *content,
|
||||
size_t content_len)
|
||||
{
|
||||
git_diff_parsed *diff;
|
||||
git_patch *patch;
|
||||
git_patch_parse_ctx *ctx = NULL;
|
||||
int error = 0;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
diff = diff_parsed_alloc();
|
||||
GITERR_CHECK_ALLOC(diff);
|
||||
|
||||
ctx = git_patch_parse_ctx_init(content, content_len, NULL);
|
||||
GITERR_CHECK_ALLOC(ctx);
|
||||
|
||||
while (ctx->remain_len) {
|
||||
if ((error = git_patch_parse(&patch, ctx)) < 0)
|
||||
break;
|
||||
|
||||
git_vector_insert(&diff->patches, patch);
|
||||
git_vector_insert(&diff->base.deltas, patch->delta);
|
||||
}
|
||||
|
||||
if (error == GIT_ENOTFOUND && git_vector_length(&diff->patches) > 0) {
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
}
|
||||
|
||||
git_patch_parse_ctx_free(ctx);
|
||||
|
||||
if (error < 0)
|
||||
git_diff_free(&diff->base);
|
||||
else
|
||||
*out = &diff->base;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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_diff_patch_h__
|
||||
#define INCLUDE_diff_patch_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "diff.h"
|
||||
#include "diff_file.h"
|
||||
#include "array.h"
|
||||
#include "git2/patch.h"
|
||||
|
||||
/* cached information about a hunk in a diff */
|
||||
typedef struct diff_patch_hunk {
|
||||
git_diff_hunk hunk;
|
||||
size_t line_start;
|
||||
size_t line_count;
|
||||
} diff_patch_hunk;
|
||||
|
||||
enum {
|
||||
GIT_DIFF_PATCH_ALLOCATED = (1 << 0),
|
||||
GIT_DIFF_PATCH_INITIALIZED = (1 << 1),
|
||||
GIT_DIFF_PATCH_LOADED = (1 << 2),
|
||||
/* the two sides are different */
|
||||
GIT_DIFF_PATCH_DIFFABLE = (1 << 3),
|
||||
/* the difference between the two sides has been computed */
|
||||
GIT_DIFF_PATCH_DIFFED = (1 << 4),
|
||||
GIT_DIFF_PATCH_FLATTENED = (1 << 5),
|
||||
};
|
||||
|
||||
struct git_patch {
|
||||
git_refcount rc;
|
||||
git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
|
||||
git_diff_options diff_opts;
|
||||
git_diff_delta *delta;
|
||||
size_t delta_index;
|
||||
git_diff_file_content ofile;
|
||||
git_diff_file_content nfile;
|
||||
uint32_t flags;
|
||||
git_diff_binary binary;
|
||||
git_array_t(diff_patch_hunk) hunks;
|
||||
git_array_t(git_diff_line) lines;
|
||||
size_t content_size, context_size, header_size;
|
||||
git_pool flattened;
|
||||
};
|
||||
|
||||
extern git_diff *git_patch__diff(git_patch *);
|
||||
|
||||
extern git_diff_driver *git_patch__driver(git_patch *);
|
||||
|
||||
extern void git_patch__old_data(char **, size_t *, git_patch *);
|
||||
extern void git_patch__new_data(char **, size_t *, git_patch *);
|
||||
|
||||
extern int git_patch__invoke_callbacks(
|
||||
git_patch *patch,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_binary_cb binary_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_line_cb line_cb,
|
||||
void *payload);
|
||||
|
||||
typedef struct git_diff_output git_diff_output;
|
||||
struct git_diff_output {
|
||||
/* these callbacks are issued with the diff data */
|
||||
git_diff_file_cb file_cb;
|
||||
git_diff_binary_cb binary_cb;
|
||||
git_diff_hunk_cb hunk_cb;
|
||||
git_diff_line_cb data_cb;
|
||||
void *payload;
|
||||
|
||||
/* this records the actual error in cases where it may be obscured */
|
||||
int error;
|
||||
|
||||
/* this callback is used to do the diff and drive the other callbacks.
|
||||
* see diff_xdiff.h for how to use this in practice for now.
|
||||
*/
|
||||
int (*diff_cb)(git_diff_output *output, git_patch *patch);
|
||||
};
|
||||
|
||||
#endif
|
401
src/diff_print.c
401
src/diff_print.c
@ -6,7 +6,8 @@
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "diff.h"
|
||||
#include "diff_patch.h"
|
||||
#include "diff_file.h"
|
||||
#include "patch_generate.h"
|
||||
#include "fileops.h"
|
||||
#include "zstream.h"
|
||||
#include "blob.h"
|
||||
@ -14,19 +15,19 @@
|
||||
#include "git2/sys/diff.h"
|
||||
|
||||
typedef struct {
|
||||
git_diff *diff;
|
||||
git_diff_format_t format;
|
||||
git_diff_line_cb print_cb;
|
||||
void *payload;
|
||||
|
||||
git_buf *buf;
|
||||
uint32_t flags;
|
||||
int oid_strlen;
|
||||
git_diff_line line;
|
||||
unsigned int
|
||||
content_loaded : 1,
|
||||
content_allocated : 1;
|
||||
git_diff_file_content *ofile;
|
||||
git_diff_file_content *nfile;
|
||||
|
||||
const char *old_prefix;
|
||||
const char *new_prefix;
|
||||
uint32_t flags;
|
||||
int id_strlen;
|
||||
|
||||
int (*strcomp)(const char *, const char *);
|
||||
} diff_print_info;
|
||||
|
||||
static int diff_print_info_init__common(
|
||||
@ -42,17 +43,15 @@ static int diff_print_info_init__common(
|
||||
pi->payload = payload;
|
||||
pi->buf = out;
|
||||
|
||||
if (!pi->oid_strlen) {
|
||||
if (!pi->id_strlen) {
|
||||
if (!repo)
|
||||
pi->oid_strlen = GIT_ABBREV_DEFAULT;
|
||||
else if (git_repository__cvar(&pi->oid_strlen, repo, GIT_CVAR_ABBREV) < 0)
|
||||
pi->id_strlen = GIT_ABBREV_DEFAULT;
|
||||
else if (git_repository__cvar(&pi->id_strlen, repo, GIT_CVAR_ABBREV) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
pi->oid_strlen += 1; /* for NUL byte */
|
||||
|
||||
if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
|
||||
pi->oid_strlen = GIT_OID_HEXSZ + 1;
|
||||
if (pi->id_strlen > GIT_OID_HEXSZ)
|
||||
pi->id_strlen = GIT_OID_HEXSZ;
|
||||
|
||||
memset(&pi->line, 0, sizeof(pi->line));
|
||||
pi->line.old_lineno = -1;
|
||||
@ -74,11 +73,13 @@ static int diff_print_info_init_fromdiff(
|
||||
|
||||
memset(pi, 0, sizeof(diff_print_info));
|
||||
|
||||
pi->diff = diff;
|
||||
|
||||
if (diff) {
|
||||
pi->flags = diff->opts.flags;
|
||||
pi->oid_strlen = diff->opts.id_abbrev;
|
||||
pi->id_strlen = diff->opts.id_abbrev;
|
||||
pi->old_prefix = diff->opts.old_prefix;
|
||||
pi->new_prefix = diff->opts.new_prefix;
|
||||
|
||||
pi->strcomp = diff->strcomp;
|
||||
}
|
||||
|
||||
return diff_print_info_init__common(pi, out, repo, format, cb, payload);
|
||||
@ -92,24 +93,16 @@ static int diff_print_info_init_frompatch(
|
||||
git_diff_line_cb cb,
|
||||
void *payload)
|
||||
{
|
||||
git_repository *repo;
|
||||
|
||||
assert(patch);
|
||||
|
||||
repo = patch->diff ? patch->diff->repo : NULL;
|
||||
|
||||
memset(pi, 0, sizeof(diff_print_info));
|
||||
|
||||
pi->diff = patch->diff;
|
||||
|
||||
pi->flags = patch->diff_opts.flags;
|
||||
pi->oid_strlen = patch->diff_opts.id_abbrev;
|
||||
pi->id_strlen = patch->diff_opts.id_abbrev;
|
||||
pi->old_prefix = patch->diff_opts.old_prefix;
|
||||
pi->new_prefix = patch->diff_opts.new_prefix;
|
||||
|
||||
pi->content_loaded = 1;
|
||||
pi->ofile = &patch->ofile;
|
||||
pi->nfile = &patch->nfile;
|
||||
|
||||
return diff_print_info_init__common(pi, out, repo, format, cb, payload);
|
||||
return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload);
|
||||
}
|
||||
|
||||
static char diff_pick_suffix(int mode)
|
||||
@ -173,8 +166,8 @@ static int diff_print_one_name_status(
|
||||
diff_print_info *pi = data;
|
||||
git_buf *out = pi->buf;
|
||||
char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
|
||||
int (*strcomp)(const char *, const char *) =
|
||||
pi->diff ? pi->diff->strcomp : git__strcmp;
|
||||
int(*strcomp)(const char *, const char *) = pi->strcomp ?
|
||||
pi->strcomp : git__strcmp;
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
|
||||
@ -213,6 +206,7 @@ static int diff_print_one_raw(
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
git_buf *out = pi->buf;
|
||||
int id_abbrev;
|
||||
char code = git_diff_status_char(delta->status);
|
||||
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||
|
||||
@ -223,11 +217,21 @@ static int diff_print_one_raw(
|
||||
|
||||
git_buf_clear(out);
|
||||
|
||||
git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id);
|
||||
git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id);
|
||||
id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
|
||||
delta->new_file.id_abbrev;
|
||||
|
||||
if (pi->id_strlen > id_abbrev) {
|
||||
giterr_set(GITERR_PATCH,
|
||||
"The patch input contains %d id characters (cannot print %d)",
|
||||
id_abbrev, pi->id_strlen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id);
|
||||
git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id);
|
||||
|
||||
git_buf_printf(
|
||||
out, (pi->oid_strlen <= GIT_OID_HEXSZ) ?
|
||||
out, (pi->id_strlen <= GIT_OID_HEXSZ) ?
|
||||
":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
|
||||
delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
|
||||
|
||||
@ -252,53 +256,140 @@ static int diff_print_one_raw(
|
||||
return pi->print_cb(delta, NULL, &pi->line, pi->payload);
|
||||
}
|
||||
|
||||
static int diff_print_modes(
|
||||
git_buf *out, const git_diff_delta *delta)
|
||||
{
|
||||
git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
|
||||
git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
|
||||
|
||||
return git_buf_oom(out) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int diff_print_oid_range(
|
||||
git_buf *out, const git_diff_delta *delta, int oid_strlen)
|
||||
git_buf *out, const git_diff_delta *delta, int id_strlen)
|
||||
{
|
||||
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||
|
||||
git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id);
|
||||
git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id);
|
||||
if (delta->old_file.mode &&
|
||||
id_strlen > delta->old_file.id_abbrev) {
|
||||
giterr_set(GITERR_PATCH,
|
||||
"The patch input contains %d id characters (cannot print %d)",
|
||||
delta->old_file.id_abbrev, id_strlen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((delta->new_file.mode &&
|
||||
id_strlen > delta->new_file.id_abbrev)) {
|
||||
giterr_set(GITERR_PATCH,
|
||||
"The patch input contains %d id characters (cannot print %d)",
|
||||
delta->new_file.id_abbrev, id_strlen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id);
|
||||
git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id);
|
||||
|
||||
/* TODO: Match git diff more closely */
|
||||
if (delta->old_file.mode == delta->new_file.mode) {
|
||||
git_buf_printf(out, "index %s..%s %o\n",
|
||||
start_oid, end_oid, delta->old_file.mode);
|
||||
} else {
|
||||
if (delta->old_file.mode == 0) {
|
||||
if (delta->old_file.mode == 0)
|
||||
git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
|
||||
} else if (delta->new_file.mode == 0) {
|
||||
else if (delta->new_file.mode == 0)
|
||||
git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
|
||||
} else {
|
||||
git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
|
||||
git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
|
||||
}
|
||||
else
|
||||
diff_print_modes(out, delta);
|
||||
|
||||
git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
|
||||
}
|
||||
|
||||
return git_buf_oom(out) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int diff_delta_format_path(
|
||||
git_buf *out, const char *prefix, const char *filename)
|
||||
{
|
||||
if (git_buf_joinpath(out, prefix, filename) < 0)
|
||||
return -1;
|
||||
|
||||
return git_buf_quote(out);
|
||||
}
|
||||
|
||||
static int diff_delta_format_with_paths(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta,
|
||||
const char *oldpfx,
|
||||
const char *newpfx,
|
||||
const char *template)
|
||||
const char *template,
|
||||
const char *oldpath,
|
||||
const char *newpath)
|
||||
{
|
||||
const char *oldpath = delta->old_file.path;
|
||||
const char *newpath = delta->new_file.path;
|
||||
|
||||
if (git_oid_iszero(&delta->old_file.id)) {
|
||||
oldpfx = "";
|
||||
if (git_oid_iszero(&delta->old_file.id))
|
||||
oldpath = "/dev/null";
|
||||
}
|
||||
if (git_oid_iszero(&delta->new_file.id)) {
|
||||
newpfx = "";
|
||||
|
||||
if (git_oid_iszero(&delta->new_file.id))
|
||||
newpath = "/dev/null";
|
||||
|
||||
return git_buf_printf(out, template, oldpath, newpath);
|
||||
}
|
||||
|
||||
int diff_delta_format_similarity_header(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta)
|
||||
{
|
||||
git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
|
||||
const char *type;
|
||||
int error = 0;
|
||||
|
||||
if (delta->similarity > 100) {
|
||||
giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity);
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
|
||||
if (delta->status == GIT_DELTA_RENAMED)
|
||||
type = "rename";
|
||||
else if (delta->status == GIT_DELTA_COPIED)
|
||||
type = "copy";
|
||||
else
|
||||
abort();
|
||||
|
||||
if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 ||
|
||||
(error = git_buf_puts(&new_path, delta->new_file.path)) < 0 ||
|
||||
(error = git_buf_quote(&old_path)) < 0 ||
|
||||
(error = git_buf_quote(&new_path)) < 0)
|
||||
goto done;
|
||||
|
||||
git_buf_printf(out,
|
||||
"similarity index %d%%\n"
|
||||
"%s from %s\n"
|
||||
"%s to %s\n",
|
||||
delta->similarity,
|
||||
type, old_path.ptr,
|
||||
type, new_path.ptr);
|
||||
|
||||
if (git_buf_oom(out))
|
||||
error = -1;
|
||||
|
||||
done:
|
||||
git_buf_free(&old_path);
|
||||
git_buf_free(&new_path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static bool delta_is_unchanged(const git_diff_delta *delta)
|
||||
{
|
||||
if (git_oid_iszero(&delta->old_file.id) &&
|
||||
git_oid_iszero(&delta->new_file.id))
|
||||
return true;
|
||||
|
||||
if (delta->old_file.mode == GIT_FILEMODE_COMMIT ||
|
||||
delta->new_file.mode == GIT_FILEMODE_COMMIT)
|
||||
return false;
|
||||
|
||||
if (git_oid_equal(&delta->old_file.id, &delta->new_file.id))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int git_diff_delta__format_file_header(
|
||||
@ -306,27 +397,56 @@ int git_diff_delta__format_file_header(
|
||||
const git_diff_delta *delta,
|
||||
const char *oldpfx,
|
||||
const char *newpfx,
|
||||
int oid_strlen)
|
||||
int id_strlen)
|
||||
{
|
||||
git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
|
||||
bool unchanged = delta_is_unchanged(delta);
|
||||
int error = 0;
|
||||
|
||||
if (!oldpfx)
|
||||
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
|
||||
if (!newpfx)
|
||||
newpfx = DIFF_NEW_PREFIX_DEFAULT;
|
||||
if (!oid_strlen)
|
||||
oid_strlen = GIT_ABBREV_DEFAULT + 1;
|
||||
if (!id_strlen)
|
||||
id_strlen = GIT_ABBREV_DEFAULT;
|
||||
|
||||
if ((error = diff_delta_format_path(
|
||||
&old_path, oldpfx, delta->old_file.path)) < 0 ||
|
||||
(error = diff_delta_format_path(
|
||||
&new_path, newpfx, delta->new_file.path)) < 0)
|
||||
goto done;
|
||||
|
||||
git_buf_clear(out);
|
||||
|
||||
git_buf_printf(out, "diff --git %s%s %s%s\n",
|
||||
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
|
||||
git_buf_printf(out, "diff --git %s %s\n",
|
||||
old_path.ptr, new_path.ptr);
|
||||
|
||||
GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen));
|
||||
if (delta->status == GIT_DELTA_RENAMED ||
|
||||
(delta->status == GIT_DELTA_COPIED && unchanged)) {
|
||||
if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
|
||||
diff_delta_format_with_paths(
|
||||
out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
|
||||
if (!unchanged) {
|
||||
if ((error = diff_print_oid_range(out, delta, id_strlen)) < 0)
|
||||
goto done;
|
||||
|
||||
return git_buf_oom(out) ? -1 : 0;
|
||||
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
|
||||
diff_delta_format_with_paths(out, delta,
|
||||
"--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
|
||||
}
|
||||
|
||||
if (unchanged && delta->old_file.mode != delta->new_file.mode)
|
||||
diff_print_modes(out, delta);
|
||||
|
||||
if (git_buf_oom(out))
|
||||
error = -1;
|
||||
|
||||
done:
|
||||
git_buf_free(&old_path);
|
||||
git_buf_free(&new_path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int format_binary(
|
||||
@ -367,37 +487,30 @@ static int format_binary(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_print_load_content(
|
||||
diff_print_info *pi,
|
||||
git_diff_delta *delta)
|
||||
static int diff_print_patch_file_binary_noshow(
|
||||
diff_print_info *pi, git_diff_delta *delta,
|
||||
const char *old_pfx, const char *new_pfx)
|
||||
{
|
||||
git_diff_file_content *ofile, *nfile;
|
||||
git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
assert(pi->diff);
|
||||
if ((error = diff_delta_format_path(
|
||||
&old_path, old_pfx, delta->old_file.path)) < 0 ||
|
||||
(error = diff_delta_format_path(
|
||||
&new_path, new_pfx, delta->new_file.path)) < 0)
|
||||
goto done;
|
||||
|
||||
ofile = git__calloc(1, sizeof(git_diff_file_content));
|
||||
nfile = git__calloc(1, sizeof(git_diff_file_content));
|
||||
|
||||
GITERR_CHECK_ALLOC(ofile);
|
||||
GITERR_CHECK_ALLOC(nfile);
|
||||
pi->line.num_lines = 1;
|
||||
error = diff_delta_format_with_paths(
|
||||
pi->buf, delta, "Binary files %s and %s differ\n",
|
||||
old_path.ptr, new_path.ptr);
|
||||
|
||||
if ((error = git_diff_file_content__init_from_diff(
|
||||
ofile, pi->diff, delta, true)) < 0 ||
|
||||
(error = git_diff_file_content__init_from_diff(
|
||||
nfile, pi->diff, delta, true)) < 0) {
|
||||
done:
|
||||
git_buf_free(&old_path);
|
||||
git_buf_free(&new_path);
|
||||
|
||||
git__free(ofile);
|
||||
git__free(nfile);
|
||||
return error;
|
||||
}
|
||||
|
||||
pi->content_loaded = 1;
|
||||
pi->content_allocated = 1;
|
||||
pi->ofile = ofile;
|
||||
pi->nfile = nfile;
|
||||
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_print_patch_file_binary(
|
||||
@ -409,11 +522,11 @@ static int diff_print_patch_file_binary(
|
||||
int error;
|
||||
|
||||
if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0)
|
||||
goto noshow;
|
||||
return diff_print_patch_file_binary_noshow(
|
||||
pi, delta, old_pfx, new_pfx);
|
||||
|
||||
if (!pi->content_loaded &&
|
||||
(error = diff_print_load_content(pi, delta)) < 0)
|
||||
return error;
|
||||
if (binary->new_file.datalen == 0 && binary->old_file.datalen == 0)
|
||||
return 0;
|
||||
|
||||
pre_binary_size = pi->buf->size;
|
||||
git_buf_printf(pi->buf, "GIT binary patch\n");
|
||||
@ -427,18 +540,14 @@ static int diff_print_patch_file_binary(
|
||||
if (error == GIT_EBUFS) {
|
||||
giterr_clear();
|
||||
git_buf_truncate(pi->buf, pre_binary_size);
|
||||
goto noshow;
|
||||
|
||||
return diff_print_patch_file_binary_noshow(
|
||||
pi, delta, old_pfx, new_pfx);
|
||||
}
|
||||
}
|
||||
|
||||
pi->line.num_lines++;
|
||||
return error;
|
||||
|
||||
noshow:
|
||||
pi->line.num_lines = 1;
|
||||
return diff_delta_format_with_paths(
|
||||
pi->buf, delta, old_pfx, new_pfx,
|
||||
"Binary files %s%s and %s%s differ\n");
|
||||
}
|
||||
|
||||
static int diff_print_patch_file(
|
||||
@ -447,15 +556,15 @@ static int diff_print_patch_file(
|
||||
int error;
|
||||
diff_print_info *pi = data;
|
||||
const char *oldpfx =
|
||||
pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
|
||||
pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
|
||||
const char *newpfx =
|
||||
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
|
||||
pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
|
||||
|
||||
bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
|
||||
(pi->flags & GIT_DIFF_FORCE_BINARY);
|
||||
bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
|
||||
int oid_strlen = binary && show_binary ?
|
||||
GIT_OID_HEXSZ + 1 : pi->oid_strlen;
|
||||
int id_strlen = binary && show_binary ?
|
||||
GIT_OID_HEXSZ : pi->id_strlen;
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
|
||||
@ -468,7 +577,7 @@ static int diff_print_patch_file(
|
||||
return 0;
|
||||
|
||||
if ((error = git_diff_delta__format_file_header(
|
||||
pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0)
|
||||
pi->buf, delta, oldpfx, newpfx, id_strlen)) < 0)
|
||||
return error;
|
||||
|
||||
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
|
||||
@ -485,9 +594,9 @@ static int diff_print_patch_binary(
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
const char *old_pfx =
|
||||
pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
|
||||
pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
|
||||
const char *new_pfx =
|
||||
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
|
||||
pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
|
||||
int error;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
@ -582,43 +691,11 @@ int git_diff_print(
|
||||
giterr_set_after_callback_function(error, "git_diff_print");
|
||||
}
|
||||
|
||||
git__free(pi.nfile);
|
||||
git__free(pi.ofile);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* print a git_patch to an output callback */
|
||||
int git_patch_print(
|
||||
git_patch *patch,
|
||||
git_diff_line_cb print_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error;
|
||||
git_buf temp = GIT_BUF_INIT;
|
||||
diff_print_info pi;
|
||||
|
||||
assert(patch && print_cb);
|
||||
|
||||
if (!(error = diff_print_info_init_frompatch(
|
||||
&pi, &temp, patch,
|
||||
GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
|
||||
{
|
||||
error = git_patch__invoke_callbacks(
|
||||
patch, diff_print_patch_file, diff_print_patch_binary,
|
||||
diff_print_patch_hunk, diff_print_patch_line, &pi);
|
||||
|
||||
if (error) /* make sure error message is set */
|
||||
giterr_set_after_callback_function(error, "git_patch_print");
|
||||
}
|
||||
|
||||
git_buf_free(&temp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_diff_print_callback__to_buf(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_hunk *hunk,
|
||||
@ -659,6 +736,46 @@ int git_diff_print_callback__to_file_handle(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* print a git_diff to a git_buf */
|
||||
int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
|
||||
{
|
||||
assert(out && diff);
|
||||
git_buf_sanitize(out);
|
||||
return git_diff_print(
|
||||
diff, format, git_diff_print_callback__to_buf, out);
|
||||
}
|
||||
|
||||
/* print a git_patch to an output callback */
|
||||
int git_patch_print(
|
||||
git_patch *patch,
|
||||
git_diff_line_cb print_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error;
|
||||
git_buf temp = GIT_BUF_INIT;
|
||||
diff_print_info pi;
|
||||
|
||||
assert(patch && print_cb);
|
||||
|
||||
if (!(error = diff_print_info_init_frompatch(
|
||||
&pi, &temp, patch,
|
||||
GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
|
||||
{
|
||||
error = git_patch__invoke_callbacks(
|
||||
patch,
|
||||
diff_print_patch_file, diff_print_patch_binary,
|
||||
diff_print_patch_hunk, diff_print_patch_line,
|
||||
&pi);
|
||||
|
||||
if (error) /* make sure error message is set */
|
||||
giterr_set_after_callback_function(error, "git_patch_print");
|
||||
}
|
||||
|
||||
git_buf_free(&temp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* print a git_patch to a git_buf */
|
||||
int git_patch_to_buf(git_buf *out, git_patch *patch)
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "common.h"
|
||||
#include "vector.h"
|
||||
#include "diff.h"
|
||||
#include "diff_patch.h"
|
||||
#include "patch_generate.h"
|
||||
|
||||
#define DIFF_RENAME_FILE_SEPARATOR " => "
|
||||
#define STATS_FULL_MIN_SCALE 7
|
||||
@ -190,8 +190,9 @@ int git_diff_get_stats(
|
||||
break;
|
||||
|
||||
/* keep a count of renames because it will affect formatting */
|
||||
delta = git_patch_get_delta(patch);
|
||||
delta = patch->delta;
|
||||
|
||||
/* TODO ugh */
|
||||
namelen = strlen(delta->new_file.path);
|
||||
if (strcmp(delta->old_file.path, delta->new_file.path) != 0) {
|
||||
namelen += strlen(delta->old_file.path);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "git2/sys/hashsig.h"
|
||||
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
#include "path.h"
|
||||
#include "fileops.h"
|
||||
#include "config.h"
|
||||
|
22
src/diff_tform.h
Normal file
22
src/diff_tform.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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_diff_tform_h__
|
||||
#define INCLUDE_diff_tform_h__
|
||||
|
||||
extern int git_diff_find_similar__hashsig_for_file(
|
||||
void **out, const git_diff_file *f, const char *path, void *p);
|
||||
|
||||
extern int git_diff_find_similar__hashsig_for_buf(
|
||||
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
|
||||
|
||||
extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
|
||||
|
||||
extern int git_diff_find_similar__calc_similarity(
|
||||
int *score, void *siga, void *sigb, void *payload);
|
||||
|
||||
#endif
|
||||
|
@ -8,8 +8,8 @@
|
||||
#include "common.h"
|
||||
#include "diff.h"
|
||||
#include "diff_driver.h"
|
||||
#include "diff_patch.h"
|
||||
#include "diff_xdiff.h"
|
||||
#include "patch_generate.h"
|
||||
|
||||
static int git_xdiff_scan_int(const char **str, int *value)
|
||||
{
|
||||
@ -56,7 +56,7 @@ fail:
|
||||
|
||||
typedef struct {
|
||||
git_xdiff_output *xo;
|
||||
git_patch *patch;
|
||||
git_patch_generated *patch;
|
||||
git_diff_hunk hunk;
|
||||
int old_lineno, new_lineno;
|
||||
mmfile_t xd_old_data, xd_new_data;
|
||||
@ -110,9 +110,9 @@ static int diff_update_lines(
|
||||
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
|
||||
{
|
||||
git_xdiff_info *info = priv;
|
||||
git_patch *patch = info->patch;
|
||||
const git_diff_delta *delta = git_patch_get_delta(patch);
|
||||
git_diff_output *output = &info->xo->output;
|
||||
git_patch_generated *patch = info->patch;
|
||||
const git_diff_delta *delta = patch->base.delta;
|
||||
git_patch_generated_output *output = &info->xo->output;
|
||||
git_diff_line line;
|
||||
|
||||
if (len == 1) {
|
||||
@ -181,7 +181,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
|
||||
return output->error;
|
||||
}
|
||||
|
||||
static int git_xdiff(git_diff_output *output, git_patch *patch)
|
||||
static int git_xdiff(git_patch_generated_output *output, git_patch_generated *patch)
|
||||
{
|
||||
git_xdiff_output *xo = (git_xdiff_output *)output;
|
||||
git_xdiff_info info;
|
||||
@ -194,7 +194,7 @@ static int git_xdiff(git_diff_output *output, git_patch *patch)
|
||||
xo->callback.priv = &info;
|
||||
|
||||
git_diff_find_context_init(
|
||||
&xo->config.find_func, &findctxt, git_patch__driver(patch));
|
||||
&xo->config.find_func, &findctxt, git_patch_generated_driver(patch));
|
||||
xo->config.find_func_priv = &findctxt;
|
||||
|
||||
if (xo->config.find_func != NULL)
|
||||
@ -206,8 +206,8 @@ static int git_xdiff(git_diff_output *output, git_patch *patch)
|
||||
* updates are needed to xo->params.flags
|
||||
*/
|
||||
|
||||
git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
|
||||
git_patch__new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
|
||||
git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
|
||||
git_patch_generated_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
|
||||
|
||||
if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE ||
|
||||
info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) {
|
||||
|
@ -8,20 +8,20 @@
|
||||
#define INCLUDE_diff_xdiff_h__
|
||||
|
||||
#include "diff.h"
|
||||
#include "diff_patch.h"
|
||||
#include "xdiff/xdiff.h"
|
||||
#include "patch_generate.h"
|
||||
|
||||
/* xdiff cannot cope with large files. these files should not be passed to
|
||||
* xdiff. callers should treat these large files as binary.
|
||||
*/
|
||||
#define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023)
|
||||
|
||||
/* A git_xdiff_output is a git_diff_output with extra fields necessary
|
||||
* to use libxdiff. Calling git_xdiff_init() will set the diff_cb field
|
||||
* of the output to use xdiff to generate the diffs.
|
||||
/* A git_xdiff_output is a git_patch_generate_output with extra fields
|
||||
* necessary to use libxdiff. Calling git_xdiff_init() will set the diff_cb
|
||||
* field of the output to use xdiff to generate the diffs.
|
||||
*/
|
||||
typedef struct {
|
||||
git_diff_output output;
|
||||
git_patch_generated_output output;
|
||||
|
||||
xdemitconf_t config;
|
||||
xpparam_t params;
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "iterator.h"
|
||||
#include "refs.h"
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
#include "diff_tform.h"
|
||||
#include "checkout.h"
|
||||
#include "tree.h"
|
||||
#include "blob.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "odb.h"
|
||||
#include "delta-apply.h"
|
||||
#include "delta.h"
|
||||
#include "filter.h"
|
||||
#include "repository.h"
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "odb.h"
|
||||
#include "delta-apply.h"
|
||||
#include "delta.h"
|
||||
#include "filebuf.h"
|
||||
|
||||
#include "git2/odb_backend.h"
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "odb.h"
|
||||
#include "delta-apply.h"
|
||||
#include "delta.h"
|
||||
#include "sha1_lookup.h"
|
||||
#include "mwindow.h"
|
||||
#include "pack.h"
|
||||
|
@ -144,7 +144,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
|
||||
pb->nr_threads = 1; /* do not spawn any thread by default */
|
||||
|
||||
if (git_hash_ctx_init(&pb->ctx) < 0 ||
|
||||
git_zstream_init(&pb->zstream) < 0 ||
|
||||
git_zstream_init(&pb->zstream, GIT_ZSTREAM_DEFLATE) < 0 ||
|
||||
git_repository_odb(&pb->odb, repo) < 0 ||
|
||||
packbuilder_config(pb) < 0)
|
||||
goto on_error;
|
||||
@ -274,6 +274,7 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po)
|
||||
git_odb_object *src = NULL, *trg = NULL;
|
||||
unsigned long delta_size;
|
||||
void *delta_buf;
|
||||
int error;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
@ -281,12 +282,15 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po)
|
||||
git_odb_read(&trg, odb, &po->id) < 0)
|
||||
goto on_error;
|
||||
|
||||
delta_buf = git_delta(
|
||||
git_odb_object_data(src), (unsigned long)git_odb_object_size(src),
|
||||
git_odb_object_data(trg), (unsigned long)git_odb_object_size(trg),
|
||||
&delta_size, 0);
|
||||
error = git_delta(&delta_buf, &delta_size,
|
||||
git_odb_object_data(src), git_odb_object_size(src),
|
||||
git_odb_object_data(trg), git_odb_object_size(trg),
|
||||
0);
|
||||
|
||||
if (!delta_buf || delta_size != po->delta_size) {
|
||||
if (error < 0 && error != GIT_EBUFS)
|
||||
goto on_error;
|
||||
|
||||
if (error == GIT_EBUFS || delta_size != po->delta_size) {
|
||||
giterr_set(GITERR_INVALID, "Delta size changed");
|
||||
goto on_error;
|
||||
}
|
||||
@ -815,16 +819,14 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg,
|
||||
*mem_usage += sz;
|
||||
}
|
||||
if (!src->index) {
|
||||
src->index = git_delta_create_index(src->data, src_size);
|
||||
if (!src->index)
|
||||
if (git_delta_index_init(&src->index, src->data, src_size) < 0)
|
||||
return 0; /* suboptimal pack - out of memory */
|
||||
|
||||
*mem_usage += git_delta_sizeof_index(src->index);
|
||||
*mem_usage += git_delta_index_size(src->index);
|
||||
}
|
||||
|
||||
delta_buf = git_delta_create(src->index, trg->data, trg_size,
|
||||
&delta_size, max_size);
|
||||
if (!delta_buf)
|
||||
if (git_delta_create_from_index(&delta_buf, &delta_size, src->index, trg->data, trg_size,
|
||||
max_size) < 0)
|
||||
return 0;
|
||||
|
||||
if (trg_object->delta) {
|
||||
@ -885,9 +887,14 @@ static unsigned int check_delta_limit(git_pobject *me, unsigned int n)
|
||||
|
||||
static unsigned long free_unpacked(struct unpacked *n)
|
||||
{
|
||||
unsigned long freed_mem = git_delta_sizeof_index(n->index);
|
||||
git_delta_free_index(n->index);
|
||||
unsigned long freed_mem = 0;
|
||||
|
||||
if (n->index) {
|
||||
freed_mem += git_delta_index_size(n->index);
|
||||
git_delta_index_free(n->index);
|
||||
}
|
||||
n->index = NULL;
|
||||
|
||||
if (n->data) {
|
||||
freed_mem += (unsigned long)n->object->size;
|
||||
git__free(n->data);
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "common.h"
|
||||
#include "odb.h"
|
||||
#include "pack.h"
|
||||
#include "delta-apply.h"
|
||||
#include "delta.h"
|
||||
#include "sha1_lookup.h"
|
||||
#include "mwindow.h"
|
||||
#include "fileops.h"
|
||||
@ -505,7 +505,7 @@ int git_packfile_resolve_header(
|
||||
git_mwindow_close(&w_curs);
|
||||
if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0)
|
||||
return error;
|
||||
error = git__delta_read_header_fromstream(&base_size, size_p, &stream);
|
||||
error = git_delta_read_header_fromstream(&base_size, size_p, &stream);
|
||||
git_packfile_stream_free(&stream);
|
||||
if (error < 0)
|
||||
return error;
|
||||
@ -730,8 +730,9 @@ int git_packfile_unpack(
|
||||
obj->len = 0;
|
||||
obj->type = GIT_OBJ_BAD;
|
||||
|
||||
error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
|
||||
error = git_delta_apply(&obj->data, &obj->len, base.data, base.len, delta.data, delta.len);
|
||||
obj->type = base_type;
|
||||
|
||||
/*
|
||||
* We usually don't want to free the base at this
|
||||
* point, as we put it into the cache in the previous
|
||||
|
207
src/patch.c
Normal file
207
src/patch.c
Normal file
@ -0,0 +1,207 @@
|
||||
#include "git2/patch.h"
|
||||
#include "diff.h"
|
||||
#include "patch.h"
|
||||
|
||||
|
||||
int git_patch__invoke_callbacks(
|
||||
git_patch *patch,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_binary_cb binary_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_line_cb line_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error = 0;
|
||||
uint32_t i, j;
|
||||
|
||||
if (file_cb)
|
||||
error = file_cb(patch->delta, 0, payload);
|
||||
|
||||
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
|
||||
if (binary_cb)
|
||||
error = binary_cb(patch->delta, &patch->binary, payload);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!hunk_cb && !line_cb)
|
||||
return error;
|
||||
|
||||
for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
|
||||
git_patch_hunk *h = git_array_get(patch->hunks, i);
|
||||
|
||||
if (hunk_cb)
|
||||
error = hunk_cb(patch->delta, &h->hunk, payload);
|
||||
|
||||
if (!line_cb)
|
||||
continue;
|
||||
|
||||
for (j = 0; !error && j < h->line_count; ++j) {
|
||||
git_diff_line *l =
|
||||
git_array_get(patch->lines, h->line_start + j);
|
||||
|
||||
error = line_cb(patch->delta, &h->hunk, l, payload);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
size_t git_patch_size(
|
||||
git_patch *patch,
|
||||
int include_context,
|
||||
int include_hunk_headers,
|
||||
int include_file_headers)
|
||||
{
|
||||
size_t out;
|
||||
|
||||
assert(patch);
|
||||
|
||||
out = patch->content_size;
|
||||
|
||||
if (!include_context)
|
||||
out -= patch->context_size;
|
||||
|
||||
if (include_hunk_headers)
|
||||
out += patch->header_size;
|
||||
|
||||
if (include_file_headers) {
|
||||
git_buf file_header = GIT_BUF_INIT;
|
||||
|
||||
if (git_diff_delta__format_file_header(
|
||||
&file_header, patch->delta, NULL, NULL, 0) < 0)
|
||||
giterr_clear();
|
||||
else
|
||||
out += git_buf_len(&file_header);
|
||||
|
||||
git_buf_free(&file_header);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int git_patch_line_stats(
|
||||
size_t *total_ctxt,
|
||||
size_t *total_adds,
|
||||
size_t *total_dels,
|
||||
const git_patch *patch)
|
||||
{
|
||||
size_t totals[3], idx;
|
||||
|
||||
memset(totals, 0, sizeof(totals));
|
||||
|
||||
for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
|
||||
git_diff_line *line = git_array_get(patch->lines, idx);
|
||||
if (!line)
|
||||
continue;
|
||||
|
||||
switch (line->origin) {
|
||||
case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
|
||||
case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
|
||||
case GIT_DIFF_LINE_DELETION: totals[2]++; break;
|
||||
default:
|
||||
/* diff --stat and --numstat don't count EOFNL marks because
|
||||
* they will always be paired with a ADDITION or DELETION line.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (total_ctxt)
|
||||
*total_ctxt = totals[0];
|
||||
if (total_adds)
|
||||
*total_adds = totals[1];
|
||||
if (total_dels)
|
||||
*total_dels = totals[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const git_diff_delta *git_patch_get_delta(const git_patch *patch)
|
||||
{
|
||||
assert(patch);
|
||||
return patch->delta;
|
||||
}
|
||||
|
||||
size_t git_patch_num_hunks(const git_patch *patch)
|
||||
{
|
||||
assert(patch);
|
||||
return git_array_size(patch->hunks);
|
||||
}
|
||||
|
||||
static int patch_error_outofrange(const char *thing)
|
||||
{
|
||||
giterr_set(GITERR_INVALID, "patch %s index out of range", thing);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
int git_patch_get_hunk(
|
||||
const git_diff_hunk **out,
|
||||
size_t *lines_in_hunk,
|
||||
git_patch *patch,
|
||||
size_t hunk_idx)
|
||||
{
|
||||
git_patch_hunk *hunk;
|
||||
assert(patch);
|
||||
|
||||
hunk = git_array_get(patch->hunks, hunk_idx);
|
||||
|
||||
if (!hunk) {
|
||||
if (out) *out = NULL;
|
||||
if (lines_in_hunk) *lines_in_hunk = 0;
|
||||
return patch_error_outofrange("hunk");
|
||||
}
|
||||
|
||||
if (out) *out = &hunk->hunk;
|
||||
if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
|
||||
{
|
||||
git_patch_hunk *hunk;
|
||||
assert(patch);
|
||||
|
||||
if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
|
||||
return patch_error_outofrange("hunk");
|
||||
return (int)hunk->line_count;
|
||||
}
|
||||
|
||||
int git_patch_get_line_in_hunk(
|
||||
const git_diff_line **out,
|
||||
git_patch *patch,
|
||||
size_t hunk_idx,
|
||||
size_t line_of_hunk)
|
||||
{
|
||||
git_patch_hunk *hunk;
|
||||
git_diff_line *line;
|
||||
|
||||
assert(patch);
|
||||
|
||||
if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
|
||||
if (out) *out = NULL;
|
||||
return patch_error_outofrange("hunk");
|
||||
}
|
||||
|
||||
if (line_of_hunk >= hunk->line_count ||
|
||||
!(line = git_array_get(
|
||||
patch->lines, hunk->line_start + line_of_hunk))) {
|
||||
if (out) *out = NULL;
|
||||
return patch_error_outofrange("line");
|
||||
}
|
||||
|
||||
if (out) *out = line;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void git_patch__free(git_patch *patch)
|
||||
{
|
||||
if (patch->free_fn)
|
||||
patch->free_fn(patch);
|
||||
}
|
||||
|
||||
void git_patch_free(git_patch *patch)
|
||||
{
|
||||
if (patch)
|
||||
GIT_REFCOUNT_DEC(patch, git_patch__free);
|
||||
}
|
66
src/patch.h
Normal file
66
src/patch.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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_patch_h__
|
||||
#define INCLUDE_patch_h__
|
||||
|
||||
#include "git2/patch.h"
|
||||
#include "array.h"
|
||||
|
||||
/* cached information about a hunk in a patch */
|
||||
typedef struct git_patch_hunk {
|
||||
git_diff_hunk hunk;
|
||||
size_t line_start;
|
||||
size_t line_count;
|
||||
} git_patch_hunk;
|
||||
|
||||
struct git_patch {
|
||||
git_refcount rc;
|
||||
|
||||
git_repository *repo; /* may be null */
|
||||
|
||||
git_diff_options diff_opts;
|
||||
|
||||
git_diff_delta *delta;
|
||||
git_diff_binary binary;
|
||||
git_array_t(git_patch_hunk) hunks;
|
||||
git_array_t(git_diff_line) lines;
|
||||
|
||||
size_t header_size;
|
||||
size_t content_size;
|
||||
size_t context_size;
|
||||
|
||||
void (*free_fn)(git_patch *patch);
|
||||
};
|
||||
|
||||
extern int git_patch__invoke_callbacks(
|
||||
git_patch *patch,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_binary_cb binary_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_line_cb line_cb,
|
||||
void *payload);
|
||||
|
||||
extern int git_patch_line_stats(
|
||||
size_t *total_ctxt,
|
||||
size_t *total_adds,
|
||||
size_t *total_dels,
|
||||
const git_patch *patch);
|
||||
|
||||
/** Options for parsing patch files. */
|
||||
typedef struct {
|
||||
/**
|
||||
* The length of the prefix (in path segments) for the filenames.
|
||||
* This prefix will be removed when looking for files. The default is 1.
|
||||
*/
|
||||
uint32_t prefix_len;
|
||||
} git_patch_options;
|
||||
|
||||
#define GIT_PATCH_OPTIONS_INIT { 1 }
|
||||
|
||||
extern void git_patch_free(git_patch *patch);
|
||||
|
||||
#endif
|
@ -7,49 +7,78 @@
|
||||
#include "common.h"
|
||||
#include "git2/blob.h"
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
#include "diff_file.h"
|
||||
#include "diff_driver.h"
|
||||
#include "diff_patch.h"
|
||||
#include "patch_generate.h"
|
||||
#include "diff_xdiff.h"
|
||||
#include "delta.h"
|
||||
#include "zstream.h"
|
||||
#include "fileops.h"
|
||||
|
||||
static void diff_output_init(
|
||||
git_diff_output*, const git_diff_options*, git_diff_file_cb,
|
||||
git_patch_generated_output *, const git_diff_options *, git_diff_file_cb,
|
||||
git_diff_binary_cb, git_diff_hunk_cb, git_diff_line_cb, void*);
|
||||
|
||||
static void diff_output_to_patch(git_diff_output *, git_patch *);
|
||||
static void diff_output_to_patch(
|
||||
git_patch_generated_output *, git_patch_generated *);
|
||||
|
||||
static void diff_patch_update_binary(git_patch *patch)
|
||||
static void patch_generated_free(git_patch *p)
|
||||
{
|
||||
if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
|
||||
git_patch_generated *patch = (git_patch_generated *)p;
|
||||
|
||||
git_array_clear(patch->base.lines);
|
||||
git_array_clear(patch->base.hunks);
|
||||
|
||||
git__free((char *)patch->base.binary.old_file.data);
|
||||
git__free((char *)patch->base.binary.new_file.data);
|
||||
|
||||
git_diff_file_content__clear(&patch->ofile);
|
||||
git_diff_file_content__clear(&patch->nfile);
|
||||
|
||||
git_diff_free(patch->diff); /* decrements refcount */
|
||||
patch->diff = NULL;
|
||||
|
||||
git_pool_clear(&patch->flattened);
|
||||
|
||||
git__free((char *)patch->base.diff_opts.old_prefix);
|
||||
git__free((char *)patch->base.diff_opts.new_prefix);
|
||||
|
||||
if (patch->flags & GIT_PATCH_GENERATED_ALLOCATED)
|
||||
git__free(patch);
|
||||
}
|
||||
|
||||
static void patch_generated_update_binary(git_patch_generated *patch)
|
||||
{
|
||||
if ((patch->base.delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
|
||||
return;
|
||||
|
||||
if ((patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0 ||
|
||||
(patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||
patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
|
||||
patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
|
||||
|
||||
else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE ||
|
||||
patch->nfile.file->size > GIT_XDIFF_MAX_SIZE)
|
||||
patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
|
||||
patch->nfile.file->size > GIT_XDIFF_MAX_SIZE)
|
||||
patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
|
||||
|
||||
else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
|
||||
(patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
|
||||
patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
|
||||
(patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
|
||||
patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
|
||||
}
|
||||
|
||||
static void diff_patch_init_common(git_patch *patch)
|
||||
static void patch_generated_init_common(git_patch_generated *patch)
|
||||
{
|
||||
diff_patch_update_binary(patch);
|
||||
patch->base.free_fn = patch_generated_free;
|
||||
|
||||
patch->flags |= GIT_DIFF_PATCH_INITIALIZED;
|
||||
patch_generated_update_binary(patch);
|
||||
|
||||
patch->flags |= GIT_PATCH_GENERATED_INITIALIZED;
|
||||
|
||||
if (patch->diff)
|
||||
git_diff_addref(patch->diff);
|
||||
}
|
||||
|
||||
static int diff_patch_normalize_options(
|
||||
static int patch_generated_normalize_options(
|
||||
git_diff_options *out,
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
@ -75,38 +104,40 @@ static int diff_patch_normalize_options(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_patch_init_from_diff(
|
||||
git_patch *patch, git_diff *diff, size_t delta_index)
|
||||
static int patch_generated_init(
|
||||
git_patch_generated *patch, git_diff *diff, size_t delta_index)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
memset(patch, 0, sizeof(*patch));
|
||||
patch->diff = diff;
|
||||
patch->delta = git_vector_get(&diff->deltas, delta_index);
|
||||
|
||||
patch->diff = diff;
|
||||
patch->base.repo = diff->repo;
|
||||
patch->base.delta = git_vector_get(&diff->deltas, delta_index);
|
||||
patch->delta_index = delta_index;
|
||||
|
||||
if ((error = diff_patch_normalize_options(
|
||||
&patch->diff_opts, &diff->opts)) < 0 ||
|
||||
if ((error = patch_generated_normalize_options(
|
||||
&patch->base.diff_opts, &diff->opts)) < 0 ||
|
||||
(error = git_diff_file_content__init_from_diff(
|
||||
&patch->ofile, diff, patch->delta, true)) < 0 ||
|
||||
&patch->ofile, diff, patch->base.delta, true)) < 0 ||
|
||||
(error = git_diff_file_content__init_from_diff(
|
||||
&patch->nfile, diff, patch->delta, false)) < 0)
|
||||
&patch->nfile, diff, patch->base.delta, false)) < 0)
|
||||
return error;
|
||||
|
||||
diff_patch_init_common(patch);
|
||||
patch_generated_init_common(patch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_patch_alloc_from_diff(
|
||||
git_patch **out, git_diff *diff, size_t delta_index)
|
||||
static int patch_generated_alloc_from_diff(
|
||||
git_patch_generated **out, git_diff *diff, size_t delta_index)
|
||||
{
|
||||
int error;
|
||||
git_patch *patch = git__calloc(1, sizeof(git_patch));
|
||||
git_patch_generated *patch = git__calloc(1, sizeof(git_patch_generated));
|
||||
GITERR_CHECK_ALLOC(patch);
|
||||
|
||||
if (!(error = diff_patch_init_from_diff(patch, diff, delta_index))) {
|
||||
patch->flags |= GIT_DIFF_PATCH_ALLOCATED;
|
||||
if (!(error = patch_generated_init(patch, diff, delta_index))) {
|
||||
patch->flags |= GIT_PATCH_GENERATED_ALLOCATED;
|
||||
GIT_REFCOUNT_INC(patch);
|
||||
} else {
|
||||
git__free(patch);
|
||||
@ -117,27 +148,27 @@ static int diff_patch_alloc_from_diff(
|
||||
return error;
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) should_skip_binary(git_patch *patch, git_diff_file *file)
|
||||
GIT_INLINE(bool) should_skip_binary(git_patch_generated *patch, git_diff_file *file)
|
||||
{
|
||||
if ((patch->diff_opts.flags & GIT_DIFF_SHOW_BINARY) != 0)
|
||||
if ((patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) != 0)
|
||||
return false;
|
||||
|
||||
return (file->flags & GIT_DIFF_FLAG_BINARY) != 0;
|
||||
}
|
||||
|
||||
static bool diff_patch_diffable(git_patch *patch)
|
||||
static bool patch_generated_diffable(git_patch_generated *patch)
|
||||
{
|
||||
size_t olen, nlen;
|
||||
|
||||
if (patch->delta->status == GIT_DELTA_UNMODIFIED)
|
||||
if (patch->base.delta->status == GIT_DELTA_UNMODIFIED)
|
||||
return false;
|
||||
|
||||
/* if we've determined this to be binary (and we are not showing binary
|
||||
* data) then we have skipped loading the map data. instead, query the
|
||||
* file data itself.
|
||||
*/
|
||||
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0 &&
|
||||
(patch->diff_opts.flags & GIT_DIFF_SHOW_BINARY) == 0) {
|
||||
if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0 &&
|
||||
(patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) == 0) {
|
||||
olen = (size_t)patch->ofile.file->size;
|
||||
nlen = (size_t)patch->nfile.file->size;
|
||||
} else {
|
||||
@ -154,12 +185,12 @@ static bool diff_patch_diffable(git_patch *patch)
|
||||
!git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id));
|
||||
}
|
||||
|
||||
static int diff_patch_load(git_patch *patch, git_diff_output *output)
|
||||
static int patch_generated_load(git_patch_generated *patch, git_patch_generated_output *output)
|
||||
{
|
||||
int error = 0;
|
||||
bool incomplete_data;
|
||||
|
||||
if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0)
|
||||
if ((patch->flags & GIT_PATCH_GENERATED_LOADED) != 0)
|
||||
return 0;
|
||||
|
||||
/* if no hunk and data callbacks and user doesn't care if data looks
|
||||
@ -180,13 +211,13 @@ static int diff_patch_load(git_patch *patch, git_diff_output *output)
|
||||
*/
|
||||
if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) {
|
||||
if ((error = git_diff_file_content__load(
|
||||
&patch->ofile, &patch->diff_opts)) < 0 ||
|
||||
&patch->ofile, &patch->base.diff_opts)) < 0 ||
|
||||
should_skip_binary(patch, patch->ofile.file))
|
||||
goto cleanup;
|
||||
}
|
||||
if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) {
|
||||
if ((error = git_diff_file_content__load(
|
||||
&patch->nfile, &patch->diff_opts)) < 0 ||
|
||||
&patch->nfile, &patch->base.diff_opts)) < 0 ||
|
||||
should_skip_binary(patch, patch->nfile.file))
|
||||
goto cleanup;
|
||||
}
|
||||
@ -194,13 +225,13 @@ static int diff_patch_load(git_patch *patch, git_diff_output *output)
|
||||
/* once workdir has been tried, load other data as needed */
|
||||
if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) {
|
||||
if ((error = git_diff_file_content__load(
|
||||
&patch->ofile, &patch->diff_opts)) < 0 ||
|
||||
&patch->ofile, &patch->base.diff_opts)) < 0 ||
|
||||
should_skip_binary(patch, patch->ofile.file))
|
||||
goto cleanup;
|
||||
}
|
||||
if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) {
|
||||
if ((error = git_diff_file_content__load(
|
||||
&patch->nfile, &patch->diff_opts)) < 0 ||
|
||||
&patch->nfile, &patch->base.diff_opts)) < 0 ||
|
||||
should_skip_binary(patch, patch->nfile.file))
|
||||
goto cleanup;
|
||||
}
|
||||
@ -212,24 +243,24 @@ static int diff_patch_load(git_patch *patch, git_diff_output *output)
|
||||
patch->ofile.file->mode == patch->nfile.file->mode &&
|
||||
patch->ofile.file->mode != GIT_FILEMODE_COMMIT &&
|
||||
git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id) &&
|
||||
patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
|
||||
patch->delta->status = GIT_DELTA_UNMODIFIED;
|
||||
patch->base.delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
|
||||
patch->base.delta->status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
cleanup:
|
||||
diff_patch_update_binary(patch);
|
||||
patch_generated_update_binary(patch);
|
||||
|
||||
if (!error) {
|
||||
if (diff_patch_diffable(patch))
|
||||
patch->flags |= GIT_DIFF_PATCH_DIFFABLE;
|
||||
if (patch_generated_diffable(patch))
|
||||
patch->flags |= GIT_PATCH_GENERATED_DIFFABLE;
|
||||
|
||||
patch->flags |= GIT_DIFF_PATCH_LOADED;
|
||||
patch->flags |= GIT_PATCH_GENERATED_LOADED;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_patch_invoke_file_callback(
|
||||
git_patch *patch, git_diff_output *output)
|
||||
static int patch_generated_invoke_file_callback(
|
||||
git_patch_generated *patch, git_patch_generated_output *output)
|
||||
{
|
||||
float progress = patch->diff ?
|
||||
((float)patch->delta_index / patch->diff->deltas.length) : 1.0f;
|
||||
@ -238,7 +269,7 @@ static int diff_patch_invoke_file_callback(
|
||||
return 0;
|
||||
|
||||
return giterr_set_after_callback_function(
|
||||
output->file_cb(patch->delta, progress, output->payload),
|
||||
output->file_cb(patch->base.delta, progress, output->payload),
|
||||
"git_patch");
|
||||
}
|
||||
|
||||
@ -270,20 +301,24 @@ static int create_binary(
|
||||
}
|
||||
|
||||
if (a_datalen && b_datalen) {
|
||||
void *delta_data = git_delta(
|
||||
a_data, (unsigned long)a_datalen,
|
||||
b_data, (unsigned long)b_datalen,
|
||||
&delta_data_len, (unsigned long)deflate.size);
|
||||
void *delta_data;
|
||||
|
||||
if (delta_data) {
|
||||
error = git_delta(&delta_data, &delta_data_len,
|
||||
a_data, a_datalen,
|
||||
b_data, b_datalen,
|
||||
deflate.size);
|
||||
|
||||
if (error == 0) {
|
||||
error = git_zstream_deflatebuf(
|
||||
&delta, delta_data, (size_t)delta_data_len);
|
||||
|
||||
git__free(delta_data);
|
||||
|
||||
if (error < 0)
|
||||
goto done;
|
||||
} else if (error == GIT_EBUFS) {
|
||||
error = 0;
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (delta.size && delta.size < deflate.size) {
|
||||
@ -305,7 +340,7 @@ done:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_binary(git_diff_output *output, git_patch *patch)
|
||||
static int diff_binary(git_patch_generated_output *output, git_patch_generated *patch)
|
||||
{
|
||||
git_diff_binary binary = {{0}};
|
||||
const char *old_data = patch->ofile.map.data;
|
||||
@ -330,7 +365,7 @@ static int diff_binary(git_diff_output *output, git_patch *patch)
|
||||
return error;
|
||||
|
||||
error = giterr_set_after_callback_function(
|
||||
output->binary_cb(patch->delta, &binary, output->payload),
|
||||
output->binary_cb(patch->base.delta, &binary, output->payload),
|
||||
"git_patch");
|
||||
|
||||
git__free((char *) binary.old_file.data);
|
||||
@ -339,25 +374,27 @@ static int diff_binary(git_diff_output *output, git_patch *patch)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_patch_generate(git_patch *patch, git_diff_output *output)
|
||||
static int patch_generated_create(
|
||||
git_patch_generated *patch,
|
||||
git_patch_generated_output *output)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
|
||||
if ((patch->flags & GIT_PATCH_GENERATED_DIFFED) != 0)
|
||||
return 0;
|
||||
|
||||
/* if we are not looking at the binary or text data, don't do the diff */
|
||||
if (!output->binary_cb && !output->hunk_cb && !output->data_cb)
|
||||
return 0;
|
||||
|
||||
if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 &&
|
||||
(error = diff_patch_load(patch, output)) < 0)
|
||||
if ((patch->flags & GIT_PATCH_GENERATED_LOADED) == 0 &&
|
||||
(error = patch_generated_load(patch, output)) < 0)
|
||||
return error;
|
||||
|
||||
if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0)
|
||||
if ((patch->flags & GIT_PATCH_GENERATED_DIFFABLE) == 0)
|
||||
return 0;
|
||||
|
||||
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
|
||||
if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
|
||||
if (output->binary_cb)
|
||||
error = diff_binary(output, patch);
|
||||
}
|
||||
@ -366,33 +403,10 @@ static int diff_patch_generate(git_patch *patch, git_diff_output *output)
|
||||
error = output->diff_cb(output, patch);
|
||||
}
|
||||
|
||||
patch->flags |= GIT_DIFF_PATCH_DIFFED;
|
||||
patch->flags |= GIT_PATCH_GENERATED_DIFFED;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void diff_patch_free(git_patch *patch)
|
||||
{
|
||||
git_diff_file_content__clear(&patch->ofile);
|
||||
git_diff_file_content__clear(&patch->nfile);
|
||||
|
||||
git_array_clear(patch->lines);
|
||||
git_array_clear(patch->hunks);
|
||||
|
||||
git_diff_free(patch->diff); /* decrements refcount */
|
||||
patch->diff = NULL;
|
||||
|
||||
git_pool_clear(&patch->flattened);
|
||||
|
||||
git__free((char *)patch->diff_opts.old_prefix);
|
||||
git__free((char *)patch->diff_opts.new_prefix);
|
||||
|
||||
git__free((char *)patch->binary.old_file.data);
|
||||
git__free((char *)patch->binary.new_file.data);
|
||||
|
||||
if (patch->flags & GIT_DIFF_PATCH_ALLOCATED)
|
||||
git__free(patch);
|
||||
}
|
||||
|
||||
static int diff_required(git_diff *diff, const char *action)
|
||||
{
|
||||
if (diff)
|
||||
@ -412,7 +426,7 @@ int git_diff_foreach(
|
||||
int error = 0;
|
||||
git_xdiff_output xo;
|
||||
size_t idx;
|
||||
git_patch patch;
|
||||
git_patch_generated patch;
|
||||
|
||||
if ((error = diff_required(diff, "git_diff_foreach")) < 0)
|
||||
return error;
|
||||
@ -423,24 +437,24 @@ int git_diff_foreach(
|
||||
&xo.output, &diff->opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
|
||||
git_xdiff_init(&xo, &diff->opts);
|
||||
|
||||
git_vector_foreach(&diff->deltas, idx, patch.delta) {
|
||||
git_vector_foreach(&diff->deltas, idx, patch.base.delta) {
|
||||
|
||||
/* check flags against patch status */
|
||||
if (git_diff_delta__should_skip(&diff->opts, patch.delta))
|
||||
if (git_diff_delta__should_skip(&diff->opts, patch.base.delta))
|
||||
continue;
|
||||
|
||||
if (binary_cb || hunk_cb || data_cb) {
|
||||
if ((error = diff_patch_init_from_diff(&patch, diff, idx)) != 0 ||
|
||||
(error = diff_patch_load(&patch, &xo.output)) != 0)
|
||||
if ((error = patch_generated_init(&patch, diff, idx)) != 0 ||
|
||||
(error = patch_generated_load(&patch, &xo.output)) != 0)
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = diff_patch_invoke_file_callback(&patch, &xo.output)) == 0) {
|
||||
if ((error = patch_generated_invoke_file_callback(&patch, &xo.output)) == 0) {
|
||||
if (binary_cb || hunk_cb || data_cb)
|
||||
error = diff_patch_generate(&patch, &xo.output);
|
||||
error = patch_generated_create(&patch, &xo.output);
|
||||
}
|
||||
|
||||
git_patch_free(&patch);
|
||||
git_patch_free(&patch.base);
|
||||
|
||||
if (error)
|
||||
break;
|
||||
@ -450,15 +464,15 @@ int git_diff_foreach(
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_patch patch;
|
||||
git_patch_generated patch;
|
||||
git_diff_delta delta;
|
||||
char paths[GIT_FLEX_ARRAY];
|
||||
} diff_patch_with_delta;
|
||||
} patch_generated_with_delta;
|
||||
|
||||
static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
|
||||
static int diff_single_generate(patch_generated_with_delta *pd, git_xdiff_output *xo)
|
||||
{
|
||||
int error = 0;
|
||||
git_patch *patch = &pd->patch;
|
||||
git_patch_generated *patch = &pd->patch;
|
||||
bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
|
||||
bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
|
||||
|
||||
@ -469,24 +483,24 @@ static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
|
||||
if (git_oid_equal(&patch->nfile.file->id, &patch->ofile.file->id))
|
||||
pd->delta.status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
patch->delta = &pd->delta;
|
||||
patch->base.delta = &pd->delta;
|
||||
|
||||
diff_patch_init_common(patch);
|
||||
patch_generated_init_common(patch);
|
||||
|
||||
if (pd->delta.status == GIT_DELTA_UNMODIFIED &&
|
||||
!(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED))
|
||||
return error;
|
||||
|
||||
error = diff_patch_invoke_file_callback(patch, (git_diff_output *)xo);
|
||||
error = patch_generated_invoke_file_callback(patch, (git_patch_generated_output *)xo);
|
||||
|
||||
if (!error)
|
||||
error = diff_patch_generate(patch, (git_diff_output *)xo);
|
||||
error = patch_generated_create(patch, (git_patch_generated_output *)xo);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_patch_from_sources(
|
||||
diff_patch_with_delta *pd,
|
||||
static int patch_generated_from_sources(
|
||||
patch_generated_with_delta *pd,
|
||||
git_xdiff_output *xo,
|
||||
git_diff_file_content_src *oldsrc,
|
||||
git_diff_file_content_src *newsrc,
|
||||
@ -499,7 +513,7 @@ static int diff_patch_from_sources(
|
||||
git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file;
|
||||
git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile;
|
||||
|
||||
if ((error = diff_patch_normalize_options(&pd->patch.diff_opts, opts)) < 0)
|
||||
if ((error = patch_generated_normalize_options(&pd->patch.base.diff_opts, opts)) < 0)
|
||||
return error;
|
||||
|
||||
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
|
||||
@ -507,7 +521,7 @@ static int diff_patch_from_sources(
|
||||
tmp = ldata; ldata = rdata; rdata = tmp;
|
||||
}
|
||||
|
||||
pd->patch.delta = &pd->delta;
|
||||
pd->patch.base.delta = &pd->delta;
|
||||
|
||||
if (!oldsrc->as_path) {
|
||||
if (newsrc->as_path)
|
||||
@ -530,12 +544,12 @@ static int diff_patch_from_sources(
|
||||
return diff_single_generate(pd, xo);
|
||||
}
|
||||
|
||||
static int diff_patch_with_delta_alloc(
|
||||
diff_patch_with_delta **out,
|
||||
static int patch_generated_with_delta_alloc(
|
||||
patch_generated_with_delta **out,
|
||||
const char **old_path,
|
||||
const char **new_path)
|
||||
{
|
||||
diff_patch_with_delta *pd;
|
||||
patch_generated_with_delta *pd;
|
||||
size_t old_len = *old_path ? strlen(*old_path) : 0;
|
||||
size_t new_len = *new_path ? strlen(*new_path) : 0;
|
||||
size_t alloc_len;
|
||||
@ -547,7 +561,7 @@ static int diff_patch_with_delta_alloc(
|
||||
*out = pd = git__calloc(1, alloc_len);
|
||||
GITERR_CHECK_ALLOC(pd);
|
||||
|
||||
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
|
||||
pd->patch.flags = GIT_PATCH_GENERATED_ALLOCATED;
|
||||
|
||||
if (*old_path) {
|
||||
memcpy(&pd->paths[0], *old_path, old_len);
|
||||
@ -575,7 +589,7 @@ static int diff_from_sources(
|
||||
void *payload)
|
||||
{
|
||||
int error = 0;
|
||||
diff_patch_with_delta pd;
|
||||
patch_generated_with_delta pd;
|
||||
git_xdiff_output xo;
|
||||
|
||||
memset(&xo, 0, sizeof(xo));
|
||||
@ -585,9 +599,9 @@ static int diff_from_sources(
|
||||
|
||||
memset(&pd, 0, sizeof(pd));
|
||||
|
||||
error = diff_patch_from_sources(&pd, &xo, oldsrc, newsrc, opts);
|
||||
error = patch_generated_from_sources(&pd, &xo, oldsrc, newsrc, opts);
|
||||
|
||||
git_patch_free(&pd.patch);
|
||||
git_patch_free(&pd.patch.base);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -599,13 +613,13 @@ static int patch_from_sources(
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
int error = 0;
|
||||
diff_patch_with_delta *pd;
|
||||
patch_generated_with_delta *pd;
|
||||
git_xdiff_output xo;
|
||||
|
||||
assert(out);
|
||||
*out = NULL;
|
||||
|
||||
if ((error = diff_patch_with_delta_alloc(
|
||||
if ((error = patch_generated_with_delta_alloc(
|
||||
&pd, &oldsrc->as_path, &newsrc->as_path)) < 0)
|
||||
return error;
|
||||
|
||||
@ -613,7 +627,7 @@ static int patch_from_sources(
|
||||
diff_output_to_patch(&xo.output, &pd->patch);
|
||||
git_xdiff_init(&xo, opts);
|
||||
|
||||
if (!(error = diff_patch_from_sources(pd, &xo, oldsrc, newsrc, opts)))
|
||||
if (!(error = patch_generated_from_sources(pd, &xo, oldsrc, newsrc, opts)))
|
||||
*out = (git_patch *)pd;
|
||||
else
|
||||
git_patch_free((git_patch *)pd);
|
||||
@ -738,7 +752,7 @@ int git_patch_from_diff(
|
||||
int error = 0;
|
||||
git_xdiff_output xo;
|
||||
git_diff_delta *delta = NULL;
|
||||
git_patch *patch = NULL;
|
||||
git_patch_generated *patch = NULL;
|
||||
|
||||
if (patch_ptr) *patch_ptr = NULL;
|
||||
|
||||
@ -760,17 +774,17 @@ int git_patch_from_diff(
|
||||
(diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
|
||||
return 0;
|
||||
|
||||
if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0)
|
||||
if ((error = patch_generated_alloc_from_diff(&patch, diff, idx)) < 0)
|
||||
return error;
|
||||
|
||||
memset(&xo, 0, sizeof(xo));
|
||||
diff_output_to_patch(&xo.output, patch);
|
||||
git_xdiff_init(&xo, &diff->opts);
|
||||
|
||||
error = diff_patch_invoke_file_callback(patch, &xo.output);
|
||||
error = patch_generated_invoke_file_callback(patch, &xo.output);
|
||||
|
||||
if (!error)
|
||||
error = diff_patch_generate(patch, &xo.output);
|
||||
error = patch_generated_create(patch, &xo.output);
|
||||
|
||||
if (!error) {
|
||||
/* TODO: if cumulative diff size is < 0.5 total size, flatten patch */
|
||||
@ -778,237 +792,34 @@ int git_patch_from_diff(
|
||||
}
|
||||
|
||||
if (error || !patch_ptr)
|
||||
git_patch_free(patch);
|
||||
git_patch_free(&patch->base);
|
||||
else
|
||||
*patch_ptr = patch;
|
||||
*patch_ptr = &patch->base;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void git_patch_free(git_patch *patch)
|
||||
{
|
||||
if (patch)
|
||||
GIT_REFCOUNT_DEC(patch, diff_patch_free);
|
||||
}
|
||||
|
||||
const git_diff_delta *git_patch_get_delta(const git_patch *patch)
|
||||
{
|
||||
assert(patch);
|
||||
return patch->delta;
|
||||
}
|
||||
|
||||
size_t git_patch_num_hunks(const git_patch *patch)
|
||||
{
|
||||
assert(patch);
|
||||
return git_array_size(patch->hunks);
|
||||
}
|
||||
|
||||
int git_patch_line_stats(
|
||||
size_t *total_ctxt,
|
||||
size_t *total_adds,
|
||||
size_t *total_dels,
|
||||
const git_patch *patch)
|
||||
{
|
||||
size_t totals[3], idx;
|
||||
|
||||
memset(totals, 0, sizeof(totals));
|
||||
|
||||
for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
|
||||
git_diff_line *line = git_array_get(patch->lines, idx);
|
||||
if (!line)
|
||||
continue;
|
||||
|
||||
switch (line->origin) {
|
||||
case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
|
||||
case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
|
||||
case GIT_DIFF_LINE_DELETION: totals[2]++; break;
|
||||
default:
|
||||
/* diff --stat and --numstat don't count EOFNL marks because
|
||||
* they will always be paired with a ADDITION or DELETION line.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (total_ctxt)
|
||||
*total_ctxt = totals[0];
|
||||
if (total_adds)
|
||||
*total_adds = totals[1];
|
||||
if (total_dels)
|
||||
*total_dels = totals[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_error_outofrange(const char *thing)
|
||||
{
|
||||
giterr_set(GITERR_INVALID, "Diff patch %s index out of range", thing);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
int git_patch_get_hunk(
|
||||
const git_diff_hunk **out,
|
||||
size_t *lines_in_hunk,
|
||||
git_patch *patch,
|
||||
size_t hunk_idx)
|
||||
{
|
||||
diff_patch_hunk *hunk;
|
||||
assert(patch);
|
||||
|
||||
hunk = git_array_get(patch->hunks, hunk_idx);
|
||||
|
||||
if (!hunk) {
|
||||
if (out) *out = NULL;
|
||||
if (lines_in_hunk) *lines_in_hunk = 0;
|
||||
return diff_error_outofrange("hunk");
|
||||
}
|
||||
|
||||
if (out) *out = &hunk->hunk;
|
||||
if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
|
||||
{
|
||||
diff_patch_hunk *hunk;
|
||||
assert(patch);
|
||||
|
||||
if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
|
||||
return diff_error_outofrange("hunk");
|
||||
return (int)hunk->line_count;
|
||||
}
|
||||
|
||||
int git_patch_get_line_in_hunk(
|
||||
const git_diff_line **out,
|
||||
git_patch *patch,
|
||||
size_t hunk_idx,
|
||||
size_t line_of_hunk)
|
||||
{
|
||||
diff_patch_hunk *hunk;
|
||||
git_diff_line *line;
|
||||
|
||||
assert(patch);
|
||||
|
||||
if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
|
||||
if (out) *out = NULL;
|
||||
return diff_error_outofrange("hunk");
|
||||
}
|
||||
|
||||
if (line_of_hunk >= hunk->line_count ||
|
||||
!(line = git_array_get(
|
||||
patch->lines, hunk->line_start + line_of_hunk))) {
|
||||
if (out) *out = NULL;
|
||||
return diff_error_outofrange("line");
|
||||
}
|
||||
|
||||
if (out) *out = line;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t git_patch_size(
|
||||
git_patch *patch,
|
||||
int include_context,
|
||||
int include_hunk_headers,
|
||||
int include_file_headers)
|
||||
{
|
||||
size_t out;
|
||||
|
||||
assert(patch);
|
||||
|
||||
out = patch->content_size;
|
||||
|
||||
if (!include_context)
|
||||
out -= patch->context_size;
|
||||
|
||||
if (include_hunk_headers)
|
||||
out += patch->header_size;
|
||||
|
||||
if (include_file_headers) {
|
||||
git_buf file_header = GIT_BUF_INIT;
|
||||
|
||||
if (git_diff_delta__format_file_header(
|
||||
&file_header, patch->delta, NULL, NULL, 0) < 0)
|
||||
giterr_clear();
|
||||
else
|
||||
out += git_buf_len(&file_header);
|
||||
|
||||
git_buf_free(&file_header);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
git_diff *git_patch__diff(git_patch *patch)
|
||||
{
|
||||
return patch->diff;
|
||||
}
|
||||
|
||||
git_diff_driver *git_patch__driver(git_patch *patch)
|
||||
git_diff_driver *git_patch_generated_driver(git_patch_generated *patch)
|
||||
{
|
||||
/* ofile driver is representative for whole patch */
|
||||
return patch->ofile.driver;
|
||||
}
|
||||
|
||||
void git_patch__old_data(
|
||||
char **ptr, size_t *len, git_patch *patch)
|
||||
void git_patch_generated_old_data(
|
||||
char **ptr, size_t *len, git_patch_generated *patch)
|
||||
{
|
||||
*ptr = patch->ofile.map.data;
|
||||
*len = patch->ofile.map.len;
|
||||
}
|
||||
|
||||
void git_patch__new_data(
|
||||
char **ptr, size_t *len, git_patch *patch)
|
||||
void git_patch_generated_new_data(
|
||||
char **ptr, size_t *len, git_patch_generated *patch)
|
||||
{
|
||||
*ptr = patch->nfile.map.data;
|
||||
*len = patch->nfile.map.len;
|
||||
}
|
||||
|
||||
int git_patch__invoke_callbacks(
|
||||
git_patch *patch,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_binary_cb binary_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_line_cb line_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error = 0;
|
||||
uint32_t i, j;
|
||||
|
||||
if (file_cb)
|
||||
error = file_cb(patch->delta, 0, payload);
|
||||
|
||||
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
|
||||
if (binary_cb)
|
||||
error = binary_cb(patch->delta, &patch->binary, payload);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!hunk_cb && !line_cb)
|
||||
return error;
|
||||
|
||||
for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
|
||||
diff_patch_hunk *h = git_array_get(patch->hunks, i);
|
||||
|
||||
if (hunk_cb)
|
||||
error = hunk_cb(patch->delta, &h->hunk, payload);
|
||||
|
||||
if (!line_cb)
|
||||
continue;
|
||||
|
||||
for (j = 0; !error && j < h->line_count; ++j) {
|
||||
git_diff_line *l =
|
||||
git_array_get(patch->lines, h->line_start + j);
|
||||
|
||||
error = line_cb(patch->delta, &h->hunk, l, payload);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int diff_patch_file_cb(
|
||||
static int patch_generated_file_cb(
|
||||
const git_diff_delta *delta,
|
||||
float progress,
|
||||
void *payload)
|
||||
@ -1017,7 +828,7 @@ static int diff_patch_file_cb(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_patch_binary_cb(
|
||||
static int patch_generated_binary_cb(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_binary *binary,
|
||||
void *payload)
|
||||
@ -1047,62 +858,62 @@ static int diff_patch_binary_cb(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_patch_hunk_cb(
|
||||
static int git_patch_hunk_cb(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_hunk *hunk_,
|
||||
void *payload)
|
||||
{
|
||||
git_patch *patch = payload;
|
||||
diff_patch_hunk *hunk;
|
||||
git_patch_generated *patch = payload;
|
||||
git_patch_hunk *hunk;
|
||||
|
||||
GIT_UNUSED(delta);
|
||||
|
||||
hunk = git_array_alloc(patch->hunks);
|
||||
hunk = git_array_alloc(patch->base.hunks);
|
||||
GITERR_CHECK_ALLOC(hunk);
|
||||
|
||||
memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
|
||||
|
||||
patch->header_size += hunk_->header_len;
|
||||
patch->base.header_size += hunk_->header_len;
|
||||
|
||||
hunk->line_start = git_array_size(patch->lines);
|
||||
hunk->line_start = git_array_size(patch->base.lines);
|
||||
hunk->line_count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_patch_line_cb(
|
||||
static int patch_generated_line_cb(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_hunk *hunk_,
|
||||
const git_diff_line *line_,
|
||||
void *payload)
|
||||
{
|
||||
git_patch *patch = payload;
|
||||
diff_patch_hunk *hunk;
|
||||
git_patch_generated *patch = payload;
|
||||
git_patch_hunk *hunk;
|
||||
git_diff_line *line;
|
||||
|
||||
GIT_UNUSED(delta);
|
||||
GIT_UNUSED(hunk_);
|
||||
|
||||
hunk = git_array_last(patch->hunks);
|
||||
hunk = git_array_last(patch->base.hunks);
|
||||
assert(hunk); /* programmer error if no hunk is available */
|
||||
|
||||
line = git_array_alloc(patch->lines);
|
||||
line = git_array_alloc(patch->base.lines);
|
||||
GITERR_CHECK_ALLOC(line);
|
||||
|
||||
memcpy(line, line_, sizeof(*line));
|
||||
|
||||
/* do some bookkeeping so we can provide old/new line numbers */
|
||||
|
||||
patch->content_size += line->content_len;
|
||||
patch->base.content_size += line->content_len;
|
||||
|
||||
if (line->origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line->origin == GIT_DIFF_LINE_DELETION)
|
||||
patch->content_size += 1;
|
||||
patch->base.content_size += 1;
|
||||
else if (line->origin == GIT_DIFF_LINE_CONTEXT) {
|
||||
patch->content_size += 1;
|
||||
patch->context_size += line->content_len + 1;
|
||||
patch->base.content_size += 1;
|
||||
patch->base.context_size += line->content_len + 1;
|
||||
} else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
|
||||
patch->context_size += line->content_len;
|
||||
patch->base.context_size += line->content_len;
|
||||
|
||||
hunk->line_count++;
|
||||
|
||||
@ -1110,7 +921,7 @@ static int diff_patch_line_cb(
|
||||
}
|
||||
|
||||
static void diff_output_init(
|
||||
git_diff_output *out,
|
||||
git_patch_generated_output *out,
|
||||
const git_diff_options *opts,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_binary_cb binary_cb,
|
||||
@ -1129,14 +940,15 @@ static void diff_output_init(
|
||||
out->payload = payload;
|
||||
}
|
||||
|
||||
static void diff_output_to_patch(git_diff_output *out, git_patch *patch)
|
||||
static void diff_output_to_patch(
|
||||
git_patch_generated_output *out, git_patch_generated *patch)
|
||||
{
|
||||
diff_output_init(
|
||||
out,
|
||||
NULL,
|
||||
diff_patch_file_cb,
|
||||
diff_patch_binary_cb,
|
||||
diff_patch_hunk_cb,
|
||||
diff_patch_line_cb,
|
||||
patch_generated_file_cb,
|
||||
patch_generated_binary_cb,
|
||||
git_patch_hunk_cb,
|
||||
patch_generated_line_cb,
|
||||
patch);
|
||||
}
|
66
src/patch_generate.h
Normal file
66
src/patch_generate.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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_patch_generate_h__
|
||||
#define INCLUDE_patch_generate_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "diff.h"
|
||||
#include "diff_file.h"
|
||||
#include "patch.h"
|
||||
|
||||
enum {
|
||||
GIT_PATCH_GENERATED_ALLOCATED = (1 << 0),
|
||||
GIT_PATCH_GENERATED_INITIALIZED = (1 << 1),
|
||||
GIT_PATCH_GENERATED_LOADED = (1 << 2),
|
||||
/* the two sides are different */
|
||||
GIT_PATCH_GENERATED_DIFFABLE = (1 << 3),
|
||||
/* the difference between the two sides has been computed */
|
||||
GIT_PATCH_GENERATED_DIFFED = (1 << 4),
|
||||
GIT_PATCH_GENERATED_FLATTENED = (1 << 5),
|
||||
};
|
||||
|
||||
struct git_patch_generated {
|
||||
struct git_patch base;
|
||||
|
||||
git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
|
||||
size_t delta_index;
|
||||
git_diff_file_content ofile;
|
||||
git_diff_file_content nfile;
|
||||
uint32_t flags;
|
||||
git_pool flattened;
|
||||
};
|
||||
|
||||
typedef struct git_patch_generated git_patch_generated;
|
||||
|
||||
extern git_diff_driver *git_patch_generated_driver(git_patch_generated *);
|
||||
|
||||
extern void git_patch_generated_old_data(
|
||||
char **, size_t *, git_patch_generated *);
|
||||
extern void git_patch_generated_new_data(
|
||||
char **, size_t *, git_patch_generated *);
|
||||
|
||||
typedef struct git_patch_generated_output git_patch_generated_output;
|
||||
|
||||
struct git_patch_generated_output {
|
||||
/* these callbacks are issued with the diff data */
|
||||
git_diff_file_cb file_cb;
|
||||
git_diff_binary_cb binary_cb;
|
||||
git_diff_hunk_cb hunk_cb;
|
||||
git_diff_line_cb data_cb;
|
||||
void *payload;
|
||||
|
||||
/* this records the actual error in cases where it may be obscured */
|
||||
int error;
|
||||
|
||||
/* this callback is used to do the diff and drive the other callbacks.
|
||||
* see diff_xdiff.h for how to use this in practice for now.
|
||||
*/
|
||||
int (*diff_cb)(git_patch_generated_output *output,
|
||||
git_patch_generated *patch);
|
||||
};
|
||||
|
||||
#endif
|
1121
src/patch_parse.c
Normal file
1121
src/patch_parse.c
Normal file
File diff suppressed because it is too large
Load Diff
54
src/patch_parse.h
Normal file
54
src/patch_parse.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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_patch_parse_h__
|
||||
#define INCLUDE_patch_parse_h__
|
||||
|
||||
typedef struct {
|
||||
git_refcount rc;
|
||||
|
||||
/* Original content buffer */
|
||||
const char *content;
|
||||
size_t content_len;
|
||||
|
||||
git_patch_options opts;
|
||||
|
||||
/* The remaining (unparsed) buffer */
|
||||
const char *remain;
|
||||
size_t remain_len;
|
||||
|
||||
const char *line;
|
||||
size_t line_len;
|
||||
size_t line_num;
|
||||
} git_patch_parse_ctx;
|
||||
|
||||
extern git_patch_parse_ctx *git_patch_parse_ctx_init(
|
||||
const char *content,
|
||||
size_t content_len,
|
||||
const git_patch_options *opts);
|
||||
|
||||
extern void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx);
|
||||
|
||||
/**
|
||||
* Create a patch for a single file from the contents of a patch buffer.
|
||||
*
|
||||
* @param out The patch to be created
|
||||
* @param contents The contents of a patch file
|
||||
* @param contents_len The length of the patch file
|
||||
* @param opts The git_patch_options
|
||||
* @return 0 on success, <0 on failure.
|
||||
*/
|
||||
extern int git_patch_from_buffer(
|
||||
git_patch **out,
|
||||
const char *contents,
|
||||
size_t contents_len,
|
||||
const git_patch_options *opts);
|
||||
|
||||
extern int git_patch_parse(
|
||||
git_patch **out,
|
||||
git_patch_parse_ctx *ctx);
|
||||
|
||||
#endif
|
19
src/path.c
19
src/path.c
@ -306,6 +306,25 @@ int git_path_join_unrooted(
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_path_squash_slashes(git_buf *path)
|
||||
{
|
||||
char *p, *q;
|
||||
|
||||
if (path->size == 0)
|
||||
return;
|
||||
|
||||
for (p = path->ptr, q = path->ptr; *q; p++, q++) {
|
||||
*p = *q;
|
||||
|
||||
while (*q == '/' && *(q+1) == '/') {
|
||||
path->size--;
|
||||
q++;
|
||||
}
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
int git_path_prettify(git_buf *path_out, const char *path, const char *base)
|
||||
{
|
||||
char buf[GIT_PATH_MAX];
|
||||
|
@ -243,6 +243,12 @@ extern bool git_path_contains_file(git_buf *dir, const char *file);
|
||||
extern int git_path_join_unrooted(
|
||||
git_buf *path_out, const char *path, const char *base, ssize_t *root_at);
|
||||
|
||||
/**
|
||||
* Removes multiple occurrences of '/' in a row, squashing them into a
|
||||
* single '/'.
|
||||
*/
|
||||
extern void git_path_squash_slashes(git_buf *path);
|
||||
|
||||
/**
|
||||
* Clean up path, prepending base if it is not already rooted.
|
||||
*/
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "iterator.h"
|
||||
#include "merge.h"
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
|
||||
static int create_error(int error, const char *msg)
|
||||
{
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "git2/diff.h"
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
|
||||
static unsigned int index_delta2status(const git_diff_delta *head2idx)
|
||||
{
|
||||
|
22
src/util.c
22
src/util.c
@ -65,6 +65,12 @@ int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
|
||||
}
|
||||
|
||||
int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int base)
|
||||
{
|
||||
|
||||
return git__strntol64(result, nptr, (size_t)-1, endptr, base);
|
||||
}
|
||||
|
||||
int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
|
||||
{
|
||||
const char *p;
|
||||
int64_t n, nn;
|
||||
@ -111,7 +117,7 @@ int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int ba
|
||||
/*
|
||||
* Non-empty sequence of digits
|
||||
*/
|
||||
for (;; p++,ndig++) {
|
||||
for (; nptr_len > 0; p++,ndig++,nptr_len--) {
|
||||
c = *p;
|
||||
v = base;
|
||||
if ('0'<=c && c<='9')
|
||||
@ -147,12 +153,18 @@ Return:
|
||||
}
|
||||
|
||||
int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base)
|
||||
{
|
||||
|
||||
return git__strntol32(result, nptr, (size_t)-1, endptr, base);
|
||||
}
|
||||
|
||||
int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
|
||||
{
|
||||
int error;
|
||||
int32_t tmp_int;
|
||||
int64_t tmp_long;
|
||||
|
||||
if ((error = git__strtol64(&tmp_long, nptr, endptr, base)) < 0)
|
||||
if ((error = git__strntol64(&tmp_long, nptr, nptr_len, endptr, base)) < 0)
|
||||
return error;
|
||||
|
||||
tmp_int = tmp_long & 0xFFFFFFFF;
|
||||
@ -321,6 +333,12 @@ char *git__strsep(char **end, const char *sep)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t git__linenlen(const char *buffer, size_t buffer_len)
|
||||
{
|
||||
char *nl = memchr(buffer, '\n', buffer_len);
|
||||
return nl ? (size_t)(nl - buffer) + 1 : buffer_len;
|
||||
}
|
||||
|
||||
void git__hexdump(const char *buffer, size_t len)
|
||||
{
|
||||
static const size_t LINE_WIDTH = 16;
|
||||
|
10
src/util.h
10
src/util.h
@ -263,7 +263,10 @@ GIT_INLINE(int) git__signum(int val)
|
||||
}
|
||||
|
||||
extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);
|
||||
extern int git__strntol32(int32_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
|
||||
extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base);
|
||||
extern int git__strntol64(int64_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
|
||||
|
||||
|
||||
extern void git__hexdump(const char *buffer, size_t n);
|
||||
extern uint32_t git__hash(const void *key, int len, uint32_t seed);
|
||||
@ -290,6 +293,8 @@ GIT_INLINE(int) git__tolower(int c)
|
||||
# define git__tolower(a) tolower(a)
|
||||
#endif
|
||||
|
||||
extern size_t git__linenlen(const char *buffer, size_t buffer_len);
|
||||
|
||||
GIT_INLINE(const char *) git__next_line(const char *s)
|
||||
{
|
||||
while (*s && *s != '\n') s++;
|
||||
@ -466,6 +471,11 @@ GIT_INLINE(bool) git__iswildcard(int c)
|
||||
return (c == '*' || c == '?' || c == '[');
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) git__isxdigit(int c)
|
||||
{
|
||||
return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a string value as a boolean, just like Core Git does.
|
||||
*
|
||||
|
42
src/vector.c
42
src/vector.c
@ -7,6 +7,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "vector.h"
|
||||
#include "integer.h"
|
||||
|
||||
/* In elements, not bytes */
|
||||
#define MIN_ALLOCSIZE 8
|
||||
@ -330,6 +331,47 @@ int git_vector_resize_to(git_vector *v, size_t new_length)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len)
|
||||
{
|
||||
size_t new_length;
|
||||
|
||||
assert(insert_len > 0 && idx <= v->length);
|
||||
|
||||
GITERR_CHECK_ALLOC_ADD(&new_length, v->length, insert_len);
|
||||
|
||||
if (new_length > v->_alloc_size && resize_vector(v, new_length) < 0)
|
||||
return -1;
|
||||
|
||||
memmove(&v->contents[idx + insert_len], &v->contents[idx],
|
||||
sizeof(void *) * (v->length - idx));
|
||||
memset(&v->contents[idx], 0, sizeof(void *) * insert_len);
|
||||
|
||||
v->length = new_length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len)
|
||||
{
|
||||
size_t new_length = v->length - remove_len;
|
||||
size_t end_idx = 0;
|
||||
|
||||
assert(remove_len > 0);
|
||||
|
||||
if (git__add_sizet_overflow(&end_idx, idx, remove_len))
|
||||
assert(0);
|
||||
|
||||
assert(end_idx <= v->length);
|
||||
|
||||
if (end_idx < v->length)
|
||||
memmove(&v->contents[idx], &v->contents[end_idx],
|
||||
sizeof(void *) * (v->length - end_idx));
|
||||
|
||||
memset(&v->contents[new_length], 0, sizeof(void *) * remove_len);
|
||||
|
||||
v->length = new_length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_vector_set(void **old, git_vector *v, size_t position, void *value)
|
||||
{
|
||||
if (position + 1 > v->length) {
|
||||
|
@ -93,6 +93,9 @@ void git_vector_remove_matching(
|
||||
void *payload);
|
||||
|
||||
int git_vector_resize_to(git_vector *v, size_t new_length);
|
||||
int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len);
|
||||
int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len);
|
||||
|
||||
int git_vector_set(void **old, git_vector *v, size_t position, void *value);
|
||||
|
||||
/** Check if vector is sorted */
|
||||
|
@ -28,20 +28,31 @@ static int zstream_seterr(git_zstream *zs)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_zstream_init(git_zstream *zstream)
|
||||
int git_zstream_init(git_zstream *zstream, git_zstream_t type)
|
||||
{
|
||||
zstream->zerr = deflateInit(&zstream->z, Z_DEFAULT_COMPRESSION);
|
||||
zstream->type = type;
|
||||
|
||||
if (zstream->type == GIT_ZSTREAM_INFLATE)
|
||||
zstream->zerr = inflateInit(&zstream->z);
|
||||
else
|
||||
zstream->zerr = deflateInit(&zstream->z, Z_DEFAULT_COMPRESSION);
|
||||
return zstream_seterr(zstream);
|
||||
}
|
||||
|
||||
void git_zstream_free(git_zstream *zstream)
|
||||
{
|
||||
deflateEnd(&zstream->z);
|
||||
if (zstream->type == GIT_ZSTREAM_INFLATE)
|
||||
inflateEnd(&zstream->z);
|
||||
else
|
||||
deflateEnd(&zstream->z);
|
||||
}
|
||||
|
||||
void git_zstream_reset(git_zstream *zstream)
|
||||
{
|
||||
deflateReset(&zstream->z);
|
||||
if (zstream->type == GIT_ZSTREAM_INFLATE)
|
||||
inflateReset(&zstream->z);
|
||||
else
|
||||
deflateReset(&zstream->z);
|
||||
zstream->in = NULL;
|
||||
zstream->in_len = 0;
|
||||
zstream->zerr = Z_STREAM_END;
|
||||
@ -75,6 +86,11 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
|
||||
int zflush = Z_FINISH;
|
||||
size_t out_remain = *out_len;
|
||||
|
||||
if (zstream->in_len && zstream->zerr == Z_STREAM_END) {
|
||||
giterr_set(GITERR_ZLIB, "zlib input had trailing garbage");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (out_remain > 0 && zstream->zerr != Z_STREAM_END) {
|
||||
size_t out_queued, in_queued, out_used, in_used;
|
||||
|
||||
@ -97,7 +113,10 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
|
||||
out_queued = (size_t)zstream->z.avail_out;
|
||||
|
||||
/* compress next chunk */
|
||||
zstream->zerr = deflate(&zstream->z, zflush);
|
||||
if (zstream->type == GIT_ZSTREAM_INFLATE)
|
||||
zstream->zerr = inflate(&zstream->z, zflush);
|
||||
else
|
||||
zstream->zerr = deflate(&zstream->z, zflush);
|
||||
|
||||
if (zstream->zerr == Z_STREAM_ERROR)
|
||||
return zstream_seterr(zstream);
|
||||
@ -120,12 +139,12 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len)
|
||||
static int zstream_buf(git_buf *out, const void *in, size_t in_len, git_zstream_t type)
|
||||
{
|
||||
git_zstream zs = GIT_ZSTREAM_INIT;
|
||||
int error = 0;
|
||||
|
||||
if ((error = git_zstream_init(&zs)) < 0)
|
||||
if ((error = git_zstream_init(&zs, type)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_zstream_set_input(&zs, in, in_len)) < 0)
|
||||
@ -154,3 +173,13 @@ done:
|
||||
git_zstream_free(&zs);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len)
|
||||
{
|
||||
return zstream_buf(out, in, in_len, GIT_ZSTREAM_DEFLATE);
|
||||
}
|
||||
|
||||
int git_zstream_inflatebuf(git_buf *out, const void *in, size_t in_len)
|
||||
{
|
||||
return zstream_buf(out, in, in_len, GIT_ZSTREAM_INFLATE);
|
||||
}
|
||||
|
@ -12,8 +12,14 @@
|
||||
#include "common.h"
|
||||
#include "buffer.h"
|
||||
|
||||
typedef enum {
|
||||
GIT_ZSTREAM_INFLATE,
|
||||
GIT_ZSTREAM_DEFLATE,
|
||||
} git_zstream_t;
|
||||
|
||||
typedef struct {
|
||||
z_stream z;
|
||||
git_zstream_t type;
|
||||
const char *in;
|
||||
size_t in_len;
|
||||
int zerr;
|
||||
@ -21,7 +27,7 @@ typedef struct {
|
||||
|
||||
#define GIT_ZSTREAM_INIT {{0}}
|
||||
|
||||
int git_zstream_init(git_zstream *zstream);
|
||||
int git_zstream_init(git_zstream *zstream, git_zstream_t type);
|
||||
void git_zstream_free(git_zstream *zstream);
|
||||
|
||||
int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len);
|
||||
@ -35,5 +41,6 @@ bool git_zstream_done(git_zstream *zstream);
|
||||
void git_zstream_reset(git_zstream *zstream);
|
||||
|
||||
int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len);
|
||||
int git_zstream_inflatebuf(git_buf *out, const void *in, size_t in_len);
|
||||
|
||||
#endif /* INCLUDE_zstream_h__ */
|
||||
|
289
tests/apply/fromdiff.c
Normal file
289
tests/apply/fromdiff.c
Normal file
@ -0,0 +1,289 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "git2/sys/repository.h"
|
||||
|
||||
#include "apply.h"
|
||||
#include "repository.h"
|
||||
#include "buf_text.h"
|
||||
|
||||
#include "../patch/patch_common.h"
|
||||
|
||||
static git_repository *repo = NULL;
|
||||
static git_diff_options binary_opts = GIT_DIFF_OPTIONS_INIT;
|
||||
|
||||
void test_apply_fromdiff__initialize(void)
|
||||
{
|
||||
repo = cl_git_sandbox_init("renames");
|
||||
|
||||
binary_opts.flags |= GIT_DIFF_SHOW_BINARY;
|
||||
}
|
||||
|
||||
void test_apply_fromdiff__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
static int apply_gitbuf(
|
||||
const git_buf *old,
|
||||
const char *oldname,
|
||||
const git_buf *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->ptr : NULL, old ? old->size : 0,
|
||||
oldname,
|
||||
new ? new->ptr : NULL, new ? new->size : 0,
|
||||
newname,
|
||||
diff_opts));
|
||||
|
||||
if (patch_expected) {
|
||||
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->ptr : NULL, old ? old->size : 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 if (error == 0) {
|
||||
cl_assert_equal_s(new->ptr, result.ptr);
|
||||
cl_assert_equal_s(newname ? newname : oldname, filename);
|
||||
cl_assert_equal_i(0100644, mode);
|
||||
}
|
||||
|
||||
git__free(filename);
|
||||
git_buf_free(&result);
|
||||
git_buf_free(&patchbuf);
|
||||
git_patch_free(patch);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
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_buf o = GIT_BUF_INIT, n = GIT_BUF_INIT,
|
||||
*optr = NULL, *nptr = NULL;
|
||||
|
||||
if (old) {
|
||||
o.ptr = (char *)old;
|
||||
o.size = strlen(old);
|
||||
optr = &o;
|
||||
}
|
||||
|
||||
if (new) {
|
||||
n.ptr = (char *)new;
|
||||
n.size = strlen(new);
|
||||
nptr = &n;
|
||||
}
|
||||
|
||||
return apply_gitbuf(optr, oldname, nptr, newname, patch_expected, diff_opts);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void test_apply_fromdiff__no_change(void)
|
||||
{
|
||||
cl_git_pass(apply_buf(
|
||||
FILE_ORIGINAL, "file.txt",
|
||||
FILE_ORIGINAL, "file.txt",
|
||||
"", NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromdiff__binary_add(void)
|
||||
{
|
||||
git_buf newfile = GIT_BUF_INIT;
|
||||
|
||||
newfile.ptr = FILE_BINARY_DELTA_MODIFIED;
|
||||
newfile.size = FILE_BINARY_DELTA_MODIFIED_LEN;
|
||||
|
||||
cl_git_pass(apply_gitbuf(
|
||||
NULL, NULL,
|
||||
&newfile, "binary.bin",
|
||||
NULL, &binary_opts));
|
||||
}
|
||||
|
||||
void test_apply_fromdiff__binary_no_change(void)
|
||||
{
|
||||
git_buf original = GIT_BUF_INIT;
|
||||
|
||||
original.ptr = FILE_BINARY_DELTA_ORIGINAL;
|
||||
original.size = FILE_BINARY_DELTA_ORIGINAL_LEN;
|
||||
|
||||
cl_git_pass(apply_gitbuf(
|
||||
&original, "binary.bin",
|
||||
&original, "binary.bin",
|
||||
"", &binary_opts));
|
||||
}
|
||||
|
||||
void test_apply_fromdiff__binary_change_delta(void)
|
||||
{
|
||||
git_buf original = GIT_BUF_INIT, modified = GIT_BUF_INIT;
|
||||
|
||||
original.ptr = FILE_BINARY_DELTA_ORIGINAL;
|
||||
original.size = FILE_BINARY_DELTA_ORIGINAL_LEN;
|
||||
|
||||
modified.ptr = FILE_BINARY_DELTA_MODIFIED;
|
||||
modified.size = FILE_BINARY_DELTA_MODIFIED_LEN;
|
||||
|
||||
cl_git_pass(apply_gitbuf(
|
||||
&original, "binary.bin",
|
||||
&modified, "binary.bin",
|
||||
NULL, &binary_opts));
|
||||
}
|
||||
|
||||
void test_apply_fromdiff__binary_change_literal(void)
|
||||
{
|
||||
git_buf original = GIT_BUF_INIT, modified = GIT_BUF_INIT;
|
||||
|
||||
original.ptr = FILE_BINARY_LITERAL_ORIGINAL;
|
||||
original.size = FILE_BINARY_LITERAL_ORIGINAL_LEN;
|
||||
|
||||
modified.ptr = FILE_BINARY_LITERAL_MODIFIED;
|
||||
modified.size = FILE_BINARY_LITERAL_MODIFIED_LEN;
|
||||
|
||||
cl_git_pass(apply_gitbuf(
|
||||
&original, "binary.bin",
|
||||
&modified, "binary.bin",
|
||||
NULL, &binary_opts));
|
||||
}
|
||||
|
||||
void test_apply_fromdiff__binary_delete(void)
|
||||
{
|
||||
git_buf original = GIT_BUF_INIT;
|
||||
|
||||
original.ptr = FILE_BINARY_DELTA_MODIFIED;
|
||||
original.size = FILE_BINARY_DELTA_MODIFIED_LEN;
|
||||
|
||||
cl_git_pass(apply_gitbuf(
|
||||
&original, "binary.bin",
|
||||
NULL, NULL,
|
||||
NULL, &binary_opts));
|
||||
}
|
449
tests/apply/fromfile.c
Normal file
449
tests/apply/fromfile.c
Normal file
@ -0,0 +1,449 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "git2/sys/repository.h"
|
||||
|
||||
#include "apply.h"
|
||||
#include "patch.h"
|
||||
#include "patch_parse.h"
|
||||
#include "repository.h"
|
||||
#include "buf_text.h"
|
||||
|
||||
#include "../patch/patch_common.h"
|
||||
|
||||
static git_repository *repo = NULL;
|
||||
|
||||
void test_apply_fromfile__initialize(void)
|
||||
{
|
||||
repo = cl_git_sandbox_init("renames");
|
||||
}
|
||||
|
||||
void test_apply_fromfile__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
static int apply_patchfile(
|
||||
const char *old,
|
||||
size_t old_len,
|
||||
const char *new,
|
||||
size_t new_len,
|
||||
const char *patchfile,
|
||||
const char *filename_expected,
|
||||
unsigned int mode_expected)
|
||||
{
|
||||
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_buffer(&patch, patchfile, strlen(patchfile), NULL));
|
||||
|
||||
error = git_apply__patch(&result, &filename, &mode, old, old_len, patch);
|
||||
|
||||
if (error == 0) {
|
||||
cl_assert_equal_i(new_len, result.size);
|
||||
cl_assert(memcmp(new, result.ptr, new_len) == 0);
|
||||
|
||||
cl_assert_equal_s(filename_expected, filename);
|
||||
cl_assert_equal_i(mode_expected, mode);
|
||||
}
|
||||
|
||||
git__free(filename);
|
||||
git_buf_free(&result);
|
||||
git_buf_free(&patchbuf);
|
||||
git_patch_free(patch);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int validate_and_apply_patchfile(
|
||||
const char *old,
|
||||
size_t old_len,
|
||||
const char *new,
|
||||
size_t new_len,
|
||||
const char *patchfile,
|
||||
const git_diff_options *diff_opts,
|
||||
const char *filename_expected,
|
||||
unsigned int mode_expected)
|
||||
{
|
||||
git_patch *patch_fromdiff;
|
||||
git_buf validated = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
cl_git_pass(git_patch_from_buffers(&patch_fromdiff,
|
||||
old, old_len, "file.txt",
|
||||
new, new_len, "file.txt",
|
||||
diff_opts));
|
||||
cl_git_pass(git_patch_to_buf(&validated, patch_fromdiff));
|
||||
|
||||
cl_assert_equal_s(patchfile, validated.ptr);
|
||||
|
||||
error = apply_patchfile(old, old_len, new, new_len, patchfile, filename_expected, mode_expected);
|
||||
|
||||
git_buf_free(&validated);
|
||||
git_patch_free(patch_fromdiff);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void test_apply_fromfile__change_middle(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
|
||||
PATCH_ORIGINAL_TO_CHANGE_MIDDLE, NULL,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__change_middle_nocontext(void)
|
||||
{
|
||||
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||
diff_opts.context_lines = 0;
|
||||
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
|
||||
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT,
|
||||
&diff_opts, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
|
||||
void test_apply_fromfile__change_firstline(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_FIRSTLINE, strlen(FILE_CHANGE_FIRSTLINE),
|
||||
PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE, NULL,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__lastline(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_LASTLINE, strlen(FILE_CHANGE_LASTLINE),
|
||||
PATCH_ORIGINAL_TO_CHANGE_LASTLINE, NULL,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__change_middle_shrink(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE_SHRINK, strlen(FILE_CHANGE_MIDDLE_SHRINK),
|
||||
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_SHRINK, NULL,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__change_middle_shrink_nocontext(void)
|
||||
{
|
||||
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||
diff_opts.context_lines = 0;
|
||||
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE_SHRINK, strlen(FILE_CHANGE_MIDDLE_SHRINK),
|
||||
PATCH_ORIGINAL_TO_MIDDLE_SHRINK_NOCONTEXT, &diff_opts,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__change_middle_grow(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE_GROW, strlen(FILE_CHANGE_MIDDLE_GROW),
|
||||
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_GROW, NULL,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__change_middle_grow_nocontext(void)
|
||||
{
|
||||
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||
diff_opts.context_lines = 0;
|
||||
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE_GROW, strlen(FILE_CHANGE_MIDDLE_GROW),
|
||||
PATCH_ORIGINAL_TO_MIDDLE_GROW_NOCONTEXT, &diff_opts,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__prepend(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_PREPEND, strlen(FILE_PREPEND),
|
||||
PATCH_ORIGINAL_TO_PREPEND, NULL, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__prepend_nocontext(void)
|
||||
{
|
||||
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||
diff_opts.context_lines = 0;
|
||||
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_PREPEND, strlen(FILE_PREPEND),
|
||||
PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT, &diff_opts,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__append(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_APPEND, strlen(FILE_APPEND),
|
||||
PATCH_ORIGINAL_TO_APPEND, NULL, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__append_nocontext(void)
|
||||
{
|
||||
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||
diff_opts.context_lines = 0;
|
||||
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_APPEND, strlen(FILE_APPEND),
|
||||
PATCH_ORIGINAL_TO_APPEND_NOCONTEXT, &diff_opts,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__prepend_and_append(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_PREPEND_AND_APPEND, strlen(FILE_PREPEND_AND_APPEND),
|
||||
PATCH_ORIGINAL_TO_PREPEND_AND_APPEND, NULL,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__to_empty_file(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
"", 0,
|
||||
PATCH_ORIGINAL_TO_EMPTY_FILE, NULL, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__from_empty_file(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
"", 0,
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
PATCH_EMPTY_FILE_TO_ORIGINAL, NULL, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__add(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
NULL, 0,
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
PATCH_ADD_ORIGINAL, NULL, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__delete(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
NULL, 0,
|
||||
PATCH_DELETE_ORIGINAL, NULL, NULL, 0));
|
||||
}
|
||||
|
||||
|
||||
void test_apply_fromfile__rename_exact(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
PATCH_RENAME_EXACT, "newfile.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__rename_similar(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
|
||||
PATCH_RENAME_SIMILAR, "newfile.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__rename_similar_quotedname(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
|
||||
PATCH_RENAME_SIMILAR_QUOTEDNAME, "foo\"bar.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__modechange(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
PATCH_MODECHANGE_UNCHANGED, "file.txt", 0100755));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__modechange_with_modification(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
|
||||
PATCH_MODECHANGE_MODIFIED, "file.txt", 0100755));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__noisy(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
|
||||
PATCH_NOISY, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__noisy_nocontext(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
|
||||
PATCH_NOISY_NOCONTEXT, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_truncated_1(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_1,
|
||||
strlen(PATCH_TRUNCATED_1), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_truncated_2(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_2,
|
||||
strlen(PATCH_TRUNCATED_2), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_truncated_3(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_3,
|
||||
strlen(PATCH_TRUNCATED_3), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_corrupt_githeader(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch, PATCH_CORRUPT_GIT_HEADER,
|
||||
strlen(PATCH_CORRUPT_GIT_HEADER), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__empty_context(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_EMPTY_CONTEXT_ORIGINAL, strlen(FILE_EMPTY_CONTEXT_ORIGINAL),
|
||||
FILE_EMPTY_CONTEXT_MODIFIED, strlen(FILE_EMPTY_CONTEXT_MODIFIED),
|
||||
PATCH_EMPTY_CONTEXT,
|
||||
"file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__append_no_nl(void)
|
||||
{
|
||||
cl_git_pass(validate_and_apply_patchfile(
|
||||
FILE_ORIGINAL, strlen(FILE_ORIGINAL),
|
||||
FILE_APPEND_NO_NL, strlen(FILE_APPEND_NO_NL),
|
||||
PATCH_APPEND_NO_NL, NULL, "file.txt", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_missing_new_file(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch,
|
||||
PATCH_CORRUPT_MISSING_NEW_FILE,
|
||||
strlen(PATCH_CORRUPT_MISSING_NEW_FILE), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_missing_old_file(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch,
|
||||
PATCH_CORRUPT_MISSING_OLD_FILE,
|
||||
strlen(PATCH_CORRUPT_MISSING_OLD_FILE), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_no_changes(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch,
|
||||
PATCH_CORRUPT_NO_CHANGES,
|
||||
strlen(PATCH_CORRUPT_NO_CHANGES), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_missing_hunk_header(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch,
|
||||
PATCH_CORRUPT_MISSING_HUNK_HEADER,
|
||||
strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__fail_not_a_patch(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
cl_git_fail(git_patch_from_buffer(&patch, PATCH_NOT_A_PATCH,
|
||||
strlen(PATCH_NOT_A_PATCH), NULL));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__binary_add(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
NULL, 0,
|
||||
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
|
||||
PATCH_BINARY_ADD, "binary.bin", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__binary_change_delta(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_BINARY_DELTA_ORIGINAL, FILE_BINARY_DELTA_ORIGINAL_LEN,
|
||||
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
|
||||
PATCH_BINARY_DELTA, "binary.bin", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__binary_change_literal(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_BINARY_LITERAL_ORIGINAL, FILE_BINARY_LITERAL_ORIGINAL_LEN,
|
||||
FILE_BINARY_LITERAL_MODIFIED, FILE_BINARY_LITERAL_MODIFIED_LEN,
|
||||
PATCH_BINARY_LITERAL, "binary.bin", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__binary_delete(void)
|
||||
{
|
||||
cl_git_pass(apply_patchfile(
|
||||
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
|
||||
NULL, 0,
|
||||
PATCH_BINARY_DELETE, NULL, 0));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__binary_change_does_not_apply(void)
|
||||
{
|
||||
/* try to apply patch backwards, ensure it does not apply */
|
||||
cl_git_fail(apply_patchfile(
|
||||
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
|
||||
FILE_BINARY_DELTA_ORIGINAL, FILE_BINARY_DELTA_ORIGINAL_LEN,
|
||||
PATCH_BINARY_DELTA, "binary.bin", 0100644));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__binary_change_must_be_reversible(void)
|
||||
{
|
||||
cl_git_fail(apply_patchfile(
|
||||
FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
|
||||
NULL, 0,
|
||||
PATCH_BINARY_NOT_REVERSIBLE, NULL, 0));
|
||||
}
|
||||
|
||||
void test_apply_fromfile__empty_file_not_allowed(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
|
||||
cl_git_fail(git_patch_from_buffer(&patch, "", 0, NULL));
|
||||
cl_git_fail(git_patch_from_buffer(&patch, NULL, 0, NULL));
|
||||
}
|
88
tests/buf/quote.c
Normal file
88
tests/buf/quote.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "buffer.h"
|
||||
|
||||
static void expect_quote_pass(const char *expected, const char *str)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_buf_puts(&buf, str));
|
||||
cl_git_pass(git_buf_quote(&buf));
|
||||
|
||||
cl_assert_equal_s(expected, git_buf_cstr(&buf));
|
||||
cl_assert_equal_i(strlen(expected), git_buf_len(&buf));
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_buf_quote__quote_succeeds(void)
|
||||
{
|
||||
expect_quote_pass("", "");
|
||||
expect_quote_pass("foo", "foo");
|
||||
expect_quote_pass("foo/bar/baz.c", "foo/bar/baz.c");
|
||||
expect_quote_pass("foo bar", "foo bar");
|
||||
expect_quote_pass("\"\\\"leading quote\"", "\"leading quote");
|
||||
expect_quote_pass("\"slash\\\\y\"", "slash\\y");
|
||||
expect_quote_pass("\"foo\\r\\nbar\"", "foo\r\nbar");
|
||||
expect_quote_pass("\"foo\\177bar\"", "foo\177bar");
|
||||
expect_quote_pass("\"foo\\001bar\"", "foo\001bar");
|
||||
expect_quote_pass("\"foo\\377bar\"", "foo\377bar");
|
||||
}
|
||||
|
||||
static void expect_unquote_pass(const char *expected, const char *quoted)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_buf_puts(&buf, quoted));
|
||||
cl_git_pass(git_buf_unquote(&buf));
|
||||
|
||||
cl_assert_equal_s(expected, git_buf_cstr(&buf));
|
||||
cl_assert_equal_i(strlen(expected), git_buf_len(&buf));
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
static void expect_unquote_fail(const char *quoted)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_buf_puts(&buf, quoted));
|
||||
cl_git_fail(git_buf_unquote(&buf));
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_buf_quote__unquote_succeeds(void)
|
||||
{
|
||||
expect_unquote_pass("", "\"\"");
|
||||
expect_unquote_pass(" ", "\" \"");
|
||||
expect_unquote_pass("foo", "\"foo\"");
|
||||
expect_unquote_pass("foo bar", "\"foo bar\"");
|
||||
expect_unquote_pass("foo\"bar", "\"foo\\\"bar\"");
|
||||
expect_unquote_pass("foo\\bar", "\"foo\\\\bar\"");
|
||||
expect_unquote_pass("foo\tbar", "\"foo\\tbar\"");
|
||||
expect_unquote_pass("\vfoo\tbar\n", "\"\\vfoo\\tbar\\n\"");
|
||||
expect_unquote_pass("foo\nbar", "\"foo\\012bar\"");
|
||||
expect_unquote_pass("foo\r\nbar", "\"foo\\015\\012bar\"");
|
||||
expect_unquote_pass("foo\r\nbar", "\"\\146\\157\\157\\015\\012\\142\\141\\162\"");
|
||||
expect_unquote_pass("newline: \n", "\"newline: \\012\"");
|
||||
expect_unquote_pass("0xff: \377", "\"0xff: \\377\"");
|
||||
}
|
||||
|
||||
void test_buf_quote__unquote_fails(void)
|
||||
{
|
||||
expect_unquote_fail("no quotes at all");
|
||||
expect_unquote_fail("\"no trailing quote");
|
||||
expect_unquote_fail("no leading quote\"");
|
||||
expect_unquote_fail("\"invalid \\z escape char\"");
|
||||
expect_unquote_fail("\"\\q invalid escape char\"");
|
||||
expect_unquote_fail("\"invalid escape char \\p\"");
|
||||
expect_unquote_fail("\"invalid \\1 escape char \"");
|
||||
expect_unquote_fail("\"invalid \\14 escape char \"");
|
||||
expect_unquote_fail("\"invalid \\280 escape char\"");
|
||||
expect_unquote_fail("\"invalid \\378 escape char\"");
|
||||
expect_unquote_fail("\"invalid \\380 escape char\"");
|
||||
expect_unquote_fail("\"invalid \\411 escape char\"");
|
||||
expect_unquote_fail("\"truncated escape char \\\"");
|
||||
expect_unquote_fail("\"truncated escape char \\0\"");
|
||||
expect_unquote_fail("\"truncated escape char \\01\"");
|
||||
}
|
@ -813,6 +813,44 @@ void test_core_buffer__encode_base85(void)
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_core_buffer__decode_base85(void)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_buf_decode_base85(&buf, "bZBXF", 5, 4));
|
||||
cl_assert_equal_sz(4, buf.size);
|
||||
cl_assert_equal_s("this", buf.ptr);
|
||||
git_buf_clear(&buf);
|
||||
|
||||
cl_git_pass(git_buf_decode_base85(&buf, "ba!tca&BaE", 10, 8));
|
||||
cl_assert_equal_sz(8, buf.size);
|
||||
cl_assert_equal_s("two rnds", buf.ptr);
|
||||
git_buf_clear(&buf);
|
||||
|
||||
cl_git_pass(git_buf_decode_base85(&buf, "bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", 30, 23));
|
||||
cl_assert_equal_sz(23, buf.size);
|
||||
cl_assert_equal_s("this is base 85 encoded", buf.ptr);
|
||||
git_buf_clear(&buf);
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_core_buffer__decode_base85_fails_gracefully(void)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
git_buf_puts(&buf, "foobar");
|
||||
|
||||
cl_git_fail(git_buf_decode_base85(&buf, "invalid charsZZ", 15, 42));
|
||||
cl_git_fail(git_buf_decode_base85(&buf, "invalidchars__ ", 15, 42));
|
||||
cl_git_fail(git_buf_decode_base85(&buf, "overflowZZ~~~~~", 15, 42));
|
||||
cl_git_fail(git_buf_decode_base85(&buf, "truncated", 9, 42));
|
||||
cl_assert_equal_sz(6, buf.size);
|
||||
cl_assert_equal_s("foobar", buf.ptr);
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_core_buffer__classify_with_utf8(void)
|
||||
{
|
||||
char *data0 = "Simple text\n";
|
||||
|
@ -274,3 +274,105 @@ void test_core_vector__remove_matching(void)
|
||||
|
||||
git_vector_free(&x);
|
||||
}
|
||||
|
||||
static void assert_vector(git_vector *x, void *expected[], size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
cl_assert_equal_i(len, x->length);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
cl_assert(expected[i] == x->contents[i]);
|
||||
}
|
||||
|
||||
void test_core_vector__grow_and_shrink(void)
|
||||
{
|
||||
git_vector x = GIT_VECTOR_INIT;
|
||||
void *expected1[] = {
|
||||
(void *)0x02, (void *)0x03, (void *)0x04, (void *)0x05,
|
||||
(void *)0x06, (void *)0x07, (void *)0x08, (void *)0x09,
|
||||
(void *)0x0a
|
||||
};
|
||||
void *expected2[] = {
|
||||
(void *)0x02, (void *)0x04, (void *)0x05, (void *)0x06,
|
||||
(void *)0x07, (void *)0x08, (void *)0x09, (void *)0x0a
|
||||
};
|
||||
void *expected3[] = {
|
||||
(void *)0x02, (void *)0x04, (void *)0x05, (void *)0x06,
|
||||
(void *)0x0a
|
||||
};
|
||||
void *expected4[] = {
|
||||
(void *)0x02, (void *)0x04, (void *)0x05
|
||||
};
|
||||
void *expected5[] = {
|
||||
(void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04,
|
||||
(void *)0x05
|
||||
};
|
||||
void *expected6[] = {
|
||||
(void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04,
|
||||
(void *)0x05, (void *)0x00
|
||||
};
|
||||
void *expected7[] = {
|
||||
(void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04,
|
||||
(void *)0x00, (void *)0x00, (void *)0x00, (void *)0x05,
|
||||
(void *)0x00
|
||||
};
|
||||
void *expected8[] = {
|
||||
(void *)0x04, (void *)0x00, (void *)0x00, (void *)0x00,
|
||||
(void *)0x05, (void *)0x00
|
||||
};
|
||||
void *expected9[] = {
|
||||
(void *)0x04, (void *)0x00, (void *)0x05, (void *)0x00
|
||||
};
|
||||
void *expectedA[] = { (void *)0x04, (void *)0x00 };
|
||||
void *expectedB[] = { (void *)0x04 };
|
||||
|
||||
git_vector_insert(&x, (void *)0x01);
|
||||
git_vector_insert(&x, (void *)0x02);
|
||||
git_vector_insert(&x, (void *)0x03);
|
||||
git_vector_insert(&x, (void *)0x04);
|
||||
git_vector_insert(&x, (void *)0x05);
|
||||
git_vector_insert(&x, (void *)0x06);
|
||||
git_vector_insert(&x, (void *)0x07);
|
||||
git_vector_insert(&x, (void *)0x08);
|
||||
git_vector_insert(&x, (void *)0x09);
|
||||
git_vector_insert(&x, (void *)0x0a);
|
||||
|
||||
git_vector_remove_range(&x, 0, 1);
|
||||
assert_vector(&x, expected1, ARRAY_SIZE(expected1));
|
||||
|
||||
git_vector_remove_range(&x, 1, 1);
|
||||
assert_vector(&x, expected2, ARRAY_SIZE(expected2));
|
||||
|
||||
git_vector_remove_range(&x, 4, 3);
|
||||
assert_vector(&x, expected3, ARRAY_SIZE(expected3));
|
||||
|
||||
git_vector_remove_range(&x, 3, 2);
|
||||
assert_vector(&x, expected4, ARRAY_SIZE(expected4));
|
||||
|
||||
git_vector_insert_null(&x, 0, 2);
|
||||
assert_vector(&x, expected5, ARRAY_SIZE(expected5));
|
||||
|
||||
git_vector_insert_null(&x, 5, 1);
|
||||
assert_vector(&x, expected6, ARRAY_SIZE(expected6));
|
||||
|
||||
git_vector_insert_null(&x, 4, 3);
|
||||
assert_vector(&x, expected7, ARRAY_SIZE(expected7));
|
||||
|
||||
git_vector_remove_range(&x, 0, 3);
|
||||
assert_vector(&x, expected8, ARRAY_SIZE(expected8));
|
||||
|
||||
git_vector_remove_range(&x, 1, 2);
|
||||
assert_vector(&x, expected9, ARRAY_SIZE(expected9));
|
||||
|
||||
git_vector_remove_range(&x, 2, 2);
|
||||
assert_vector(&x, expectedA, ARRAY_SIZE(expectedA));
|
||||
|
||||
git_vector_remove_range(&x, 1, 1);
|
||||
assert_vector(&x, expectedB, ARRAY_SIZE(expectedB));
|
||||
|
||||
git_vector_remove_range(&x, 0, 1);
|
||||
assert_vector(&x, NULL, 0);
|
||||
|
||||
git_vector_free(&x);
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ void test_core_zstream__basic(void)
|
||||
char out[128];
|
||||
size_t outlen = sizeof(out);
|
||||
|
||||
cl_git_pass(git_zstream_init(&z));
|
||||
cl_git_pass(git_zstream_init(&z, GIT_ZSTREAM_DEFLATE));
|
||||
cl_git_pass(git_zstream_set_input(&z, data, strlen(data) + 1));
|
||||
cl_git_pass(git_zstream_get_output(out, &outlen, &z));
|
||||
cl_assert(git_zstream_done(&z));
|
||||
@ -58,6 +58,25 @@ void test_core_zstream__basic(void)
|
||||
assert_zlib_equal(data, strlen(data) + 1, out, outlen);
|
||||
}
|
||||
|
||||
void test_core_zstream__fails_on_trailing_garbage(void)
|
||||
{
|
||||
git_buf deflated = GIT_BUF_INIT, inflated = GIT_BUF_INIT;
|
||||
size_t i = 0;
|
||||
|
||||
/* compress a simple string */
|
||||
git_zstream_deflatebuf(&deflated, "foobar!!", 8);
|
||||
|
||||
/* append some garbage */
|
||||
for (i = 0; i < 10; i++) {
|
||||
git_buf_putc(&deflated, i);
|
||||
}
|
||||
|
||||
cl_git_fail(git_zstream_inflatebuf(&inflated, deflated.ptr, deflated.size));
|
||||
|
||||
git_buf_free(&deflated);
|
||||
git_buf_free(&inflated);
|
||||
}
|
||||
|
||||
void test_core_zstream__buffer(void)
|
||||
{
|
||||
git_buf out = GIT_BUF_INIT;
|
||||
@ -68,9 +87,10 @@ void test_core_zstream__buffer(void)
|
||||
|
||||
#define BIG_STRING_PART "Big Data IS Big - Long Data IS Long - We need a buffer larger than 1024 x 1024 to make sure we trigger chunked compression - Big Big Data IS Bigger than Big - Long Long Data IS Longer than Long"
|
||||
|
||||
static void compress_input_various_ways(git_buf *input)
|
||||
static void compress_and_decompress_input_various_ways(git_buf *input)
|
||||
{
|
||||
git_buf out1 = GIT_BUF_INIT, out2 = GIT_BUF_INIT;
|
||||
git_buf inflated = GIT_BUF_INIT;
|
||||
size_t i, fixed_size = max(input->size / 2, 256);
|
||||
char *fixed = git__malloc(fixed_size);
|
||||
cl_assert(fixed);
|
||||
@ -93,7 +113,7 @@ static void compress_input_various_ways(git_buf *input)
|
||||
}
|
||||
cl_assert(use_fixed_size <= fixed_size);
|
||||
|
||||
cl_git_pass(git_zstream_init(&zs));
|
||||
cl_git_pass(git_zstream_init(&zs, GIT_ZSTREAM_DEFLATE));
|
||||
cl_git_pass(git_zstream_set_input(&zs, input->ptr, input->size));
|
||||
|
||||
while (!git_zstream_done(&zs)) {
|
||||
@ -112,7 +132,12 @@ static void compress_input_various_ways(git_buf *input)
|
||||
git_buf_free(&out2);
|
||||
}
|
||||
|
||||
cl_git_pass(git_zstream_inflatebuf(&inflated, out1.ptr, out1.size));
|
||||
cl_assert_equal_i(input->size, inflated.size);
|
||||
cl_assert(memcmp(input->ptr, inflated.ptr, inflated.size) == 0);
|
||||
|
||||
git_buf_free(&out1);
|
||||
git_buf_free(&inflated);
|
||||
git__free(fixed);
|
||||
}
|
||||
|
||||
@ -129,14 +154,14 @@ void test_core_zstream__big_data(void)
|
||||
cl_git_pass(
|
||||
git_buf_put(&in, BIG_STRING_PART, strlen(BIG_STRING_PART)));
|
||||
|
||||
compress_input_various_ways(&in);
|
||||
compress_and_decompress_input_various_ways(&in);
|
||||
|
||||
/* make a big string that's hard to compress */
|
||||
srand(0xabad1dea);
|
||||
for (scan = 0; scan < in.size; ++scan)
|
||||
in.ptr[scan] = (char)rand();
|
||||
|
||||
compress_input_various_ways(&in);
|
||||
compress_and_decompress_input_various_ways(&in);
|
||||
}
|
||||
|
||||
git_buf_free(&in);
|
||||
|
@ -241,3 +241,76 @@ void diff_print_raw(FILE *fp, git_diff *diff)
|
||||
git_diff_print(diff, GIT_DIFF_FORMAT_RAW,
|
||||
git_diff_print_callback__to_file_handle, fp ? fp : stderr));
|
||||
}
|
||||
|
||||
static size_t num_modified_deltas(git_diff *diff)
|
||||
{
|
||||
const git_diff_delta *delta;
|
||||
size_t i, cnt = 0;
|
||||
|
||||
for (i = 0; i < git_diff_num_deltas(diff); i++) {
|
||||
delta = git_diff_get_delta(diff, i);
|
||||
|
||||
if (delta->status != GIT_DELTA_UNMODIFIED)
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
void diff_assert_equal(git_diff *a, git_diff *b)
|
||||
{
|
||||
const git_diff_delta *ad, *bd;
|
||||
size_t i, j;
|
||||
|
||||
assert(a && b);
|
||||
|
||||
cl_assert_equal_i(num_modified_deltas(a), num_modified_deltas(b));
|
||||
|
||||
for (i = 0, j = 0;
|
||||
i < git_diff_num_deltas(a) && j < git_diff_num_deltas(b); ) {
|
||||
|
||||
ad = git_diff_get_delta(a, i);
|
||||
bd = git_diff_get_delta(b, j);
|
||||
|
||||
if (ad->status == GIT_DELTA_UNMODIFIED) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (bd->status == GIT_DELTA_UNMODIFIED) {
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
cl_assert_equal_i(ad->status, bd->status);
|
||||
cl_assert_equal_i(ad->flags, bd->flags);
|
||||
cl_assert_equal_i(ad->similarity, bd->similarity);
|
||||
cl_assert_equal_i(ad->nfiles, bd->nfiles);
|
||||
|
||||
/* Don't examine the size or the flags of the deltas;
|
||||
* computed deltas have sizes (parsed deltas do not) and
|
||||
* computed deltas will have flags of `VALID_ID` and
|
||||
* `EXISTS` (parsed deltas will not query the ODB.)
|
||||
*/
|
||||
|
||||
/* an empty id indicates that it wasn't presented, because
|
||||
* the diff was identical. (eg, pure rename, mode change only, etc)
|
||||
*/
|
||||
if (ad->old_file.id_abbrev && bd->old_file.id_abbrev) {
|
||||
cl_assert_equal_i(ad->old_file.id_abbrev, bd->old_file.id_abbrev);
|
||||
cl_assert_equal_oid(&ad->old_file.id, &bd->old_file.id);
|
||||
cl_assert_equal_i(ad->old_file.mode, bd->old_file.mode);
|
||||
}
|
||||
cl_assert_equal_s(ad->old_file.path, bd->old_file.path);
|
||||
|
||||
if (ad->new_file.id_abbrev && bd->new_file.id_abbrev) {
|
||||
cl_assert_equal_oid(&ad->new_file.id, &bd->new_file.id);
|
||||
cl_assert_equal_i(ad->new_file.id_abbrev, bd->new_file.id_abbrev);
|
||||
cl_assert_equal_i(ad->new_file.mode, bd->new_file.mode);
|
||||
}
|
||||
cl_assert_equal_s(ad->new_file.path, bd->new_file.path);
|
||||
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,3 +68,6 @@ extern int diff_foreach_via_iterator(
|
||||
|
||||
extern void diff_print(FILE *fp, git_diff *diff);
|
||||
extern void diff_print_raw(FILE *fp, git_diff *diff);
|
||||
|
||||
extern void diff_assert_equal(git_diff *a, git_diff *b);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "buffer.h"
|
||||
#include "commit.h"
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
|
||||
static git_repository *repo;
|
||||
|
||||
@ -350,9 +351,6 @@ void test_diff_format_email__mode_change(void)
|
||||
"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
|
||||
"old mode 100644\n" \
|
||||
"new mode 100755\n" \
|
||||
"index a97157a..a97157a\n" \
|
||||
"--- a/file1.txt.renamed\n" \
|
||||
"+++ b/file1.txt.renamed\n" \
|
||||
"--\n" \
|
||||
"libgit2 " LIBGIT2_VERSION "\n" \
|
||||
"\n";
|
||||
|
153
tests/diff/parse.c
Normal file
153
tests/diff/parse.c
Normal file
@ -0,0 +1,153 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "patch.h"
|
||||
#include "patch_parse.h"
|
||||
#include "diff_helpers.h"
|
||||
|
||||
#include "../patch/patch_common.h"
|
||||
|
||||
void test_diff_parse__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
void test_diff_parse__nonpatches_fail_with_notfound(void)
|
||||
{
|
||||
git_diff *diff;
|
||||
const char *not = PATCH_NOT_A_PATCH;
|
||||
const char *not_with_leading = "Leading text.\n" PATCH_NOT_A_PATCH;
|
||||
const char *not_with_trailing = PATCH_NOT_A_PATCH "Trailing text.\n";
|
||||
const char *not_with_both = "Lead.\n" PATCH_NOT_A_PATCH "Trail.\n";
|
||||
|
||||
cl_git_fail_with(GIT_ENOTFOUND,
|
||||
git_diff_from_buffer(&diff,
|
||||
not,
|
||||
strlen(not)));
|
||||
cl_git_fail_with(GIT_ENOTFOUND,
|
||||
git_diff_from_buffer(&diff,
|
||||
not_with_leading,
|
||||
strlen(not_with_leading)));
|
||||
cl_git_fail_with(GIT_ENOTFOUND,
|
||||
git_diff_from_buffer(&diff,
|
||||
not_with_trailing,
|
||||
strlen(not_with_trailing)));
|
||||
cl_git_fail_with(GIT_ENOTFOUND,
|
||||
git_diff_from_buffer(&diff,
|
||||
not_with_both,
|
||||
strlen(not_with_both)));
|
||||
}
|
||||
|
||||
static void test_parse_invalid_diff(const char *invalid_diff)
|
||||
{
|
||||
git_diff *diff;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
/* throw some random (legitimate) diffs in with the given invalid
|
||||
* one.
|
||||
*/
|
||||
git_buf_puts(&buf, PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE);
|
||||
git_buf_puts(&buf, PATCH_BINARY_DELTA);
|
||||
git_buf_puts(&buf, invalid_diff);
|
||||
git_buf_puts(&buf, PATCH_ORIGINAL_TO_CHANGE_MIDDLE);
|
||||
git_buf_puts(&buf, PATCH_BINARY_LITERAL);
|
||||
|
||||
cl_git_fail_with(GIT_ERROR,
|
||||
git_diff_from_buffer(&diff, buf.ptr, buf.size));
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_diff_parse__invalid_patches_fails(void)
|
||||
{
|
||||
test_parse_invalid_diff(PATCH_CORRUPT_MISSING_NEW_FILE);
|
||||
test_parse_invalid_diff(PATCH_CORRUPT_MISSING_OLD_FILE);
|
||||
test_parse_invalid_diff(PATCH_CORRUPT_NO_CHANGES);
|
||||
test_parse_invalid_diff(PATCH_CORRUPT_MISSING_HUNK_HEADER);
|
||||
}
|
||||
|
||||
static void test_tree_to_tree_computed_to_parsed(
|
||||
const char *sandbox, const char *a_id, const char *b_id,
|
||||
uint32_t diff_flags, uint32_t find_flags)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_diff *computed, *parsed;
|
||||
git_tree *a, *b;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||
git_buf computed_buf = GIT_BUF_INIT;
|
||||
|
||||
repo = cl_git_sandbox_init(sandbox);
|
||||
|
||||
opts.id_abbrev = GIT_OID_HEXSZ;
|
||||
opts.flags = GIT_DIFF_SHOW_BINARY | diff_flags;
|
||||
findopts.flags = find_flags;
|
||||
|
||||
cl_assert((a = resolve_commit_oid_to_tree(repo, a_id)) != NULL);
|
||||
cl_assert((b = resolve_commit_oid_to_tree(repo, b_id)) != NULL);
|
||||
|
||||
cl_git_pass(git_diff_tree_to_tree(&computed, repo, a, b, &opts));
|
||||
|
||||
if (find_flags)
|
||||
cl_git_pass(git_diff_find_similar(computed, &findopts));
|
||||
|
||||
cl_git_pass(git_diff_to_buf(&computed_buf,
|
||||
computed, GIT_DIFF_FORMAT_PATCH));
|
||||
|
||||
cl_git_pass(git_diff_from_buffer(&parsed,
|
||||
computed_buf.ptr, computed_buf.size));
|
||||
|
||||
diff_assert_equal(computed, parsed);
|
||||
|
||||
git_tree_free(a);
|
||||
git_tree_free(b);
|
||||
|
||||
git_diff_free(computed);
|
||||
git_diff_free(parsed);
|
||||
|
||||
git_buf_free(&computed_buf);
|
||||
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
void test_diff_parse__can_parse_generated_diff(void)
|
||||
{
|
||||
test_tree_to_tree_computed_to_parsed(
|
||||
"diff", "d70d245e", "7a9e0b02", 0, 0);
|
||||
test_tree_to_tree_computed_to_parsed(
|
||||
"unsymlinked.git", "806999", "a8595c", 0, 0);
|
||||
test_tree_to_tree_computed_to_parsed("diff",
|
||||
"d70d245ed97ed2aa596dd1af6536e4bfdb047b69",
|
||||
"7a9e0b02e63179929fed24f0a3e0f19168114d10", 0, 0);
|
||||
test_tree_to_tree_computed_to_parsed(
|
||||
"unsymlinked.git", "7fccd7", "806999", 0, 0);
|
||||
test_tree_to_tree_computed_to_parsed(
|
||||
"unsymlinked.git", "7fccd7", "a8595c", 0, 0);
|
||||
test_tree_to_tree_computed_to_parsed(
|
||||
"attr", "605812a", "370fe9ec22", 0, 0);
|
||||
test_tree_to_tree_computed_to_parsed(
|
||||
"attr", "f5b0af1fb4f5c", "370fe9ec22", 0, 0);
|
||||
test_tree_to_tree_computed_to_parsed(
|
||||
"diff", "d70d245e", "d70d245e", 0, 0);
|
||||
test_tree_to_tree_computed_to_parsed("diff_format_email",
|
||||
"873806f6f27e631eb0b23e4b56bea2bfac14a373",
|
||||
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
|
||||
GIT_DIFF_SHOW_BINARY, 0);
|
||||
test_tree_to_tree_computed_to_parsed("diff_format_email",
|
||||
"897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
|
||||
"873806f6f27e631eb0b23e4b56bea2bfac14a373",
|
||||
GIT_DIFF_SHOW_BINARY, 0);
|
||||
test_tree_to_tree_computed_to_parsed("renames",
|
||||
"31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
|
||||
"2bc7f351d20b53f1c72c16c4b036e491c478c49a",
|
||||
0, GIT_DIFF_FIND_RENAMES);
|
||||
test_tree_to_tree_computed_to_parsed("renames",
|
||||
"31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
|
||||
"2bc7f351d20b53f1c72c16c4b036e491c478c49a",
|
||||
GIT_DIFF_INCLUDE_UNMODIFIED,
|
||||
0);
|
||||
test_tree_to_tree_computed_to_parsed("renames",
|
||||
"31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
|
||||
"2bc7f351d20b53f1c72c16c4b036e491c478c49a",
|
||||
GIT_DIFF_INCLUDE_UNMODIFIED,
|
||||
GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED | GIT_DIFF_FIND_EXACT_MATCH_ONLY);
|
||||
}
|
||||
|
@ -1702,3 +1702,49 @@ void test_diff_rename__blank_files_not_renamed_when_not_ignoring_whitespace(void
|
||||
expect_files_not_renamed("", "\n\n\n\n", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
|
||||
expect_files_not_renamed("\n\n\n\n", "\r\n\r\n\r\n", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
|
||||
}
|
||||
|
||||
/* test that 100% renames and copies emit the correct patch file
|
||||
* git diff --find-copies-harder -M100 -B100 \
|
||||
* 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
|
||||
* 2bc7f351d20b53f1c72c16c4b036e491c478c49a
|
||||
*/
|
||||
void test_diff_rename__identical(void)
|
||||
{
|
||||
const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
|
||||
const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
|
||||
git_tree *old_tree, *new_tree;
|
||||
git_diff *diff;
|
||||
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||
git_buf diff_buf = GIT_BUF_INIT;
|
||||
const char *expected =
|
||||
"diff --git a/serving.txt b/sixserving.txt\n"
|
||||
"similarity index 100%\n"
|
||||
"rename from serving.txt\n"
|
||||
"rename to sixserving.txt\n"
|
||||
"diff --git a/sevencities.txt b/songofseven.txt\n"
|
||||
"similarity index 100%\n"
|
||||
"copy from sevencities.txt\n"
|
||||
"copy to songofseven.txt\n";
|
||||
|
||||
old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
|
||||
new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
|
||||
|
||||
diff_opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
|
||||
find_opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED |
|
||||
GIT_DIFF_FIND_EXACT_MATCH_ONLY;
|
||||
|
||||
cl_git_pass(git_diff_tree_to_tree(&diff,
|
||||
g_repo, old_tree, new_tree, &diff_opts));
|
||||
cl_git_pass(git_diff_find_similar(diff, &find_opts));
|
||||
|
||||
cl_git_pass(git_diff_to_buf(&diff_buf, diff, GIT_DIFF_FORMAT_PATCH));
|
||||
|
||||
cl_assert_equal_s(expected, diff_buf.ptr);
|
||||
|
||||
git_buf_free(&diff_buf);
|
||||
git_diff_free(diff);
|
||||
git_tree_free(old_tree);
|
||||
git_tree_free(new_tree);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "buffer.h"
|
||||
#include "commit.h"
|
||||
#include "diff.h"
|
||||
#include "diff_generate.h"
|
||||
|
||||
static git_repository *_repo;
|
||||
static git_diff_stats *_stats;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "merge.h"
|
||||
#include "../merge_helpers.h"
|
||||
#include "diff.h"
|
||||
#include "diff_tform.h"
|
||||
#include "git2/sys/hashsig.h"
|
||||
|
||||
static git_repository *repo;
|
||||
|
104
tests/patch/parse.c
Normal file
104
tests/patch/parse.c
Normal file
@ -0,0 +1,104 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "patch.h"
|
||||
#include "patch_parse.h"
|
||||
|
||||
#include "patch_common.h"
|
||||
|
||||
static void ensure_patch_validity(git_patch *patch)
|
||||
{
|
||||
const git_diff_delta *delta;
|
||||
char idstr[GIT_OID_HEXSZ+1] = {0};
|
||||
|
||||
cl_assert((delta = git_patch_get_delta(patch)) != NULL);
|
||||
cl_assert_equal_i(2, delta->nfiles);
|
||||
|
||||
cl_assert_equal_s(delta->old_file.path, "file.txt");
|
||||
cl_assert(delta->old_file.mode == GIT_FILEMODE_BLOB);
|
||||
cl_assert_equal_i(7, delta->old_file.id_abbrev);
|
||||
git_oid_nfmt(idstr, delta->old_file.id_abbrev, &delta->old_file.id);
|
||||
cl_assert_equal_s(idstr, "9432026");
|
||||
cl_assert_equal_i(0, delta->old_file.size);
|
||||
|
||||
cl_assert_equal_s(delta->new_file.path, "file.txt");
|
||||
cl_assert(delta->new_file.mode == GIT_FILEMODE_BLOB);
|
||||
cl_assert_equal_i(7, delta->new_file.id_abbrev);
|
||||
git_oid_nfmt(idstr, delta->new_file.id_abbrev, &delta->new_file.id);
|
||||
cl_assert_equal_s(idstr, "cd8fd12");
|
||||
cl_assert_equal_i(0, delta->new_file.size);
|
||||
}
|
||||
|
||||
void test_patch_parse__original_to_change_middle(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
|
||||
cl_git_pass(git_patch_from_buffer(
|
||||
&patch, PATCH_ORIGINAL_TO_CHANGE_MIDDLE,
|
||||
strlen(PATCH_ORIGINAL_TO_CHANGE_MIDDLE), NULL));
|
||||
ensure_patch_validity(patch);
|
||||
git_patch_free(patch);
|
||||
}
|
||||
|
||||
void test_patch_parse__leading_and_trailing_garbage(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
const char *leading = "This is some leading garbage.\n"
|
||||
"Maybe it's email headers?\n"
|
||||
"\n"
|
||||
PATCH_ORIGINAL_TO_CHANGE_MIDDLE;
|
||||
const char *trailing = PATCH_ORIGINAL_TO_CHANGE_MIDDLE
|
||||
"\n"
|
||||
"This is some trailing garbage.\n"
|
||||
"Maybe it's an email signature?\n";
|
||||
const char *both = "Here's some leading garbage\n"
|
||||
PATCH_ORIGINAL_TO_CHANGE_MIDDLE
|
||||
"And here's some trailing.\n";
|
||||
|
||||
cl_git_pass(git_patch_from_buffer(&patch, leading, strlen(leading),
|
||||
NULL));
|
||||
ensure_patch_validity(patch);
|
||||
git_patch_free(patch);
|
||||
|
||||
cl_git_pass(git_patch_from_buffer(&patch, trailing, strlen(trailing),
|
||||
NULL));
|
||||
ensure_patch_validity(patch);
|
||||
git_patch_free(patch);
|
||||
|
||||
cl_git_pass(git_patch_from_buffer(&patch, both, strlen(both),
|
||||
NULL));
|
||||
ensure_patch_validity(patch);
|
||||
git_patch_free(patch);
|
||||
}
|
||||
|
||||
void test_patch_parse__nonpatches_fail_with_notfound(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
|
||||
cl_git_fail_with(GIT_ENOTFOUND,
|
||||
git_patch_from_buffer(&patch, PATCH_NOT_A_PATCH,
|
||||
strlen(PATCH_NOT_A_PATCH), NULL));
|
||||
}
|
||||
|
||||
void test_patch_parse__invalid_patches_fails(void)
|
||||
{
|
||||
git_patch *patch;
|
||||
|
||||
cl_git_fail_with(GIT_ERROR,
|
||||
git_patch_from_buffer(&patch, PATCH_CORRUPT_GIT_HEADER,
|
||||
strlen(PATCH_CORRUPT_GIT_HEADER), NULL));
|
||||
cl_git_fail_with(GIT_ERROR,
|
||||
git_patch_from_buffer(&patch,
|
||||
PATCH_CORRUPT_MISSING_NEW_FILE,
|
||||
strlen(PATCH_CORRUPT_MISSING_NEW_FILE), NULL));
|
||||
cl_git_fail_with(GIT_ERROR,
|
||||
git_patch_from_buffer(&patch,
|
||||
PATCH_CORRUPT_MISSING_OLD_FILE,
|
||||
strlen(PATCH_CORRUPT_MISSING_OLD_FILE), NULL));
|
||||
cl_git_fail_with(GIT_ERROR,
|
||||
git_patch_from_buffer(&patch, PATCH_CORRUPT_NO_CHANGES,
|
||||
strlen(PATCH_CORRUPT_NO_CHANGES), NULL));
|
||||
cl_git_fail_with(GIT_ERROR,
|
||||
git_patch_from_buffer(&patch,
|
||||
PATCH_CORRUPT_MISSING_HUNK_HEADER,
|
||||
strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
|
||||
}
|
||||
|
663
tests/patch/patch_common.h
Normal file
663
tests/patch/patch_common.h
Normal file
@ -0,0 +1,663 @@
|
||||
/* 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"
|
||||
|
||||
/* A change of the middle where we remove many lines */
|
||||
|
||||
#define FILE_CHANGE_MIDDLE_SHRINK \
|
||||
"hey!\n" \
|
||||
"i've changed a lot, but left the line\n" \
|
||||
"below it!\n"
|
||||
|
||||
#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE_SHRINK \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..629cd35 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"@@ -1,9 +1,3 @@\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" \
|
||||
"+i've changed a lot, but left the line\n" \
|
||||
" below it!\n"
|
||||
|
||||
#define PATCH_ORIGINAL_TO_MIDDLE_SHRINK_NOCONTEXT \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..629cd35 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"@@ -2,7 +2 @@ 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" \
|
||||
"+i've changed a lot, but left the line\n"
|
||||
|
||||
/* A change to the middle where we grow many lines */
|
||||
|
||||
#define FILE_CHANGE_MIDDLE_GROW \
|
||||
"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 line is added\n" \
|
||||
"so is this\n" \
|
||||
"(this too)\n" \
|
||||
"whee...\n" \
|
||||
"and this\n" \
|
||||
"is additional context\n" \
|
||||
"below it!\n"
|
||||
|
||||
#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE_GROW \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..207ebca 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"@@ -3,7 +3,11 @@ 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 line is added\n" \
|
||||
"+so is this\n" \
|
||||
"+(this too)\n" \
|
||||
"+whee...\n" \
|
||||
" and this\n" \
|
||||
" is additional context\n" \
|
||||
" below it!\n"
|
||||
|
||||
|
||||
#define PATCH_ORIGINAL_TO_MIDDLE_GROW_NOCONTEXT \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..207ebca 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"@@ -6 +6,5 @@ yes it is!\n" \
|
||||
"-(this line is changed)\n" \
|
||||
"+this line is changed\n" \
|
||||
"+and this line is added\n" \
|
||||
"+so is this\n" \
|
||||
"+(this too)\n" \
|
||||
"+whee...\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"
|
||||
|
||||
#define PATCH_RENAME_EXACT_QUOTEDNAME \
|
||||
"diff --git a/file.txt \"b/foo\\\"bar.txt\"\n" \
|
||||
"similarity index 100%\n" \
|
||||
"rename from file.txt\n" \
|
||||
"rename to \"foo\\\"bar.txt\"\n"
|
||||
|
||||
#define PATCH_RENAME_SIMILAR_QUOTEDNAME \
|
||||
"diff --git a/file.txt \"b/foo\\\"bar.txt\"\n" \
|
||||
"similarity index 77%\n" \
|
||||
"rename from file.txt\n" \
|
||||
"rename to \"foo\\\"bar.txt\"\n" \
|
||||
"index 9432026..cd8fd12 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ \"b/foo\\\"bar.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_MODECHANGE_UNCHANGED \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"old mode 100644\n" \
|
||||
"new mode 100755\n"
|
||||
|
||||
#define PATCH_MODECHANGE_MODIFIED \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"old mode 100644\n" \
|
||||
"new mode 100755\n" \
|
||||
"index 9432026..cd8fd12\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_NOISY \
|
||||
"This is some\nleading noise\n@@ - that\nlooks like a hunk header\n" \
|
||||
"but actually isn't and should parse ok\n" \
|
||||
PATCH_ORIGINAL_TO_CHANGE_MIDDLE \
|
||||
"plus some trailing garbage for good measure\n"
|
||||
|
||||
#define PATCH_NOISY_NOCONTEXT \
|
||||
"This is some\nleading noise\n@@ - that\nlooks like a hunk header\n" \
|
||||
"but actually isn't and should parse ok\n" \
|
||||
PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT \
|
||||
"plus some trailing garbage for good measure\n"
|
||||
|
||||
#define PATCH_TRUNCATED_1 \
|
||||
"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"
|
||||
|
||||
#define PATCH_TRUNCATED_2 \
|
||||
"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" \
|
||||
"-(this line is changed)\n" \
|
||||
"+(THIS line is changed!)\n" \
|
||||
" and this\n" \
|
||||
" is additional context\n" \
|
||||
" below it!\n"
|
||||
|
||||
#define PATCH_TRUNCATED_3 \
|
||||
"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" \
|
||||
" and this\n" \
|
||||
" is additional context\n" \
|
||||
" below it!\n"
|
||||
|
||||
#define FILE_EMPTY_CONTEXT_ORIGINAL \
|
||||
"this\nhas\nan\n\nempty\ncontext\nline\n"
|
||||
|
||||
#define FILE_EMPTY_CONTEXT_MODIFIED \
|
||||
"this\nhas\nan\n\nempty...\ncontext\nline\n"
|
||||
|
||||
#define PATCH_EMPTY_CONTEXT \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 398d2df..bb15234 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"@@ -2,6 +2,6 @@ this\n" \
|
||||
" has\n" \
|
||||
" an\n" \
|
||||
"\n" \
|
||||
"-empty\n" \
|
||||
"+empty...\n" \
|
||||
" context\n" \
|
||||
" line\n"
|
||||
|
||||
#define FILE_APPEND_NO_NL \
|
||||
"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" \
|
||||
"added line with no nl"
|
||||
|
||||
#define PATCH_APPEND_NO_NL \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..83759c0 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" \
|
||||
"+added line with no nl\n" \
|
||||
"\\ No newline at end of file\n"
|
||||
|
||||
#define PATCH_CORRUPT_GIT_HEADER \
|
||||
"diff --git a/file.txt\n" \
|
||||
"index 9432026..0f39b9a 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"@@ -0,0 +1 @@\n" \
|
||||
"+insert at front\n"
|
||||
|
||||
#define PATCH_CORRUPT_MISSING_NEW_FILE \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..cd8fd12 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"@@ -6 +6 @@ yes it is!\n" \
|
||||
"-(this line is changed)\n" \
|
||||
"+(THIS line is changed!)\n"
|
||||
|
||||
#define PATCH_CORRUPT_MISSING_OLD_FILE \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..cd8fd12 100644\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"@@ -6 +6 @@ yes it is!\n" \
|
||||
"-(this line is changed)\n" \
|
||||
"+(THIS line is changed!)\n"
|
||||
|
||||
#define PATCH_CORRUPT_NO_CHANGES \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..cd8fd12 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"@@ -0,0 +0,0 @@ yes it is!\n"
|
||||
|
||||
#define PATCH_CORRUPT_MISSING_HUNK_HEADER \
|
||||
"diff --git a/file.txt b/file.txt\n" \
|
||||
"index 9432026..cd8fd12 100644\n" \
|
||||
"--- a/file.txt\n" \
|
||||
"+++ b/file.txt\n" \
|
||||
"-(this line is changed)\n" \
|
||||
"+(THIS line is changed!)\n"
|
||||
|
||||
#define PATCH_NOT_A_PATCH \
|
||||
"+++this is not\n" \
|
||||
"--actually even\n" \
|
||||
" a legitimate \n" \
|
||||
"+patch file\n" \
|
||||
"-it's something else\n" \
|
||||
" entirely!"
|
||||
|
||||
/* binary contents */
|
||||
|
||||
#define FILE_BINARY_LITERAL_ORIGINAL "\x00\x00\x0a"
|
||||
#define FILE_BINARY_LITERAL_ORIGINAL_LEN 3
|
||||
|
||||
#define FILE_BINARY_LITERAL_MODIFIED "\x00\x00\x01\x02\x0a"
|
||||
#define FILE_BINARY_LITERAL_MODIFIED_LEN 5
|
||||
|
||||
#define PATCH_BINARY_LITERAL \
|
||||
"diff --git a/binary.bin b/binary.bin\n" \
|
||||
"index bd474b2519cc15eab801ff851cc7d50f0dee49a1..9ac35ff15cd8864aeafd889e4826a3150f0b06c4 100644\n" \
|
||||
"GIT binary patch\n" \
|
||||
"literal 5\n" \
|
||||
"Mc${NkU}WL~000&M4gdfE\n" \
|
||||
"\n" \
|
||||
"literal 3\n" \
|
||||
"Kc${Nk-~s>u4FC%O\n\n"
|
||||
|
||||
#define FILE_BINARY_DELTA_ORIGINAL \
|
||||
"\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02\x0a\x54\x68\x69" \
|
||||
"\x73\x20\x69\x73\x20\x61\x20\x62\x69\x6e\x61\x72\x79\x20\x66\x69" \
|
||||
"\x6c\x65\x2c\x20\x62\x79\x20\x76\x69\x72\x74\x75\x65\x20\x6f\x66" \
|
||||
"\x20\x68\x61\x76\x69\x6e\x67\x20\x73\x6f\x6d\x65\x20\x6e\x75\x6c" \
|
||||
"\x6c\x73\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02" \
|
||||
"\x0a\x57\x65\x27\x72\x65\x20\x67\x6f\x69\x6e\x67\x20\x74\x6f\x20" \
|
||||
"\x63\x68\x61\x6e\x67\x65\x20\x70\x6f\x72\x74\x69\x6f\x6e\x73\x20" \
|
||||
"\x6f\x66\x20\x69\x74\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00" \
|
||||
"\x00\x01\x02\x0a\x53\x6f\x20\x74\x68\x61\x74\x20\x77\x65\x20\x67" \
|
||||
"\x69\x74\x20\x61\x20\x62\x69\x6e\x61\x72\x79\x20\x64\x65\x6c\x74" \
|
||||
"\x61\x20\x69\x6e\x73\x74\x65\x61\x64\x20\x6f\x66\x20\x74\x68\x65" \
|
||||
"\x20\x64\x65\x66\x6c\x61\x74\x65\x64\x20\x63\x6f\x6e\x74\x65\x6e" \
|
||||
"\x74\x73\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02" \
|
||||
"\x0a"
|
||||
#define FILE_BINARY_DELTA_ORIGINAL_LEN 209
|
||||
|
||||
#define FILE_BINARY_DELTA_MODIFIED \
|
||||
"\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02\x0a\x5a\x5a\x5a" \
|
||||
"\x5a\x20\x69\x73\x20\x61\x20\x62\x69\x6e\x61\x72\x79\x20\x66\x69" \
|
||||
"\x6c\x65\x2c\x20\x62\x79\x20\x76\x69\x72\x74\x75\x65\x20\x6f\x66" \
|
||||
"\x20\x68\x61\x76\x69\x6e\x67\x20\x73\x6f\x6d\x65\x20\x6e\x75\x6c" \
|
||||
"\x6c\x73\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02" \
|
||||
"\x0a\x57\x65\x27\x72\x65\x20\x67\x6f\x69\x6e\x67\x20\x74\x6f\x20" \
|
||||
"\x63\x68\x61\x6e\x67\x65\x20\x70\x6f\x72\x74\x69\x6f\x6e\x73\x20" \
|
||||
"\x6f\x66\x20\x49\x54\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00" \
|
||||
"\x00\x01\x02\x0a\x53\x4f\x20\x74\x68\x61\x74\x20\x77\x65\x20\x67" \
|
||||
"\x69\x74\x20\x61\x20\x62\x69\x6e\x61\x72\x79\x20\x64\x65\x6c\x74" \
|
||||
"\x61\x20\x69\x6e\x73\x74\x65\x61\x64\x20\x6f\x66\x20\x74\x68\x65" \
|
||||
"\x20\x64\x65\x66\x6c\x61\x74\x65\x64\x20\x63\x6f\x6e\x74\x65\x6e" \
|
||||
"\x74\x73\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02" \
|
||||
"\x0a"
|
||||
#define FILE_BINARY_DELTA_MODIFIED_LEN 209
|
||||
|
||||
#define PATCH_BINARY_DELTA \
|
||||
"diff --git a/binary.bin b/binary.bin\n" \
|
||||
"index 27184d9883b12c4c9c54b4a31137603586169f51..7c94f9e60bf366033d98e0d551ae37d30faef74a 100644\n" \
|
||||
"GIT binary patch\n" \
|
||||
"delta 48\n" \
|
||||
"kc$~Y)c#%<%fq{_;hPk4EV4`4>uxE%K7m7r%|HL+L0In7XGynhq\n" \
|
||||
"\n" \
|
||||
"delta 48\n" \
|
||||
"mc$~Y)c#%<%fq{_;hPgsAGK(h)CJASj=y9P)1m{m|^9BI99|yz$\n\n"
|
||||
|
||||
#define PATCH_BINARY_ADD \
|
||||
"diff --git a/binary.bin b/binary.bin\n" \
|
||||
"new file mode 100644\n" \
|
||||
"index 0000000000000000000000000000000000000000..7c94f9e60bf366033d98e0d551ae37d30faef74a\n" \
|
||||
"GIT binary patch\n" \
|
||||
"literal 209\n" \
|
||||
"zc${60u?oUK5JXSQe8qG&;(u6KC<u0&+$Ohh?#kUJlD{_rLCL^0!@QXgcKh&k^H>C_\n" \
|
||||
"zAhe=XX7rNzh<3&##YcwqNHmEKsP<&&m~%Zf;eX@Khr$?aExDmfqyyt+#l^I)3+LMg\n" \
|
||||
"kxnAIj9Pfn_|Gh`fP7tlm6j#y{FJYg_IifRlR^R@A08f862mk;8\n" \
|
||||
"\n" \
|
||||
"literal 0\n" \
|
||||
"Hc$@<O00001\n\n"
|
||||
|
||||
#define PATCH_BINARY_DELETE \
|
||||
"diff --git a/binary.bin b/binary.bin\n" \
|
||||
"deleted file mode 100644\n" \
|
||||
"index 7c94f9e60bf366033d98e0d551ae37d30faef74a..0000000000000000000000000000000000000000\n" \
|
||||
"GIT binary patch\n" \
|
||||
"literal 0\n" \
|
||||
"Hc$@<O00001\n" \
|
||||
"\n" \
|
||||
"literal 209\n" \
|
||||
"zc${60u?oUK5JXSQe8qG&;(u6KC<u0&+$Ohh?#kUJlD{_rLCL^0!@QXgcKh&k^H>C_\n" \
|
||||
"zAhe=XX7rNzh<3&##YcwqNHmEKsP<&&m~%Zf;eX@Khr$?aExDmfqyyt+#l^I)3+LMg\n" \
|
||||
"kxnAIj9Pfn_|Gh`fP7tlm6j#y{FJYg_IifRlR^R@A08f862mk;8\n\n"
|
||||
|
||||
/* contains an old side that does not match the expected source */
|
||||
#define PATCH_BINARY_NOT_REVERSIBLE \
|
||||
"diff --git a/binary.bin b/binary.bin\n" \
|
||||
"index 27184d9883b12c4c9c54b4a31137603586169f51..7c94f9e60bf366033d98e0d551ae37d30faef74a 100644\n" \
|
||||
"GIT binary patch\n" \
|
||||
"literal 5\n" \
|
||||
"Mc${NkU}WL~000&M4gdfE\n" \
|
||||
"\n" \
|
||||
"delta 48\n" \
|
||||
"mc$~Y)c#%<%fq{_;hPgsAGK(h)CJASj=y9P)1m{m|^9BI99|yz$\n\n"
|
168
tests/patch/print.c
Normal file
168
tests/patch/print.c
Normal file
@ -0,0 +1,168 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "patch.h"
|
||||
#include "patch_parse.h"
|
||||
|
||||
#include "patch_common.h"
|
||||
|
||||
|
||||
/* sanity check the round-trip of patch parsing: ensure that we can parse
|
||||
* and then print a variety of patch files.
|
||||
*/
|
||||
|
||||
void patch_print_from_patchfile(const char *data, size_t len)
|
||||
{
|
||||
git_patch *patch;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_patch_from_buffer(&patch, data, len, NULL));
|
||||
cl_git_pass(git_patch_to_buf(&buf, patch));
|
||||
|
||||
cl_assert_equal_s(data, buf.ptr);
|
||||
|
||||
git_patch_free(patch);
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
void test_patch_print__change_middle(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_CHANGE_MIDDLE,
|
||||
strlen(PATCH_ORIGINAL_TO_CHANGE_MIDDLE));
|
||||
}
|
||||
|
||||
void test_patch_print__change_middle_nocontext(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT,
|
||||
strlen(PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT));
|
||||
}
|
||||
|
||||
void test_patch_print__change_firstline(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE,
|
||||
strlen(PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE));
|
||||
}
|
||||
|
||||
void test_patch_print__change_lastline(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_CHANGE_LASTLINE,
|
||||
strlen(PATCH_ORIGINAL_TO_CHANGE_LASTLINE));
|
||||
}
|
||||
|
||||
void test_patch_print__prepend(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_PREPEND,
|
||||
strlen(PATCH_ORIGINAL_TO_PREPEND));
|
||||
}
|
||||
|
||||
void test_patch_print__prepend_nocontext(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT,
|
||||
strlen(PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT));
|
||||
}
|
||||
|
||||
void test_patch_print__append(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_APPEND,
|
||||
strlen(PATCH_ORIGINAL_TO_APPEND));
|
||||
}
|
||||
|
||||
void test_patch_print__append_nocontext(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_APPEND_NOCONTEXT,
|
||||
strlen(PATCH_ORIGINAL_TO_APPEND_NOCONTEXT));
|
||||
}
|
||||
|
||||
void test_patch_print__prepend_and_append(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_PREPEND_AND_APPEND,
|
||||
strlen(PATCH_ORIGINAL_TO_PREPEND_AND_APPEND));
|
||||
}
|
||||
|
||||
void test_patch_print__to_empty_file(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ORIGINAL_TO_EMPTY_FILE,
|
||||
strlen(PATCH_ORIGINAL_TO_EMPTY_FILE));
|
||||
}
|
||||
|
||||
void test_patch_print__from_empty_file(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_EMPTY_FILE_TO_ORIGINAL,
|
||||
strlen(PATCH_EMPTY_FILE_TO_ORIGINAL));
|
||||
}
|
||||
|
||||
void test_patch_print__add(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_ADD_ORIGINAL,
|
||||
strlen(PATCH_ADD_ORIGINAL));
|
||||
}
|
||||
|
||||
void test_patch_print__delete(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_DELETE_ORIGINAL,
|
||||
strlen(PATCH_DELETE_ORIGINAL));
|
||||
}
|
||||
|
||||
void test_patch_print__rename_exact(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_RENAME_EXACT,
|
||||
strlen(PATCH_RENAME_EXACT));
|
||||
}
|
||||
|
||||
void test_patch_print__rename_similar(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_RENAME_SIMILAR,
|
||||
strlen(PATCH_RENAME_SIMILAR));
|
||||
}
|
||||
|
||||
void test_patch_print__rename_exact_quotedname(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_RENAME_EXACT_QUOTEDNAME,
|
||||
strlen(PATCH_RENAME_EXACT_QUOTEDNAME));
|
||||
}
|
||||
|
||||
void test_patch_print__rename_similar_quotedname(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_RENAME_SIMILAR_QUOTEDNAME,
|
||||
strlen(PATCH_RENAME_SIMILAR_QUOTEDNAME));
|
||||
}
|
||||
|
||||
void test_patch_print__modechange_unchanged(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_MODECHANGE_UNCHANGED,
|
||||
strlen(PATCH_MODECHANGE_UNCHANGED));
|
||||
}
|
||||
|
||||
void test_patch_print__modechange_modified(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_MODECHANGE_MODIFIED,
|
||||
strlen(PATCH_MODECHANGE_MODIFIED));
|
||||
}
|
||||
|
||||
void test_patch_print__binary_literal(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_BINARY_LITERAL,
|
||||
strlen(PATCH_BINARY_LITERAL));
|
||||
}
|
||||
|
||||
void test_patch_print__binary_delta(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_BINARY_DELTA,
|
||||
strlen(PATCH_BINARY_DELTA));
|
||||
}
|
||||
|
||||
void test_patch_print__binary_add(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_BINARY_ADD,
|
||||
strlen(PATCH_BINARY_ADD));
|
||||
}
|
||||
|
||||
void test_patch_print__binary_delete(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_BINARY_DELETE,
|
||||
strlen(PATCH_BINARY_DELETE));
|
||||
}
|
||||
|
||||
void test_patch_print__not_reversible(void)
|
||||
{
|
||||
patch_print_from_patchfile(PATCH_BINARY_NOT_REVERSIBLE,
|
||||
strlen(PATCH_BINARY_NOT_REVERSIBLE));
|
||||
}
|
Loading…
Reference in New Issue
Block a user