Commit 20302aa4 by Edward Thomson Committed by GitHub

Merge pull request #3223 from ethomson/apply

Reading patch files
parents 8774c47e 1a79cd95
...@@ -264,10 +264,15 @@ typedef enum { ...@@ -264,10 +264,15 @@ typedef enum {
* link, a submodule commit id, or even a tree (although that only if you * link, a submodule commit id, or even a tree (although that only if you
* are tracking type changes or ignored/untracked directories). * 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), * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
* then the oid will be zeroes. * 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 * `path` is the NUL-terminated path to the entry relative to the working
* directory of the repository. * directory of the repository.
* *
...@@ -280,6 +285,7 @@ typedef enum { ...@@ -280,6 +285,7 @@ typedef enum {
*/ */
typedef struct { typedef struct {
git_oid id; git_oid id;
int id_abbrev;
const char *path; const char *path;
git_off_t size; git_off_t size;
uint32_t flags; uint32_t flags;
...@@ -448,6 +454,8 @@ typedef int (*git_diff_file_cb)( ...@@ -448,6 +454,8 @@ typedef int (*git_diff_file_cb)(
float progress, float progress,
void *payload); void *payload);
#define GIT_DIFF_HUNK_HEADER_SIZE 128
/** /**
* When producing a binary diff, the binary data returned will be * When producing a binary diff, the binary data returned will be
* either the deflated full ("literal") contents of the file, or * either the deflated full ("literal") contents of the file, or
...@@ -499,12 +507,12 @@ typedef int(*git_diff_binary_cb)( ...@@ -499,12 +507,12 @@ typedef int(*git_diff_binary_cb)(
* Structure describing a hunk of a diff. * Structure describing a hunk of a diff.
*/ */
typedef struct { typedef struct {
int old_start; /**< Starting line number in old_file */ int old_start; /** Starting line number in old_file */
int old_lines; /**< Number of lines in old_file */ int old_lines; /** Number of lines in old_file */
int new_start; /**< Starting line number in new_file */ int new_start; /** Starting line number in new_file */
int new_lines; /**< Number of lines in new_file */ int new_lines; /** Number of lines in new_file */
size_t header_len; /**< Number of bytes in header text */ size_t header_len; /** Number of bytes in header text */
char header[128]; /**< Header text, NUL-byte terminated */ char header[GIT_DIFF_HUNK_HEADER_SIZE]; /** Header text, NUL-byte terminated */
} git_diff_hunk; } git_diff_hunk;
/** /**
...@@ -1046,6 +1054,21 @@ GIT_EXTERN(int) git_diff_print( ...@@ -1046,6 +1054,21 @@ GIT_EXTERN(int) git_diff_print(
git_diff_line_cb print_cb, git_diff_line_cb print_cb,
void *payload); 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( ...@@ -1166,6 +1189,11 @@ GIT_EXTERN(int) git_diff_buffers(
git_diff_line_cb line_cb, git_diff_line_cb line_cb,
void *payload); 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`. * 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 * You are responsible for releasing the object memory when done, using the
......
...@@ -98,7 +98,8 @@ typedef enum { ...@@ -98,7 +98,8 @@ typedef enum {
GITERR_CHERRYPICK, GITERR_CHERRYPICK,
GITERR_DESCRIBE, GITERR_DESCRIBE,
GITERR_REBASE, GITERR_REBASE,
GITERR_FILESYSTEM GITERR_FILESYSTEM,
GITERR_PATCH,
} git_error_t; } git_error_t;
/** /**
......
/*
* 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;
}
/*
* 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: ...@@ -85,7 +85,6 @@ on_oom:
#define git_array_foreach(a, i, element) \ #define git_array_foreach(a, i, element) \
for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++) for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
GIT_INLINE(int) git_array__search( GIT_INLINE(int) git_array__search(
size_t *out, size_t *out,
void *array_ptr, void *array_ptr,
......
...@@ -273,42 +273,51 @@ int git_buf_encode_base64(git_buf *buf, const char *data, size_t len) ...@@ -273,42 +273,51 @@ int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
return 0; return 0;
} }
/* The inverse of base64_encode, offset by '+' == 43. */ /* The inverse of base64_encode */
static const int8_t base64_decode[] = { static const int8_t base64_decode[] = {
62, -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,
63, -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, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
-1, -1, -1, 0, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-1, -1, -1, -1, -1, -1, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
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
}; };
#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) int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
{ {
size_t i; size_t i;
int8_t a, b, c, d; int8_t a, b, c, d;
size_t orig_size = buf->size, new_size; size_t orig_size = buf->size, new_size;
if (len % 4) {
giterr_set(GITERR_INVALID, "invalid base64 input");
return -1;
}
assert(len % 4 == 0); assert(len % 4 == 0);
GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size); GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1); GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
ENSURE_SIZE(buf, new_size); ENSURE_SIZE(buf, new_size);
for (i = 0; i < len; i += 4) { for (i = 0; i < len; i += 4) {
if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 || if ((a = base64_decode[(unsigned char)base64[i]]) < 0 ||
(b = BASE64_DECODE_VALUE(base64[i+1])) < 0 || (b = base64_decode[(unsigned char)base64[i+1]]) < 0 ||
(c = BASE64_DECODE_VALUE(base64[i+2])) < 0 || (c = base64_decode[(unsigned char)base64[i+2]]) < 0 ||
(d = BASE64_DECODE_VALUE(base64[i+3])) < 0) { (d = base64_decode[(unsigned char)base64[i+3]]) < 0) {
buf->size = orig_size; buf->size = orig_size;
buf->ptr[buf->size] = '\0'; buf->ptr[buf->size] = '\0';
giterr_set(GITERR_INVALID, "Invalid base64 input"); giterr_set(GITERR_INVALID, "invalid base64 input");
return -1; return -1;
} }
...@@ -321,7 +330,7 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len) ...@@ -321,7 +330,7 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
return 0; return 0;
} }
static const char b85str[] = static const char base85_encode[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len) 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) ...@@ -351,7 +360,7 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
int val = acc % 85; int val = acc % 85;
acc /= 85; acc /= 85;
b85[i] = b85str[val]; b85[i] = base85_encode[val];
} }
for (i = 0; i < 5; i++) for (i = 0; i < 5; i++)
...@@ -363,6 +372,88 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len) ...@@ -363,6 +372,88 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
return 0; 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) int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{ {
size_t expected_size, new_size; size_t expected_size, new_size;
...@@ -766,3 +857,144 @@ int git_buf_splice( ...@@ -766,3 +857,144 @@ int git_buf_splice(
buf->ptr[buf->size] = '\0'; buf->ptr[buf->size] = '\0';
return 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(&quoted, '"');
git_buf_put(&quoted, 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(&quoted, '\\');
git_buf_putc(&quoted, whitespace[buf->ptr[i] - '\a']);
}
/* double quote and backslash must be escaped */
else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
git_buf_putc(&quoted, '\\');
git_buf_putc(&quoted, buf->ptr[i]);
}
/* escape anything unprintable as octal */
else if (buf->ptr[i] != ' ' &&
(buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
git_buf_printf(&quoted, "\\%03o", (unsigned char)buf->ptr[i]);
}
/* yay, printable! */
else {
git_buf_putc(&quoted, buf->ptr[i]);
}
}
git_buf_putc(&quoted, '"');
if (git_buf_oom(&quoted)) {
error = -1;
goto done;
}
git_buf_swap(&quoted, buf);
done:
git_buf_free(&quoted);
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); ...@@ -173,6 +173,12 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b); 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 */ /* Write data as base64 encoded in buffer */
int git_buf_encode_base64(git_buf *buf, const char *data, size_t len); 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 */ /* 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); ...@@ -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 */ /* Write data as "base85" encoded in buffer */
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len); 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. * Insert, remove or replace a portion of the buffer.
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "filter.h" #include "filter.h"
#include "blob.h" #include "blob.h"
#include "diff.h" #include "diff.h"
#include "diff_generate.h"
#include "pathspec.h" #include "pathspec.h"
#include "buf_text.h" #include "buf_text.h"
#include "diff_xdiff.h" #include "diff_xdiff.h"
......
/*
* 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;
}
/*
* 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
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
#define INCLUDE_git_delta_h__ #define INCLUDE_git_delta_h__
#include "common.h" #include "common.h"
#include "pack.h"
/* opaque object for delta index */ typedef struct git_delta_index git_delta_index;
struct 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 * 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 * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
...@@ -19,22 +19,18 @@ struct git_delta_index; ...@@ -19,22 +19,18 @@ struct git_delta_index;
* before free_delta_index() is called. The returned pointer must be freed * before free_delta_index() is called. The returned pointer must be freed
* using free_delta_index(). * using free_delta_index().
*/ */
extern struct git_delta_index * extern int git_delta_index_init(
git_delta_create_index(const void *buf, unsigned long bufsize); git_delta_index **out, const void *buf, size_t bufsize);
/* /*
* free_delta_index: free the index created by create_delta_index() * Free the index created by git_delta_index_init()
*
* Given pointer must be what create_delta_index() returned, or NULL.
*/ */
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 * Returns memory usage of delta index.
*
* Given pointer must be what create_delta_index() returned, or NULL.
*/ */
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 * 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); ...@@ -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 * returned and *delta_size is updated with its size. The returned buffer
* must be freed by the caller. * 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 struct git_delta_index *index,
const void *buf, const void *buf,
unsigned long bufsize, size_t bufsize,
unsigned long *delta_size, size_t max_delta_size);
unsigned long max_delta_size);
/* /*
* diff_delta: create a delta from source buffer to target buffer * 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 * 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 * 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. * updated with its size. The returned buffer must be freed by the caller.
*/ */
GIT_INLINE(void *) git_delta( GIT_INLINE(int) git_delta(
const void *src_buf, unsigned long src_bufsize, void **out, size_t *out_len,
const void *trg_buf, unsigned long trg_bufsize, const void *src_buf, size_t src_bufsize,
unsigned long *delta_size, const void *trg_buf, size_t trg_bufsize,
unsigned long max_delta_size) size_t max_delta_size)
{ {
struct git_delta_index *index = git_delta_create_index(src_buf, src_bufsize); git_delta_index *index;
int error = 0;
*out = NULL;
*out_len = 0;
if ((error = git_delta_index_init(&index, src_buf, src_bufsize)) < 0)
return error;
if (index) { if (index) {
void *delta = git_delta_create( error = git_delta_create_from_index(out, out_len,
index, trg_buf, trg_bufsize, delta_size, max_delta_size); index, trg_buf, trg_bufsize, max_delta_size);
git_delta_free_index(index);
return delta; git_delta_index_free(index);
} }
return NULL;
}
/* return error;
* 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);
/* the smallest possible delta size is 4 bytes */ /* the smallest possible delta size is 4 bytes */
#define GIT_DELTA_SIZE_MIN 4 #define GIT_DELTA_SIZE_MIN 4
/* /**
* This must be called twice on the delta data buffer, first to get the * Apply a git binary delta to recover the original content.
* expected source buffer size, and again to get the target buffer size. * 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( extern int git_delta_read_header_fromstream(
const unsigned char **datap, const unsigned char *top) size_t *base_out,
{ size_t *result_out,
const unsigned char *data = *datap; git_packfile_stream *stream);
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;
}
#endif #endif
...@@ -22,67 +22,30 @@ ...@@ -22,67 +22,30 @@
#define DIFF_OLD_PREFIX_DEFAULT "a/" #define DIFF_OLD_PREFIX_DEFAULT "a/"
#define DIFF_NEW_PREFIX_DEFAULT "b/" #define DIFF_NEW_PREFIX_DEFAULT "b/"
enum { typedef enum {
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */ GIT_DIFF_TYPE_UNKNOWN = 0,
GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */ GIT_DIFF_TYPE_GENERATED = 1,
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */ GIT_DIFF_TYPE_PARSED = 2,
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */ } git_diff_origin_t;
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)
struct git_diff { struct git_diff {
git_refcount rc; git_refcount rc;
git_repository *repo; git_repository *repo;
git_diff_origin_t type;
git_diff_options opts; git_diff_options opts;
git_vector pathspec;
git_vector deltas; /* vector of git_diff_delta */ git_vector deltas; /* vector of git_diff_delta */
git_pool pool; git_pool pool;
git_iterator_type_t old_src; git_iterator_type_t old_src;
git_iterator_type_t new_src; git_iterator_type_t new_src;
uint32_t diffcaps;
git_diff_perfdata perf; git_diff_perfdata perf;
bool index_updated;
int (*strcomp)(const char *, const char *); int (*strcomp)(const char *, const char *);
int (*strncomp)(const char *, const char *, size_t); int (*strncomp)(const char *, const char *, size_t);
int (*pfxcomp)(const char *str, const char *pfx); int (*pfxcomp)(const char *str, const char *pfx);
int (*entrycomp)(const void *a, const void *b); int (*entrycomp)(const void *a, const void *b);
};
extern void git_diff__cleanup_modes(
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
extern void git_diff_addref(git_diff *diff); void (*free_fn)(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( extern int git_diff_delta__format_file_header(
git_buf *out, git_buf *out,
...@@ -91,84 +54,11 @@ extern int git_diff_delta__format_file_header( ...@@ -91,84 +54,11 @@ extern int git_diff_delta__format_file_header(
const char *newpfx, const char *newpfx,
int oid_strlen); int oid_strlen);
extern int git_diff__oid_for_file( extern int git_diff_delta__cmp(const void *a, const void *b);
git_oid *out, git_diff *, const char *, uint16_t, git_off_t); extern int git_diff_delta__casecmp(const void *a, const void *b);
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__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 #endif
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include "git2/attr.h" #include "git2/attr.h"
#include "diff.h" #include "diff.h"
#include "diff_patch.h"
#include "diff_driver.h" #include "diff_driver.h"
#include "strmap.h" #include "strmap.h"
#include "map.h" #include "map.h"
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "git2/blob.h" #include "git2/blob.h"
#include "git2/submodule.h" #include "git2/submodule.h"
#include "diff.h" #include "diff.h"
#include "diff_generate.h"
#include "diff_file.h" #include "diff_file.h"
#include "odb.h" #include "odb.h"
#include "fileops.h" #include "fileops.h"
...@@ -149,12 +150,14 @@ int git_diff_file_content__init_from_src( ...@@ -149,12 +150,14 @@ int git_diff_file_content__init_from_src(
if (src->blob) { if (src->blob) {
fc->file->size = git_blob_rawsize(src->blob); fc->file->size = git_blob_rawsize(src->blob);
git_oid_cpy(&fc->file->id, git_blob_id(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.len = (size_t)fc->file->size;
fc->map.data = (char *)git_blob_rawcontent(src->blob); fc->map.data = (char *)git_blob_rawcontent(src->blob);
} else { } else {
fc->file->size = src->buflen; fc->file->size = src->buflen;
git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB); 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.len = src->buflen;
fc->map.data = (char *)src->buf; fc->map.data = (char *)src->buf;
......
/*
* 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
/*
* 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;
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include "common.h" #include "common.h"
#include "vector.h" #include "vector.h"
#include "diff.h" #include "diff.h"
#include "diff_patch.h" #include "patch_generate.h"
#define DIFF_RENAME_FILE_SEPARATOR " => " #define DIFF_RENAME_FILE_SEPARATOR " => "
#define STATS_FULL_MIN_SCALE 7 #define STATS_FULL_MIN_SCALE 7
...@@ -190,8 +190,9 @@ int git_diff_get_stats( ...@@ -190,8 +190,9 @@ int git_diff_get_stats(
break; break;
/* keep a count of renames because it will affect formatting */ /* 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); namelen = strlen(delta->new_file.path);
if (strcmp(delta->old_file.path, delta->new_file.path) != 0) { if (strcmp(delta->old_file.path, delta->new_file.path) != 0) {
namelen += strlen(delta->old_file.path); namelen += strlen(delta->old_file.path);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "git2/sys/hashsig.h" #include "git2/sys/hashsig.h"
#include "diff.h" #include "diff.h"
#include "diff_generate.h"
#include "path.h" #include "path.h"
#include "fileops.h" #include "fileops.h"
#include "config.h" #include "config.h"
......
/*
* 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 @@ ...@@ -8,8 +8,8 @@
#include "common.h" #include "common.h"
#include "diff.h" #include "diff.h"
#include "diff_driver.h" #include "diff_driver.h"
#include "diff_patch.h"
#include "diff_xdiff.h" #include "diff_xdiff.h"
#include "patch_generate.h"
static int git_xdiff_scan_int(const char **str, int *value) static int git_xdiff_scan_int(const char **str, int *value)
{ {
...@@ -56,7 +56,7 @@ fail: ...@@ -56,7 +56,7 @@ fail:
typedef struct { typedef struct {
git_xdiff_output *xo; git_xdiff_output *xo;
git_patch *patch; git_patch_generated *patch;
git_diff_hunk hunk; git_diff_hunk hunk;
int old_lineno, new_lineno; int old_lineno, new_lineno;
mmfile_t xd_old_data, xd_new_data; mmfile_t xd_old_data, xd_new_data;
...@@ -110,9 +110,9 @@ static int diff_update_lines( ...@@ -110,9 +110,9 @@ static int diff_update_lines(
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
{ {
git_xdiff_info *info = priv; git_xdiff_info *info = priv;
git_patch *patch = info->patch; git_patch_generated *patch = info->patch;
const git_diff_delta *delta = git_patch_get_delta(patch); const git_diff_delta *delta = patch->base.delta;
git_diff_output *output = &info->xo->output; git_patch_generated_output *output = &info->xo->output;
git_diff_line line; git_diff_line line;
if (len == 1) { if (len == 1) {
...@@ -181,7 +181,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) ...@@ -181,7 +181,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
return output->error; 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_output *xo = (git_xdiff_output *)output;
git_xdiff_info info; git_xdiff_info info;
...@@ -194,7 +194,7 @@ static int git_xdiff(git_diff_output *output, git_patch *patch) ...@@ -194,7 +194,7 @@ static int git_xdiff(git_diff_output *output, git_patch *patch)
xo->callback.priv = &info; xo->callback.priv = &info;
git_diff_find_context_init( 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; xo->config.find_func_priv = &findctxt;
if (xo->config.find_func != NULL) if (xo->config.find_func != NULL)
...@@ -206,8 +206,8 @@ static int git_xdiff(git_diff_output *output, git_patch *patch) ...@@ -206,8 +206,8 @@ static int git_xdiff(git_diff_output *output, git_patch *patch)
* updates are needed to xo->params.flags * updates are needed to xo->params.flags
*/ */
git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch); git_patch_generated_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_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE || if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE ||
info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) { info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) {
......
...@@ -8,20 +8,20 @@ ...@@ -8,20 +8,20 @@
#define INCLUDE_diff_xdiff_h__ #define INCLUDE_diff_xdiff_h__
#include "diff.h" #include "diff.h"
#include "diff_patch.h"
#include "xdiff/xdiff.h" #include "xdiff/xdiff.h"
#include "patch_generate.h"
/* xdiff cannot cope with large files. these files should not be passed to /* xdiff cannot cope with large files. these files should not be passed to
* xdiff. callers should treat these large files as binary. * xdiff. callers should treat these large files as binary.
*/ */
#define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023) #define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023)
/* A git_xdiff_output is a git_diff_output with extra fields necessary /* A git_xdiff_output is a git_patch_generate_output with extra fields
* to use libxdiff. Calling git_xdiff_init() will set the diff_cb field * necessary to use libxdiff. Calling git_xdiff_init() will set the diff_cb
* of the output to use xdiff to generate the diffs. * field of the output to use xdiff to generate the diffs.
*/ */
typedef struct { typedef struct {
git_diff_output output; git_patch_generated_output output;
xdemitconf_t config; xdemitconf_t config;
xpparam_t params; xpparam_t params;
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include "iterator.h" #include "iterator.h"
#include "refs.h" #include "refs.h"
#include "diff.h" #include "diff.h"
#include "diff_generate.h"
#include "diff_tform.h"
#include "checkout.h" #include "checkout.h"
#include "tree.h" #include "tree.h"
#include "blob.h" #include "blob.h"
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "fileops.h" #include "fileops.h"
#include "hash.h" #include "hash.h"
#include "odb.h" #include "odb.h"
#include "delta-apply.h" #include "delta.h"
#include "filter.h" #include "filter.h"
#include "repository.h" #include "repository.h"
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "fileops.h" #include "fileops.h"
#include "hash.h" #include "hash.h"
#include "odb.h" #include "odb.h"
#include "delta-apply.h" #include "delta.h"
#include "filebuf.h" #include "filebuf.h"
#include "git2/odb_backend.h" #include "git2/odb_backend.h"
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include "fileops.h" #include "fileops.h"
#include "hash.h" #include "hash.h"
#include "odb.h" #include "odb.h"
#include "delta-apply.h" #include "delta.h"
#include "sha1_lookup.h" #include "sha1_lookup.h"
#include "mwindow.h" #include "mwindow.h"
#include "pack.h" #include "pack.h"
......
...@@ -144,7 +144,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo) ...@@ -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 */ pb->nr_threads = 1; /* do not spawn any thread by default */
if (git_hash_ctx_init(&pb->ctx) < 0 || 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 || git_repository_odb(&pb->odb, repo) < 0 ||
packbuilder_config(pb) < 0) packbuilder_config(pb) < 0)
goto on_error; goto on_error;
...@@ -274,6 +274,7 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po) ...@@ -274,6 +274,7 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po)
git_odb_object *src = NULL, *trg = NULL; git_odb_object *src = NULL, *trg = NULL;
unsigned long delta_size; unsigned long delta_size;
void *delta_buf; void *delta_buf;
int error;
*out = NULL; *out = NULL;
...@@ -281,12 +282,15 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po) ...@@ -281,12 +282,15 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po)
git_odb_read(&trg, odb, &po->id) < 0) git_odb_read(&trg, odb, &po->id) < 0)
goto on_error; goto on_error;
delta_buf = git_delta( error = git_delta(&delta_buf, &delta_size,
git_odb_object_data(src), (unsigned long)git_odb_object_size(src), git_odb_object_data(src), git_odb_object_size(src),
git_odb_object_data(trg), (unsigned long)git_odb_object_size(trg), git_odb_object_data(trg), git_odb_object_size(trg),
&delta_size, 0); 0);
if (error < 0 && error != GIT_EBUFS)
goto on_error;
if (!delta_buf || delta_size != po->delta_size) { if (error == GIT_EBUFS || delta_size != po->delta_size) {
giterr_set(GITERR_INVALID, "Delta size changed"); giterr_set(GITERR_INVALID, "Delta size changed");
goto on_error; goto on_error;
} }
...@@ -815,16 +819,14 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg, ...@@ -815,16 +819,14 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg,
*mem_usage += sz; *mem_usage += sz;
} }
if (!src->index) { if (!src->index) {
src->index = git_delta_create_index(src->data, src_size); if (git_delta_index_init(&src->index, src->data, src_size) < 0)
if (!src->index)
return 0; /* suboptimal pack - out of memory */ 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, if (git_delta_create_from_index(&delta_buf, &delta_size, src->index, trg->data, trg_size,
&delta_size, max_size); max_size) < 0)
if (!delta_buf)
return 0; return 0;
if (trg_object->delta) { if (trg_object->delta) {
...@@ -885,9 +887,14 @@ static unsigned int check_delta_limit(git_pobject *me, unsigned int n) ...@@ -885,9 +887,14 @@ static unsigned int check_delta_limit(git_pobject *me, unsigned int n)
static unsigned long free_unpacked(struct unpacked *n) static unsigned long free_unpacked(struct unpacked *n)
{ {
unsigned long freed_mem = git_delta_sizeof_index(n->index); unsigned long freed_mem = 0;
git_delta_free_index(n->index);
if (n->index) {
freed_mem += git_delta_index_size(n->index);
git_delta_index_free(n->index);
}
n->index = NULL; n->index = NULL;
if (n->data) { if (n->data) {
freed_mem += (unsigned long)n->object->size; freed_mem += (unsigned long)n->object->size;
git__free(n->data); git__free(n->data);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include "common.h" #include "common.h"
#include "odb.h" #include "odb.h"
#include "pack.h" #include "pack.h"
#include "delta-apply.h" #include "delta.h"
#include "sha1_lookup.h" #include "sha1_lookup.h"
#include "mwindow.h" #include "mwindow.h"
#include "fileops.h" #include "fileops.h"
...@@ -505,7 +505,7 @@ int git_packfile_resolve_header( ...@@ -505,7 +505,7 @@ int git_packfile_resolve_header(
git_mwindow_close(&w_curs); git_mwindow_close(&w_curs);
if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0) if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0)
return error; 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); git_packfile_stream_free(&stream);
if (error < 0) if (error < 0)
return error; return error;
...@@ -730,8 +730,9 @@ int git_packfile_unpack( ...@@ -730,8 +730,9 @@ int git_packfile_unpack(
obj->len = 0; obj->len = 0;
obj->type = GIT_OBJ_BAD; 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; obj->type = base_type;
/* /*
* We usually don't want to free the base at this * We usually don't want to free the base at this
* point, as we put it into the cache in the previous * point, as we put it into the cache in the previous
......
#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);
}
/*
* 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
...@@ -4,66 +4,48 @@ ...@@ -4,66 +4,48 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with * This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#ifndef INCLUDE_diff_patch_h__ #ifndef INCLUDE_patch_generate_h__
#define INCLUDE_diff_patch_h__ #define INCLUDE_patch_generate_h__
#include "common.h" #include "common.h"
#include "diff.h" #include "diff.h"
#include "diff_file.h" #include "diff_file.h"
#include "array.h" #include "patch.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 { enum {
GIT_DIFF_PATCH_ALLOCATED = (1 << 0), GIT_PATCH_GENERATED_ALLOCATED = (1 << 0),
GIT_DIFF_PATCH_INITIALIZED = (1 << 1), GIT_PATCH_GENERATED_INITIALIZED = (1 << 1),
GIT_DIFF_PATCH_LOADED = (1 << 2), GIT_PATCH_GENERATED_LOADED = (1 << 2),
/* the two sides are different */ /* the two sides are different */
GIT_DIFF_PATCH_DIFFABLE = (1 << 3), GIT_PATCH_GENERATED_DIFFABLE = (1 << 3),
/* the difference between the two sides has been computed */ /* the difference between the two sides has been computed */
GIT_DIFF_PATCH_DIFFED = (1 << 4), GIT_PATCH_GENERATED_DIFFED = (1 << 4),
GIT_DIFF_PATCH_FLATTENED = (1 << 5), GIT_PATCH_GENERATED_FLATTENED = (1 << 5),
}; };
struct git_patch { struct git_patch_generated {
git_refcount rc; struct git_patch base;
git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */ git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
git_diff_options diff_opts;
git_diff_delta *delta;
size_t delta_index; size_t delta_index;
git_diff_file_content ofile; git_diff_file_content ofile;
git_diff_file_content nfile; git_diff_file_content nfile;
uint32_t flags; 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; git_pool flattened;
}; };
extern git_diff *git_patch__diff(git_patch *); typedef struct git_patch_generated git_patch_generated;
extern git_diff_driver *git_patch__driver(git_patch *); extern git_diff_driver *git_patch_generated_driver(git_patch_generated *);
extern void git_patch__old_data(char **, size_t *, git_patch *); extern void git_patch_generated_old_data(
extern void git_patch__new_data(char **, size_t *, git_patch *); char **, size_t *, git_patch_generated *);
extern void git_patch_generated_new_data(
char **, size_t *, git_patch_generated *);
extern int git_patch__invoke_callbacks( typedef struct git_patch_generated_output git_patch_generated_output;
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_patch_generated_output {
struct git_diff_output {
/* these callbacks are issued with the diff data */ /* these callbacks are issued with the diff data */
git_diff_file_cb file_cb; git_diff_file_cb file_cb;
git_diff_binary_cb binary_cb; git_diff_binary_cb binary_cb;
...@@ -77,7 +59,8 @@ struct git_diff_output { ...@@ -77,7 +59,8 @@ struct git_diff_output {
/* this callback is used to do the diff and drive the other callbacks. /* 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. * see diff_xdiff.h for how to use this in practice for now.
*/ */
int (*diff_cb)(git_diff_output *output, git_patch *patch); int (*diff_cb)(git_patch_generated_output *output,
git_patch_generated *patch);
}; };
#endif #endif
/*
* 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
...@@ -306,6 +306,25 @@ int git_path_join_unrooted( ...@@ -306,6 +306,25 @@ int git_path_join_unrooted(
return 0; 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) int git_path_prettify(git_buf *path_out, const char *path, const char *base)
{ {
char buf[GIT_PATH_MAX]; char buf[GIT_PATH_MAX];
......
...@@ -244,6 +244,12 @@ extern int git_path_join_unrooted( ...@@ -244,6 +244,12 @@ extern int git_path_join_unrooted(
git_buf *path_out, const char *path, const char *base, ssize_t *root_at); 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. * Clean up path, prepending base if it is not already rooted.
*/ */
extern int git_path_prettify(git_buf *path_out, const char *path, const char *base); extern int git_path_prettify(git_buf *path_out, const char *path, const char *base);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "iterator.h" #include "iterator.h"
#include "merge.h" #include "merge.h"
#include "diff.h" #include "diff.h"
#include "diff_generate.h"
static int create_error(int error, const char *msg) static int create_error(int error, const char *msg)
{ {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "git2/diff.h" #include "git2/diff.h"
#include "diff.h" #include "diff.h"
#include "diff_generate.h"
static unsigned int index_delta2status(const git_diff_delta *head2idx) static unsigned int index_delta2status(const git_diff_delta *head2idx)
{ {
......
...@@ -66,6 +66,12 @@ int git_strarray_copy(git_strarray *tgt, const git_strarray *src) ...@@ -66,6 +66,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) 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; const char *p;
int64_t n, nn; int64_t n, nn;
int c, ovfl, v, neg, ndig; int c, ovfl, v, neg, ndig;
...@@ -111,7 +117,7 @@ int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int ba ...@@ -111,7 +117,7 @@ int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int ba
/* /*
* Non-empty sequence of digits * Non-empty sequence of digits
*/ */
for (;; p++,ndig++) { for (; nptr_len > 0; p++,ndig++,nptr_len--) {
c = *p; c = *p;
v = base; v = base;
if ('0'<=c && c<='9') if ('0'<=c && c<='9')
...@@ -148,11 +154,17 @@ Return: ...@@ -148,11 +154,17 @@ Return:
int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base) 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; int error;
int32_t tmp_int; int32_t tmp_int;
int64_t tmp_long; 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; return error;
tmp_int = tmp_long & 0xFFFFFFFF; tmp_int = tmp_long & 0xFFFFFFFF;
...@@ -321,6 +333,12 @@ char *git__strsep(char **end, const char *sep) ...@@ -321,6 +333,12 @@ char *git__strsep(char **end, const char *sep)
return NULL; 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) void git__hexdump(const char *buffer, size_t len)
{ {
static const size_t LINE_WIDTH = 16; static const size_t LINE_WIDTH = 16;
......
...@@ -263,7 +263,10 @@ GIT_INLINE(int) git__signum(int val) ...@@ -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__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__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 void git__hexdump(const char *buffer, size_t n);
extern uint32_t git__hash(const void *key, int len, uint32_t seed); extern uint32_t git__hash(const void *key, int len, uint32_t seed);
...@@ -290,6 +293,8 @@ GIT_INLINE(int) git__tolower(int c) ...@@ -290,6 +293,8 @@ GIT_INLINE(int) git__tolower(int c)
# define git__tolower(a) tolower(a) # define git__tolower(a) tolower(a)
#endif #endif
extern size_t git__linenlen(const char *buffer, size_t buffer_len);
GIT_INLINE(const char *) git__next_line(const char *s) GIT_INLINE(const char *) git__next_line(const char *s)
{ {
while (*s && *s != '\n') s++; while (*s && *s != '\n') s++;
...@@ -466,6 +471,11 @@ GIT_INLINE(bool) git__iswildcard(int c) ...@@ -466,6 +471,11 @@ GIT_INLINE(bool) git__iswildcard(int c)
return (c == '*' || c == '?' || 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. * Parse a string value as a boolean, just like Core Git does.
* *
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "common.h" #include "common.h"
#include "vector.h" #include "vector.h"
#include "integer.h"
/* In elements, not bytes */ /* In elements, not bytes */
#define MIN_ALLOCSIZE 8 #define MIN_ALLOCSIZE 8
...@@ -330,6 +331,47 @@ int git_vector_resize_to(git_vector *v, size_t new_length) ...@@ -330,6 +331,47 @@ int git_vector_resize_to(git_vector *v, size_t new_length)
return 0; 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) int git_vector_set(void **old, git_vector *v, size_t position, void *value)
{ {
if (position + 1 > v->length) { if (position + 1 > v->length) {
......
...@@ -93,6 +93,9 @@ void git_vector_remove_matching( ...@@ -93,6 +93,9 @@ void git_vector_remove_matching(
void *payload); void *payload);
int git_vector_resize_to(git_vector *v, size_t new_length); int git_vector_resize_to(git_vector *v, size_t new_length);
int git_vector_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); int git_vector_set(void **old, git_vector *v, size_t position, void *value);
/** Check if vector is sorted */ /** Check if vector is sorted */
......
...@@ -28,20 +28,31 @@ static int zstream_seterr(git_zstream *zs) ...@@ -28,20 +28,31 @@ static int zstream_seterr(git_zstream *zs)
return -1; 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); return zstream_seterr(zstream);
} }
void git_zstream_free(git_zstream *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) 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 = NULL;
zstream->in_len = 0; zstream->in_len = 0;
zstream->zerr = Z_STREAM_END; zstream->zerr = Z_STREAM_END;
...@@ -75,6 +86,11 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream) ...@@ -75,6 +86,11 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
int zflush = Z_FINISH; int zflush = Z_FINISH;
size_t out_remain = *out_len; 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) { while (out_remain > 0 && zstream->zerr != Z_STREAM_END) {
size_t out_queued, in_queued, out_used, in_used; 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) ...@@ -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; out_queued = (size_t)zstream->z.avail_out;
/* compress next chunk */ /* 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) if (zstream->zerr == Z_STREAM_ERROR)
return zstream_seterr(zstream); return zstream_seterr(zstream);
...@@ -120,12 +139,12 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream) ...@@ -120,12 +139,12 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
return 0; 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; git_zstream zs = GIT_ZSTREAM_INIT;
int error = 0; int error = 0;
if ((error = git_zstream_init(&zs)) < 0) if ((error = git_zstream_init(&zs, type)) < 0)
return error; return error;
if ((error = git_zstream_set_input(&zs, in, in_len)) < 0) if ((error = git_zstream_set_input(&zs, in, in_len)) < 0)
...@@ -154,3 +173,13 @@ done: ...@@ -154,3 +173,13 @@ done:
git_zstream_free(&zs); git_zstream_free(&zs);
return error; 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 @@ ...@@ -12,8 +12,14 @@
#include "common.h" #include "common.h"
#include "buffer.h" #include "buffer.h"
typedef enum {
GIT_ZSTREAM_INFLATE,
GIT_ZSTREAM_DEFLATE,
} git_zstream_t;
typedef struct { typedef struct {
z_stream z; z_stream z;
git_zstream_t type;
const char *in; const char *in;
size_t in_len; size_t in_len;
int zerr; int zerr;
...@@ -21,7 +27,7 @@ typedef struct { ...@@ -21,7 +27,7 @@ typedef struct {
#define GIT_ZSTREAM_INIT {{0}} #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); void git_zstream_free(git_zstream *zstream);
int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len); 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); ...@@ -35,5 +41,6 @@ bool git_zstream_done(git_zstream *zstream);
void git_zstream_reset(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_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__ */ #endif /* INCLUDE_zstream_h__ */
#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));
}
#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) ...@@ -813,6 +813,44 @@ void test_core_buffer__encode_base85(void)
git_buf_free(&buf); 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) void test_core_buffer__classify_with_utf8(void)
{ {
char *data0 = "Simple text\n"; char *data0 = "Simple text\n";
......
...@@ -274,3 +274,105 @@ void test_core_vector__remove_matching(void) ...@@ -274,3 +274,105 @@ void test_core_vector__remove_matching(void)
git_vector_free(&x); 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) ...@@ -48,7 +48,7 @@ void test_core_zstream__basic(void)
char out[128]; char out[128];
size_t outlen = sizeof(out); 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_set_input(&z, data, strlen(data) + 1));
cl_git_pass(git_zstream_get_output(out, &outlen, &z)); cl_git_pass(git_zstream_get_output(out, &outlen, &z));
cl_assert(git_zstream_done(&z)); cl_assert(git_zstream_done(&z));
...@@ -58,6 +58,25 @@ void test_core_zstream__basic(void) ...@@ -58,6 +58,25 @@ void test_core_zstream__basic(void)
assert_zlib_equal(data, strlen(data) + 1, out, outlen); 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) void test_core_zstream__buffer(void)
{ {
git_buf out = GIT_BUF_INIT; git_buf out = GIT_BUF_INIT;
...@@ -68,9 +87,10 @@ void test_core_zstream__buffer(void) ...@@ -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" #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 out1 = GIT_BUF_INIT, out2 = GIT_BUF_INIT;
git_buf inflated = GIT_BUF_INIT;
size_t i, fixed_size = max(input->size / 2, 256); size_t i, fixed_size = max(input->size / 2, 256);
char *fixed = git__malloc(fixed_size); char *fixed = git__malloc(fixed_size);
cl_assert(fixed); cl_assert(fixed);
...@@ -93,7 +113,7 @@ static void compress_input_various_ways(git_buf *input) ...@@ -93,7 +113,7 @@ static void compress_input_various_ways(git_buf *input)
} }
cl_assert(use_fixed_size <= fixed_size); 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)); cl_git_pass(git_zstream_set_input(&zs, input->ptr, input->size));
while (!git_zstream_done(&zs)) { while (!git_zstream_done(&zs)) {
...@@ -112,7 +132,12 @@ static void compress_input_various_ways(git_buf *input) ...@@ -112,7 +132,12 @@ static void compress_input_various_ways(git_buf *input)
git_buf_free(&out2); 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(&out1);
git_buf_free(&inflated);
git__free(fixed); git__free(fixed);
} }
...@@ -129,14 +154,14 @@ void test_core_zstream__big_data(void) ...@@ -129,14 +154,14 @@ void test_core_zstream__big_data(void)
cl_git_pass( cl_git_pass(
git_buf_put(&in, BIG_STRING_PART, strlen(BIG_STRING_PART))); 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 */ /* make a big string that's hard to compress */
srand(0xabad1dea); srand(0xabad1dea);
for (scan = 0; scan < in.size; ++scan) for (scan = 0; scan < in.size; ++scan)
in.ptr[scan] = (char)rand(); in.ptr[scan] = (char)rand();
compress_input_various_ways(&in); compress_and_decompress_input_various_ways(&in);
} }
git_buf_free(&in); git_buf_free(&in);
......
...@@ -241,3 +241,76 @@ void diff_print_raw(FILE *fp, git_diff *diff) ...@@ -241,3 +241,76 @@ void diff_print_raw(FILE *fp, git_diff *diff)
git_diff_print(diff, GIT_DIFF_FORMAT_RAW, git_diff_print(diff, GIT_DIFF_FORMAT_RAW,
git_diff_print_callback__to_file_handle, fp ? fp : stderr)); 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( ...@@ -68,3 +68,6 @@ extern int diff_foreach_via_iterator(
extern void diff_print(FILE *fp, git_diff *diff); extern void diff_print(FILE *fp, git_diff *diff);
extern void diff_print_raw(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 @@ ...@@ -4,6 +4,7 @@
#include "buffer.h" #include "buffer.h"
#include "commit.h" #include "commit.h"
#include "diff.h" #include "diff.h"
#include "diff_generate.h"
static git_repository *repo; static git_repository *repo;
...@@ -350,9 +351,6 @@ void test_diff_format_email__mode_change(void) ...@@ -350,9 +351,6 @@ void test_diff_format_email__mode_change(void)
"diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \ "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
"old mode 100644\n" \ "old mode 100644\n" \
"new mode 100755\n" \ "new mode 100755\n" \
"index a97157a..a97157a\n" \
"--- a/file1.txt.renamed\n" \
"+++ b/file1.txt.renamed\n" \
"--\n" \ "--\n" \
"libgit2 " LIBGIT2_VERSION "\n" \ "libgit2 " LIBGIT2_VERSION "\n" \
"\n"; "\n";
......
#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 ...@@ -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", 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); 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 @@ ...@@ -4,6 +4,7 @@
#include "buffer.h" #include "buffer.h"
#include "commit.h" #include "commit.h"
#include "diff.h" #include "diff.h"
#include "diff_generate.h"
static git_repository *_repo; static git_repository *_repo;
static git_diff_stats *_stats; static git_diff_stats *_stats;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "merge.h" #include "merge.h"
#include "../merge_helpers.h" #include "../merge_helpers.h"
#include "diff.h" #include "diff.h"
#include "diff_tform.h"
#include "git2/sys/hashsig.h" #include "git2/sys/hashsig.h"
static git_repository *repo; static git_repository *repo;
......
#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));
}
#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));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment