mirror of
https://git.proxmox.com/git/libgit2
synced 2025-10-14 17:53:31 +00:00
Merge pull request #941 from arrbee/diff-separate-iterators
Create a diff patch object as a replacement for iterators
This commit is contained in:
commit
b55fd885fc
@ -122,7 +122,7 @@ typedef enum {
|
||||
*/
|
||||
typedef struct {
|
||||
git_oid oid;
|
||||
char *path;
|
||||
const char *path;
|
||||
git_off_t size;
|
||||
unsigned int flags;
|
||||
uint16_t mode;
|
||||
@ -154,7 +154,7 @@ typedef struct {
|
||||
*/
|
||||
typedef int (*git_diff_file_fn)(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
const git_diff_delta *delta,
|
||||
float progress);
|
||||
|
||||
/**
|
||||
@ -172,8 +172,8 @@ typedef struct {
|
||||
*/
|
||||
typedef int (*git_diff_hunk_fn)(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
const char *header,
|
||||
size_t header_len);
|
||||
|
||||
@ -213,16 +213,20 @@ enum {
|
||||
*/
|
||||
typedef int (*git_diff_data_fn)(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char line_origin, /**< GIT_DIFF_LINE_... value from above */
|
||||
const char *content,
|
||||
size_t content_len);
|
||||
|
||||
/**
|
||||
* The diff iterator object is used to scan a diff list.
|
||||
* The diff patch is used to store all the text diffs for a delta.
|
||||
*
|
||||
* You can easily loop over the content of patches and get information about
|
||||
* them.
|
||||
*/
|
||||
typedef struct git_diff_iterator git_diff_iterator;
|
||||
typedef struct git_diff_patch git_diff_patch;
|
||||
|
||||
|
||||
/** @name Diff List Generator Functions
|
||||
*
|
||||
@ -349,7 +353,7 @@ GIT_EXTERN(int) git_diff_merge(
|
||||
/**@{*/
|
||||
|
||||
/**
|
||||
* Iterate over a diff list issuing callbacks.
|
||||
* Loop over all deltas in a diff list issuing callbacks.
|
||||
*
|
||||
* This will iterate through all of the files described in a diff. You
|
||||
* should provide a file callback to learn about each file.
|
||||
@ -380,137 +384,6 @@ GIT_EXTERN(int) git_diff_foreach(
|
||||
git_diff_hunk_fn hunk_cb,
|
||||
git_diff_data_fn line_cb);
|
||||
|
||||
/**
|
||||
* Create a diff iterator object that can be used to traverse a diff.
|
||||
*
|
||||
* This iterator can be used instead of `git_diff_foreach` in situations
|
||||
* where callback functions are awkward to use. Because of the way that
|
||||
* diffs are calculated internally, using an iterator will use somewhat
|
||||
* more memory than `git_diff_foreach` would.
|
||||
*
|
||||
* @param iterator Output parameter of newly created iterator.
|
||||
* @param diff Diff over which you wish to iterate.
|
||||
* @return 0 on success, < 0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_iterator_new(
|
||||
git_diff_iterator **iterator,
|
||||
git_diff_list *diff);
|
||||
|
||||
/**
|
||||
* Release the iterator object.
|
||||
*
|
||||
* Call this when you are done using the iterator.
|
||||
*
|
||||
* @param iterator The diff iterator to be freed.
|
||||
*/
|
||||
GIT_EXTERN(void) git_diff_iterator_free(git_diff_iterator *iterator);
|
||||
|
||||
/**
|
||||
* Return progress value for traversing the diff.
|
||||
*
|
||||
* This returns a value between 0.0 and 1.0 that represents the progress
|
||||
* through the diff iterator. The value is monotonically increasing and
|
||||
* will advance gradually as you progress through the iteration.
|
||||
*
|
||||
* @param iterator The diff iterator
|
||||
* @return Value between 0.0 and 1.0
|
||||
*/
|
||||
GIT_EXTERN(float) git_diff_iterator_progress(git_diff_iterator *iterator);
|
||||
|
||||
/**
|
||||
* Return the number of hunks in the current file
|
||||
*
|
||||
* This will return the number of diff hunks in the current file. If the
|
||||
* diff has not been performed yet, this may result in loading the file and
|
||||
* performing the diff.
|
||||
*
|
||||
* @param iterator The iterator object
|
||||
* @return The number of hunks in the current file or <0 on loading failure
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_iterator_num_hunks_in_file(git_diff_iterator *iterator);
|
||||
|
||||
/**
|
||||
* Return the number of lines in the hunk currently being examined.
|
||||
*
|
||||
* This will return the number of lines in the current hunk. If the diff
|
||||
* has not been performed yet, this may result in loading the file and
|
||||
* performing the diff.
|
||||
*
|
||||
* @param iterator The iterator object
|
||||
* @return The number of lines in the current hunk (context, added, and
|
||||
* removed all added together) or <0 on loading failure
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_iterator_num_lines_in_hunk(git_diff_iterator *iterator);
|
||||
|
||||
/**
|
||||
* Return the delta information for the next file in the diff.
|
||||
*
|
||||
* This will return a pointer to the next git_diff_delta` to be processed or
|
||||
* NULL if the iterator is at the end of the diff, then advance. This
|
||||
* returns the value `GIT_ITEROVER` after processing the last file.
|
||||
*
|
||||
* @param delta Output parameter for the next delta object
|
||||
* @param iterator The iterator object
|
||||
* @return 0 on success, GIT_ITEROVER when done, other value < 0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_iterator_next_file(
|
||||
git_diff_delta **delta,
|
||||
git_diff_iterator *iterator);
|
||||
|
||||
/**
|
||||
* Return the hunk information for the next hunk in the current file.
|
||||
*
|
||||
* It is recommended that you not call this if the file is a binary
|
||||
* file, but it is allowed to do so.
|
||||
*
|
||||
* The `header` text output will contain the standard hunk header that
|
||||
* would appear in diff output. The header string will be NUL terminated.
|
||||
*
|
||||
* WARNING! Call this function for the first time on a file is when the
|
||||
* actual text diff will be computed (it cannot be computed incrementally)
|
||||
* so the first call for a new file is expensive (at least in relative
|
||||
* terms - in reality, it is still pretty darn fast).
|
||||
*
|
||||
* @param range Output pointer to range of lines covered by the hunk;
|
||||
* This range object is owned by the library and should not be freed.
|
||||
* @param header Output pointer to the text of the hunk header
|
||||
* This string is owned by the library and should not be freed.
|
||||
* @param header_len Output pointer to store the length of the header text
|
||||
* @param iterator The iterator object
|
||||
* @return 0 on success, GIT_ITEROVER when done with current file, other
|
||||
* value < 0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_iterator_next_hunk(
|
||||
git_diff_range **range,
|
||||
const char **header,
|
||||
size_t *header_len,
|
||||
git_diff_iterator *iterator);
|
||||
|
||||
/**
|
||||
* Return the next line of the current hunk of diffs.
|
||||
*
|
||||
* The `line_origin` output will tell you what type of line this is
|
||||
* (e.g. was it added or removed or is it just context for the diff).
|
||||
*
|
||||
* The `content` will be a pointer to the file data that goes in the
|
||||
* line. IT WILL NOT BE NUL TERMINATED. You have to use the `content_len`
|
||||
* value and only process that many bytes of data from the content string.
|
||||
*
|
||||
* @param line_origin Output pointer to store a GIT_DIFF_LINE value for this
|
||||
* next chunk of data. The value is a single character, not a buffer.
|
||||
* @param content Output pointer to store the content of the diff; this
|
||||
* string is owned by the library and should not be freed.
|
||||
* @param content_len Output pointer to store the length of the content.
|
||||
* @param iterator The iterator object
|
||||
* @return 0 on success, GIT_ITEROVER when done with current line, other
|
||||
* value < 0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_iterator_next_line(
|
||||
char *line_origin, /**< GIT_DIFF_LINE_... value from above */
|
||||
const char **content,
|
||||
size_t *content_len,
|
||||
git_diff_iterator *iterator);
|
||||
|
||||
/**
|
||||
* Iterate over a diff generating text output like "git diff --name-status".
|
||||
*
|
||||
@ -527,6 +400,20 @@ GIT_EXTERN(int) git_diff_print_compact(
|
||||
void *cb_data,
|
||||
git_diff_data_fn print_cb);
|
||||
|
||||
/**
|
||||
* Look up the single character abbreviation for a delta status code.
|
||||
*
|
||||
* When you call `git_diff_print_compact` it prints single letter codes into
|
||||
* the output such as 'A' for added, 'D' for deleted, 'M' for modified, etc.
|
||||
* It is sometimes convenient to convert a git_delta_t value into these
|
||||
* letters for your own purposes. This function does just that. By the
|
||||
* way, unmodified will return a space (i.e. ' ').
|
||||
*
|
||||
* @param delta_t The git_delta_t value to look up
|
||||
* @return The single character label for that code
|
||||
*/
|
||||
GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
|
||||
|
||||
/**
|
||||
* Iterate over a diff generating text output like "git diff".
|
||||
*
|
||||
@ -552,17 +439,137 @@ GIT_EXTERN(int) git_diff_print_patch(
|
||||
/**
|
||||
* Query how many diff records are there in a diff list.
|
||||
*
|
||||
* You can optionally pass in a `git_delta_t` value if you want a count
|
||||
* of just entries that match that delta type, or pass -1 for all delta
|
||||
* records.
|
||||
* @param diff A git_diff_list generated by one of the above functions
|
||||
* @return Count of number of deltas in the list
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_diff_num_deltas(git_diff_list *diff);
|
||||
|
||||
/**
|
||||
* Query how many diff deltas are there in a diff list filtered by type.
|
||||
*
|
||||
* This works just like `git_diff_entrycount()` with an extra parameter
|
||||
* that is a `git_delta_t` and returns just the count of how many deltas
|
||||
* match that particular type.
|
||||
*
|
||||
* @param diff A git_diff_list generated by one of the above functions
|
||||
* @param delta_t A git_delta_t value to filter the count, or -1 for all records
|
||||
* @param type A git_delta_t value to filter the count
|
||||
* @return Count of number of deltas matching delta_t type
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_entrycount(
|
||||
GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
|
||||
git_diff_list *diff,
|
||||
int delta_t);
|
||||
git_delta_t type);
|
||||
|
||||
/**
|
||||
* Return the diff delta and patch for an entry in the diff list.
|
||||
*
|
||||
* The `git_diff_patch` is a newly created object contains the text diffs
|
||||
* for the delta. You have to call `git_diff_patch_free()` when you are
|
||||
* done with it. You can use the patch object to loop over all the hunks
|
||||
* and lines in the diff of the one delta.
|
||||
*
|
||||
* For an unchanged file or a binary file, no `git_diff_patch` will be
|
||||
* created, the output will be set to NULL, and the `binary` flag will be
|
||||
* set true in the `git_diff_delta` structure.
|
||||
*
|
||||
* The `git_diff_delta` pointer points to internal data and you do not have
|
||||
* to release it when you are done with it. It will go away when the
|
||||
* `git_diff_list` and `git_diff_patch` go away.
|
||||
*
|
||||
* It is okay to pass NULL for either of the output parameters; if you pass
|
||||
* NULL for the `git_diff_patch`, then the text diff will not be calculated.
|
||||
*
|
||||
* @param patch Output parameter for the delta patch object
|
||||
* @param delta Output parameter for the delta object
|
||||
* @param diff Diff list object
|
||||
* @param idx Index into diff list
|
||||
* @return 0 on success, other value < 0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_get_patch(
|
||||
git_diff_patch **patch,
|
||||
const git_diff_delta **delta,
|
||||
git_diff_list *diff,
|
||||
size_t idx);
|
||||
|
||||
/**
|
||||
* Free a git_diff_patch object.
|
||||
*/
|
||||
GIT_EXTERN(void) git_diff_patch_free(
|
||||
git_diff_patch *patch);
|
||||
|
||||
/**
|
||||
* Get the delta associated with a patch
|
||||
*/
|
||||
GIT_EXTERN(const git_diff_delta *) git_diff_patch_delta(
|
||||
git_diff_patch *patch);
|
||||
|
||||
/**
|
||||
* Get the number of hunks in a patch
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_diff_patch_num_hunks(
|
||||
git_diff_patch *patch);
|
||||
|
||||
/**
|
||||
* Get the information about a hunk in a patch
|
||||
*
|
||||
* Given a patch and a hunk index into the patch, this returns detailed
|
||||
* information about that hunk. Any of the output pointers can be passed
|
||||
* as NULL if you don't care about that particular piece of information.
|
||||
*
|
||||
* @param range Output pointer to git_diff_range of hunk
|
||||
* @param header Output pointer to header string for hunk. Unlike the
|
||||
* content pointer for each line, this will be NUL-terminated
|
||||
* @param header_len Output value of characters in header string
|
||||
* @param lines_in_hunk Output count of total lines in this hunk
|
||||
* @param patch Input pointer to patch object
|
||||
* @param hunk_idx Input index of hunk to get information about
|
||||
* @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_patch_get_hunk(
|
||||
const git_diff_range **range,
|
||||
const char **header,
|
||||
size_t *header_len,
|
||||
size_t *lines_in_hunk,
|
||||
git_diff_patch *patch,
|
||||
size_t hunk_idx);
|
||||
|
||||
/**
|
||||
* Get the number of lines in a hunk.
|
||||
*
|
||||
* @param patch The git_diff_patch object
|
||||
* @param hunk_idx Index of the hunk
|
||||
* @return Number of lines in hunk or -1 if invalid hunk index
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_patch_num_lines_in_hunk(
|
||||
git_diff_patch *patch,
|
||||
size_t hunk_idx);
|
||||
|
||||
/**
|
||||
* Get data about a line in a hunk of a patch.
|
||||
*
|
||||
* Given a patch, a hunk index, and a line index in the hunk, this
|
||||
* will return a lot of details about that line. If you pass a hunk
|
||||
* index larger than the number of hunks or a line index larger than
|
||||
* the number of lines in the hunk, this will return -1.
|
||||
*
|
||||
* @param line_origin A GIT_DIFF_LINE constant from above
|
||||
* @param content Pointer to content of diff line, not NUL-terminated
|
||||
* @param content_len Number of characters in content
|
||||
* @param old_lineno Line number in old file or -1 if line is added
|
||||
* @param new_lineno Line number in new file or -1 if line is deleted
|
||||
* @param patch The patch to look in
|
||||
* @param hunk_idx The index of the hunk
|
||||
* @param line_of_index The index of the line in the hunk
|
||||
* @return 0 on success, <0 on failure
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_patch_get_line_in_hunk(
|
||||
char *line_origin,
|
||||
const char **content,
|
||||
size_t *content_len,
|
||||
int *old_lineno,
|
||||
int *new_lineno,
|
||||
git_diff_patch *patch,
|
||||
size_t hunk_idx,
|
||||
size_t line_of_hunk);
|
||||
|
||||
/**@}*/
|
||||
|
||||
@ -588,7 +595,7 @@ GIT_EXTERN(int) git_diff_entrycount(
|
||||
GIT_EXTERN(int) git_diff_blobs(
|
||||
git_blob *old_blob,
|
||||
git_blob *new_blob,
|
||||
git_diff_options *options,
|
||||
const git_diff_options *options,
|
||||
void *cb_data,
|
||||
git_diff_file_fn file_cb,
|
||||
git_diff_hunk_fn hunk_cb,
|
||||
|
@ -126,7 +126,7 @@ static int blob_content_to_link(git_blob *blob, const char *path, bool can_symli
|
||||
|
||||
static int checkout_blob(
|
||||
git_repository *repo,
|
||||
git_oid *blob_oid,
|
||||
const git_oid *blob_oid,
|
||||
const char *path,
|
||||
mode_t filemode,
|
||||
bool can_symlink,
|
||||
@ -150,7 +150,7 @@ static int checkout_blob(
|
||||
|
||||
static int checkout_diff_fn(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
const git_diff_delta *delta,
|
||||
float progress)
|
||||
{
|
||||
struct checkout_diff_data *data;
|
||||
|
72
src/diff.c
72
src/diff.c
@ -5,8 +5,6 @@
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "git2/diff.h"
|
||||
#include "git2/oid.h"
|
||||
#include "diff.h"
|
||||
#include "fileops.h"
|
||||
#include "config.h"
|
||||
@ -268,9 +266,17 @@ static int diff_delta__from_two(
|
||||
delta->old_file.mode = old_mode;
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
|
||||
git_oid_cpy(&delta->new_file.oid, &new_entry->oid);
|
||||
delta->new_file.size = new_entry->file_size;
|
||||
delta->new_file.mode = new_mode;
|
||||
|
||||
if (new_oid) {
|
||||
if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0)
|
||||
git_oid_cpy(&delta->old_file.oid, new_oid);
|
||||
else
|
||||
git_oid_cpy(&delta->new_file.oid, new_oid);
|
||||
}
|
||||
|
||||
if (new_oid || !git_oid_iszero(&new_entry->oid))
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
@ -425,6 +431,11 @@ void git_diff_list_free(git_diff_list *diff)
|
||||
GIT_REFCOUNT_DEC(diff, diff_list_free);
|
||||
}
|
||||
|
||||
void git_diff_list_addref(git_diff_list *diff)
|
||||
{
|
||||
GIT_REFCOUNT_INC(diff);
|
||||
}
|
||||
|
||||
static int oid_for_workdir_item(
|
||||
git_repository *repo,
|
||||
const git_index_entry *item,
|
||||
@ -519,17 +530,17 @@ static int maybe_modified(
|
||||
omode == nmode)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* if modes match and we have an unknown OID and a workdir iterator,
|
||||
* then check deeper for matching
|
||||
/* if we have an unknown OID and a workdir iterator, then check some
|
||||
* circumstances that can accelerate things or need special handling
|
||||
*/
|
||||
else if (omode == nmode &&
|
||||
git_oid_iszero(&nitem->oid) &&
|
||||
else if (git_oid_iszero(&nitem->oid) &&
|
||||
new_iter->type == GIT_ITERATOR_WORKDIR)
|
||||
{
|
||||
/* TODO: add check against index file st_mtime to avoid racy-git */
|
||||
|
||||
/* if they files look exactly alike, then we'll assume the same */
|
||||
if (oitem->file_size == nitem->file_size &&
|
||||
/* if the stat data looks exactly alike, then assume the same */
|
||||
if (omode == nmode &&
|
||||
oitem->file_size == nitem->file_size &&
|
||||
(!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
|
||||
(oitem->ctime.seconds == nitem->ctime.seconds)) &&
|
||||
oitem->mtime.seconds == nitem->mtime.seconds &&
|
||||
@ -554,16 +565,15 @@ static int maybe_modified(
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: check git attributes so we will not have to read the file
|
||||
* in if it is marked binary.
|
||||
/* if we got here and decided that the files are modified, but we
|
||||
* haven't calculated the OID of the new item, then calculate it now
|
||||
*/
|
||||
|
||||
else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
|
||||
if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) {
|
||||
if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
|
||||
return -1;
|
||||
|
||||
else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
|
||||
omode == nmode)
|
||||
else if (omode == nmode && git_oid_equal(&oitem->oid, &noid))
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* store calculated oid so we don't have to recalc later */
|
||||
@ -797,6 +807,28 @@ on_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta)
|
||||
{
|
||||
uint32_t flags = opts ? opts->flags : 0;
|
||||
|
||||
if (delta->status == GIT_DELTA_UNMODIFIED &&
|
||||
(flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
return true;
|
||||
|
||||
if (delta->status == GIT_DELTA_IGNORED &&
|
||||
(flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
|
||||
return true;
|
||||
|
||||
if (delta->status == GIT_DELTA_UNTRACKED &&
|
||||
(flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int git_diff_merge(
|
||||
git_diff_list *onto,
|
||||
const git_diff_list *from)
|
||||
@ -833,6 +865,14 @@ int git_diff_merge(
|
||||
j++;
|
||||
}
|
||||
|
||||
/* the ignore rules for the target may not match the source
|
||||
* or the result of a merged delta could be skippable...
|
||||
*/
|
||||
if (git_diff_delta__should_skip(&onto->opts, delta)) {
|
||||
git__free(delta);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
|
||||
break;
|
||||
}
|
||||
|
35
src/diff.h
35
src/diff.h
@ -7,6 +7,9 @@
|
||||
#ifndef INCLUDE_diff_h__
|
||||
#define INCLUDE_diff_h__
|
||||
|
||||
#include "git2/diff.h"
|
||||
#include "git2/oid.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "vector.h"
|
||||
#include "buffer.h"
|
||||
@ -25,14 +28,17 @@ enum {
|
||||
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
|
||||
};
|
||||
|
||||
#define MAX_DIFF_FILESIZE 0x20000000
|
||||
typedef struct {
|
||||
git_refcount rc;
|
||||
git_diff_delta delta;
|
||||
} git_diff_delta_refcounted;
|
||||
|
||||
struct git_diff_list {
|
||||
git_refcount rc;
|
||||
git_repository *repo;
|
||||
git_diff_options opts;
|
||||
git_vector pathspec;
|
||||
git_vector deltas; /* vector of git_diff_file_delta */
|
||||
git_vector deltas; /* vector of git_diff_delta_refcounted */
|
||||
git_pool pool;
|
||||
git_iterator_type_t old_src;
|
||||
git_iterator_type_t new_src;
|
||||
@ -42,27 +48,10 @@ struct git_diff_list {
|
||||
extern void git_diff__cleanup_modes(
|
||||
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
|
||||
|
||||
/**
|
||||
* Return the maximum possible number of files in the diff.
|
||||
*
|
||||
* NOTE: This number has to be treated as an upper bound on the number of
|
||||
* files that have changed if the diff is with the working directory.
|
||||
*
|
||||
* Why?! For efficiency, we defer loading the file contents as long as
|
||||
* possible, so if a file has been "touched" in the working directory and
|
||||
* then reverted to the original content, it may get stored in the diff list
|
||||
* as MODIFIED along with a flag that the status should be reconfirmed when
|
||||
* it is actually loaded into memory. When that load happens, it could get
|
||||
* flipped to UNMODIFIED. If unmodified files are being skipped, then the
|
||||
* iterator will skip that file and this number may be too high.
|
||||
*
|
||||
* This behavior is true of `git_diff_foreach` as well, but the only
|
||||
* implication there is that the `progress` value would not advance evenly.
|
||||
*
|
||||
* @param iterator The iterator object
|
||||
* @return The maximum number of files to be iterated over
|
||||
*/
|
||||
int git_diff_iterator__max_files(git_diff_iterator *iterator);
|
||||
extern void git_diff_list_addref(git_diff_list *diff);
|
||||
|
||||
extern bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta);
|
||||
|
||||
#endif
|
||||
|
||||
|
1181
src/diff_output.c
1181
src/diff_output.c
File diff suppressed because it is too large
Load Diff
86
src/diff_output.h
Normal file
86
src/diff_output.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* 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_output_h__
|
||||
#define INCLUDE_diff_output_h__
|
||||
|
||||
#include "git2/blob.h"
|
||||
#include "diff.h"
|
||||
#include "map.h"
|
||||
#include "xdiff/xdiff.h"
|
||||
|
||||
#define MAX_DIFF_FILESIZE 0x20000000
|
||||
|
||||
enum {
|
||||
GIT_DIFF_PATCH_ALLOCATED = (1 << 0),
|
||||
GIT_DIFF_PATCH_PREPPED = (1 << 1),
|
||||
GIT_DIFF_PATCH_LOADED = (1 << 2),
|
||||
GIT_DIFF_PATCH_DIFFABLE = (1 << 3),
|
||||
GIT_DIFF_PATCH_DIFFED = (1 << 4),
|
||||
};
|
||||
|
||||
/* context for performing diffs */
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
git_diff_list *diff;
|
||||
const git_diff_options *opts;
|
||||
git_diff_file_fn file_cb;
|
||||
git_diff_hunk_fn hunk_cb;
|
||||
git_diff_data_fn data_cb;
|
||||
void *cb_data;
|
||||
int cb_error;
|
||||
git_diff_range cb_range;
|
||||
xdemitconf_t xdiff_config;
|
||||
xpparam_t xdiff_params;
|
||||
} diff_context;
|
||||
|
||||
/* cached information about a single span in a diff */
|
||||
typedef struct diff_patch_line diff_patch_line;
|
||||
struct diff_patch_line {
|
||||
const char *ptr;
|
||||
size_t len;
|
||||
int lines, oldno, newno;
|
||||
char origin;
|
||||
};
|
||||
|
||||
/* cached information about a hunk in a diff */
|
||||
typedef struct diff_patch_hunk diff_patch_hunk;
|
||||
struct diff_patch_hunk {
|
||||
git_diff_range range;
|
||||
char header[128];
|
||||
size_t header_len;
|
||||
size_t line_start;
|
||||
size_t line_count;
|
||||
};
|
||||
|
||||
struct git_diff_patch {
|
||||
git_refcount rc;
|
||||
git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
|
||||
git_diff_delta *delta;
|
||||
diff_context *ctxt; /* only valid while generating patch */
|
||||
git_iterator_type_t old_src;
|
||||
git_iterator_type_t new_src;
|
||||
git_blob *old_blob;
|
||||
git_blob *new_blob;
|
||||
git_map old_data;
|
||||
git_map new_data;
|
||||
uint32_t flags;
|
||||
diff_patch_hunk *hunks;
|
||||
size_t hunks_asize, hunks_size;
|
||||
diff_patch_line *lines;
|
||||
size_t lines_asize, lines_size;
|
||||
};
|
||||
|
||||
/* context for performing diff on a single delta */
|
||||
typedef struct {
|
||||
git_diff_patch *patch;
|
||||
uint32_t prepped : 1;
|
||||
uint32_t loaded : 1;
|
||||
uint32_t diffable : 1;
|
||||
uint32_t diffed : 1;
|
||||
} diff_delta_context;
|
||||
|
||||
#endif
|
@ -1457,7 +1457,7 @@ static int submodule_wd_status(unsigned int *status, git_submodule *sm)
|
||||
error = git_diff_index_to_tree(sm_repo, &opt, sm_head, &diff);
|
||||
|
||||
if (!error) {
|
||||
if (git_diff_entrycount(diff, -1) > 0)
|
||||
if (git_diff_num_deltas(diff) > 0)
|
||||
*status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
|
||||
|
||||
git_diff_list_free(diff);
|
||||
@ -1474,12 +1474,13 @@ static int submodule_wd_status(unsigned int *status, git_submodule *sm)
|
||||
error = git_diff_workdir_to_index(sm_repo, &opt, &diff);
|
||||
|
||||
if (!error) {
|
||||
int untracked = git_diff_entrycount(diff, GIT_DELTA_UNTRACKED);
|
||||
int untracked =
|
||||
git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
|
||||
|
||||
if (untracked > 0)
|
||||
*status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
|
||||
|
||||
if (git_diff_entrycount(diff, -1) - untracked > 0)
|
||||
if ((git_diff_num_deltas(diff) - untracked) > 0)
|
||||
*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
@ -23,7 +23,7 @@ git_tree *resolve_commit_oid_to_tree(
|
||||
|
||||
int diff_file_fn(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
const git_diff_delta *delta,
|
||||
float progress)
|
||||
{
|
||||
diff_expects *e = cb_data;
|
||||
@ -48,8 +48,8 @@ int diff_file_fn(
|
||||
|
||||
int diff_hunk_fn(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
const char *header,
|
||||
size_t header_len)
|
||||
{
|
||||
@ -67,8 +67,8 @@ int diff_hunk_fn(
|
||||
|
||||
int diff_line_fn(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char line_origin,
|
||||
const char *content,
|
||||
size_t content_len)
|
||||
@ -112,64 +112,71 @@ int diff_foreach_via_iterator(
|
||||
git_diff_hunk_fn hunk_cb,
|
||||
git_diff_data_fn line_cb)
|
||||
{
|
||||
int error;
|
||||
git_diff_iterator *iter;
|
||||
git_diff_delta *delta;
|
||||
size_t d, num_d = git_diff_num_deltas(diff);
|
||||
|
||||
if ((error = git_diff_iterator_new(&iter, diff)) < 0)
|
||||
return error;
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
git_diff_patch *patch;
|
||||
const git_diff_delta *delta;
|
||||
size_t h, num_h;
|
||||
|
||||
while (!(error = git_diff_iterator_next_file(&delta, iter))) {
|
||||
git_diff_range *range;
|
||||
const char *hdr;
|
||||
size_t hdr_len;
|
||||
float progress = git_diff_iterator_progress(iter);
|
||||
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
||||
cl_assert(delta);
|
||||
|
||||
/* call file_cb for this file */
|
||||
if (file_cb != NULL && file_cb(data, delta, progress) != 0)
|
||||
if (file_cb != NULL && file_cb(data, delta, (float)d / num_d) != 0) {
|
||||
git_diff_patch_free(patch);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!hunk_cb && !line_cb)
|
||||
/* if there are no changes, then the patch will be NULL */
|
||||
if (!patch) {
|
||||
cl_assert(delta->status == GIT_DELTA_UNMODIFIED || delta->binary == 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (!(error = git_diff_iterator_next_hunk(
|
||||
&range, &hdr, &hdr_len, iter))) {
|
||||
if (!hunk_cb && !line_cb) {
|
||||
git_diff_patch_free(patch);
|
||||
continue;
|
||||
}
|
||||
|
||||
num_h = git_diff_patch_num_hunks(patch);
|
||||
|
||||
for (h = 0; h < num_h; h++) {
|
||||
const git_diff_range *range;
|
||||
const char *hdr;
|
||||
size_t hdr_len, l, num_l;
|
||||
|
||||
cl_git_pass(git_diff_patch_get_hunk(
|
||||
&range, &hdr, &hdr_len, &num_l, patch, h));
|
||||
|
||||
if (hunk_cb && hunk_cb(data, delta, range, hdr, hdr_len) != 0) {
|
||||
git_diff_patch_free(patch);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
for (l = 0; l < num_l; ++l) {
|
||||
char origin;
|
||||
const char *line;
|
||||
size_t line_len;
|
||||
int old_lineno, new_lineno;
|
||||
|
||||
if (hunk_cb && hunk_cb(data, delta, range, hdr, hdr_len) != 0)
|
||||
goto abort;
|
||||
cl_git_pass(git_diff_patch_get_line_in_hunk(
|
||||
&origin, &line, &line_len, &old_lineno, &new_lineno,
|
||||
patch, h, l));
|
||||
|
||||
if (!line_cb)
|
||||
continue;
|
||||
|
||||
while (!(error = git_diff_iterator_next_line(
|
||||
&origin, &line, &line_len, iter))) {
|
||||
|
||||
if (line_cb(data, delta, range, origin, line, line_len) != 0)
|
||||
if (line_cb(data, delta, range, origin, line, line_len) != 0) {
|
||||
git_diff_patch_free(patch);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (error && error != GIT_ITEROVER)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (error && error != GIT_ITEROVER)
|
||||
goto done;
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
|
||||
done:
|
||||
git_diff_iterator_free(iter);
|
||||
|
||||
if (error == GIT_ITEROVER)
|
||||
error = 0;
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
|
||||
abort:
|
||||
git_diff_iterator_free(iter);
|
||||
giterr_clear();
|
||||
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
@ -27,20 +27,20 @@ typedef struct {
|
||||
|
||||
extern int diff_file_fn(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
const git_diff_delta *delta,
|
||||
float progress);
|
||||
|
||||
extern int diff_hunk_fn(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
const char *header,
|
||||
size_t header_len);
|
||||
|
||||
extern int diff_line_fn(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char line_origin,
|
||||
const char *content,
|
||||
size_t content_len);
|
||||
|
@ -14,11 +14,16 @@ void test_diff_diffiter__create(void)
|
||||
{
|
||||
git_repository *repo = cl_git_sandbox_init("attr");
|
||||
git_diff_list *diff;
|
||||
git_diff_iterator *iter;
|
||||
size_t d, num_d;
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff));
|
||||
cl_git_pass(git_diff_iterator_new(&iter, diff));
|
||||
git_diff_iterator_free(iter);
|
||||
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
const git_diff_delta *delta;
|
||||
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
|
||||
}
|
||||
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
@ -26,24 +31,22 @@ void test_diff_diffiter__iterate_files(void)
|
||||
{
|
||||
git_repository *repo = cl_git_sandbox_init("attr");
|
||||
git_diff_list *diff;
|
||||
git_diff_iterator *iter;
|
||||
git_diff_delta *delta;
|
||||
int error, count = 0;
|
||||
size_t d, num_d;
|
||||
int count = 0;
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff));
|
||||
cl_git_pass(git_diff_iterator_new(&iter, diff));
|
||||
|
||||
while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) {
|
||||
cl_assert_equal_i(0, error);
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
cl_assert_equal_i(6, num_d);
|
||||
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
const git_diff_delta *delta;
|
||||
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
|
||||
cl_assert(delta != NULL);
|
||||
count++;
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert(delta == NULL);
|
||||
cl_assert_equal_i(6, count);
|
||||
|
||||
git_diff_iterator_free(iter);
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
@ -51,24 +54,22 @@ void test_diff_diffiter__iterate_files_2(void)
|
||||
{
|
||||
git_repository *repo = cl_git_sandbox_init("status");
|
||||
git_diff_list *diff;
|
||||
git_diff_iterator *iter;
|
||||
git_diff_delta *delta;
|
||||
int error, count = 0;
|
||||
size_t d, num_d;
|
||||
int count = 0;
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff));
|
||||
cl_git_pass(git_diff_iterator_new(&iter, diff));
|
||||
|
||||
while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) {
|
||||
cl_assert_equal_i(0, error);
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
cl_assert_equal_i(8, num_d);
|
||||
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
const git_diff_delta *delta;
|
||||
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
|
||||
cl_assert(delta != NULL);
|
||||
count++;
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert(delta == NULL);
|
||||
cl_assert_equal_i(8, count);
|
||||
|
||||
git_diff_iterator_free(iter);
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
@ -77,12 +78,8 @@ void test_diff_diffiter__iterate_files_and_hunks(void)
|
||||
git_repository *repo = cl_git_sandbox_init("status");
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff = NULL;
|
||||
git_diff_iterator *iter;
|
||||
git_diff_delta *delta;
|
||||
git_diff_range *range;
|
||||
const char *header;
|
||||
size_t header_len;
|
||||
int error, file_count = 0, hunk_count = 0;
|
||||
size_t d, num_d;
|
||||
int file_count = 0, hunk_count = 0;
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
@ -90,28 +87,42 @@ void test_diff_diffiter__iterate_files_and_hunks(void)
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff));
|
||||
|
||||
cl_git_pass(git_diff_iterator_new(&iter, diff));
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
git_diff_patch *patch;
|
||||
const git_diff_delta *delta;
|
||||
size_t h, num_h;
|
||||
|
||||
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
||||
|
||||
while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) {
|
||||
cl_assert_equal_i(0, error);
|
||||
cl_assert(delta);
|
||||
cl_assert(patch);
|
||||
|
||||
file_count++;
|
||||
|
||||
while ((error = git_diff_iterator_next_hunk(
|
||||
&range, &header, &header_len, iter)) != GIT_ITEROVER) {
|
||||
cl_assert_equal_i(0, error);
|
||||
num_h = git_diff_patch_num_hunks(patch);
|
||||
|
||||
for (h = 0; h < num_h; h++) {
|
||||
const git_diff_range *range;
|
||||
const char *header;
|
||||
size_t header_len, num_l;
|
||||
|
||||
cl_git_pass(git_diff_patch_get_hunk(
|
||||
&range, &header, &header_len, &num_l, patch, h));
|
||||
|
||||
cl_assert(range);
|
||||
cl_assert(header);
|
||||
|
||||
hunk_count++;
|
||||
}
|
||||
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert(delta == NULL);
|
||||
cl_assert_equal_i(13, file_count);
|
||||
cl_assert_equal_i(8, hunk_count);
|
||||
|
||||
git_diff_iterator_free(iter);
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
@ -120,45 +131,42 @@ void test_diff_diffiter__max_size_threshold(void)
|
||||
git_repository *repo = cl_git_sandbox_init("status");
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff = NULL;
|
||||
git_diff_iterator *iter;
|
||||
git_diff_delta *delta;
|
||||
int error, file_count = 0, binary_count = 0, hunk_count = 0;
|
||||
int file_count = 0, binary_count = 0, hunk_count = 0;
|
||||
size_t d, num_d;
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff));
|
||||
cl_git_pass(git_diff_iterator_new(&iter, diff));
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
|
||||
while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) {
|
||||
cl_assert_equal_i(0, error);
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
git_diff_patch *patch;
|
||||
const git_diff_delta *delta;
|
||||
|
||||
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
||||
cl_assert(delta);
|
||||
cl_assert(patch);
|
||||
|
||||
file_count++;
|
||||
|
||||
hunk_count += git_diff_iterator_num_hunks_in_file(iter);
|
||||
hunk_count += git_diff_patch_num_hunks(patch);
|
||||
|
||||
assert(delta->binary == 0 || delta->binary == 1);
|
||||
|
||||
binary_count += delta->binary;
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert(delta == NULL);
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(13, file_count);
|
||||
cl_assert_equal_i(0, binary_count);
|
||||
cl_assert_equal_i(8, hunk_count);
|
||||
|
||||
git_diff_iterator_free(iter);
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* try again with low file size threshold */
|
||||
|
||||
file_count = 0;
|
||||
binary_count = 0;
|
||||
hunk_count = 0;
|
||||
file_count = binary_count = hunk_count = 0;
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
@ -166,36 +174,171 @@ void test_diff_diffiter__max_size_threshold(void)
|
||||
opts.max_size = 50; /* treat anything over 50 bytes as binary! */
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff));
|
||||
cl_git_pass(git_diff_iterator_new(&iter, diff));
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
|
||||
while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) {
|
||||
cl_assert_equal_i(0, error);
|
||||
cl_assert(delta);
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
git_diff_patch *patch;
|
||||
const git_diff_delta *delta;
|
||||
|
||||
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
||||
|
||||
file_count++;
|
||||
|
||||
hunk_count += git_diff_iterator_num_hunks_in_file(iter);
|
||||
hunk_count += git_diff_patch_num_hunks(patch);
|
||||
|
||||
assert(delta->binary == 0 || delta->binary == 1);
|
||||
|
||||
binary_count += delta->binary;
|
||||
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert(delta == NULL);
|
||||
|
||||
cl_assert_equal_i(13, file_count);
|
||||
|
||||
/* Three files are over the 50 byte threshold:
|
||||
* - staged_changes_file_deleted
|
||||
* - staged_changes_modified_file
|
||||
* - staged_new_file_modified_file
|
||||
*/
|
||||
cl_assert_equal_i(3, binary_count);
|
||||
|
||||
cl_assert_equal_i(5, hunk_count);
|
||||
|
||||
git_diff_iterator_free(iter);
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
|
||||
void test_diff_diffiter__iterate_all(void)
|
||||
{
|
||||
git_repository *repo = cl_git_sandbox_init("status");
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff = NULL;
|
||||
diff_expects exp = {0};
|
||||
size_t d, num_d;
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff));
|
||||
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
git_diff_patch *patch;
|
||||
const git_diff_delta *delta;
|
||||
size_t h, num_h;
|
||||
|
||||
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
||||
cl_assert(patch && delta);
|
||||
exp.files++;
|
||||
|
||||
num_h = git_diff_patch_num_hunks(patch);
|
||||
for (h = 0; h < num_h; h++) {
|
||||
const git_diff_range *range;
|
||||
const char *header;
|
||||
size_t header_len, l, num_l;
|
||||
|
||||
cl_git_pass(git_diff_patch_get_hunk(
|
||||
&range, &header, &header_len, &num_l, patch, h));
|
||||
cl_assert(range && header);
|
||||
exp.hunks++;
|
||||
|
||||
for (l = 0; l < num_l; ++l) {
|
||||
char origin;
|
||||
const char *content;
|
||||
size_t content_len;
|
||||
|
||||
cl_git_pass(git_diff_patch_get_line_in_hunk(
|
||||
&origin, &content, &content_len, NULL, NULL, patch, h, l));
|
||||
cl_assert(content);
|
||||
exp.lines++;
|
||||
}
|
||||
}
|
||||
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(13, exp.files);
|
||||
cl_assert_equal_i(8, exp.hunks);
|
||||
cl_assert_equal_i(14, exp.lines);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
static void iterate_over_patch(git_diff_patch *patch, diff_expects *exp)
|
||||
{
|
||||
size_t h, num_h = git_diff_patch_num_hunks(patch), num_l;
|
||||
|
||||
exp->files++;
|
||||
exp->hunks += num_h;
|
||||
|
||||
/* let's iterate in reverse, just because we can! */
|
||||
for (h = 1, num_l = 0; h <= num_h; ++h)
|
||||
num_l += git_diff_patch_num_lines_in_hunk(patch, num_h - h);
|
||||
|
||||
exp->lines += num_l;
|
||||
}
|
||||
|
||||
#define PATCH_CACHE 5
|
||||
|
||||
void test_diff_diffiter__iterate_randomly_while_saving_state(void)
|
||||
{
|
||||
git_repository *repo = cl_git_sandbox_init("status");
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff = NULL;
|
||||
diff_expects exp = {0};
|
||||
git_diff_patch *patches[PATCH_CACHE];
|
||||
size_t p, d, num_d;
|
||||
|
||||
memset(patches, 0, sizeof(patches));
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff));
|
||||
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
|
||||
/* To make sure that references counts work for diff and patch objects,
|
||||
* this generates patches and randomly caches them. Only when the patch
|
||||
* is removed from the cache are hunks and lines counted. At the end,
|
||||
* there are still patches in the cache, so free the diff and try to
|
||||
* process remaining patches after the diff is freed.
|
||||
*/
|
||||
|
||||
srand(121212);
|
||||
p = rand() % PATCH_CACHE;
|
||||
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
/* take old patch */
|
||||
git_diff_patch *patch = patches[p];
|
||||
patches[p] = NULL;
|
||||
|
||||
/* cache new patch */
|
||||
cl_git_pass(git_diff_get_patch(&patches[p], NULL, diff, d));
|
||||
cl_assert(patches[p] != NULL);
|
||||
|
||||
/* process old patch if non-NULL */
|
||||
if (patch != NULL) {
|
||||
iterate_over_patch(patch, &exp);
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
|
||||
p = rand() % PATCH_CACHE;
|
||||
}
|
||||
|
||||
/* free diff list now - refcounts should keep things safe */
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* process remaining unprocessed patches */
|
||||
for (p = 0; p < PATCH_CACHE; p++) {
|
||||
git_diff_patch *patch = patches[p];
|
||||
|
||||
if (patch != NULL) {
|
||||
iterate_over_patch(patch, &exp);
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
}
|
||||
|
||||
/* hopefully it all still added up right */
|
||||
cl_assert_equal_i(13, exp.files);
|
||||
cl_assert_equal_i(8, exp.hunks);
|
||||
cl_assert_equal_i(14, exp.lines);
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ void test_diff_index__0(void)
|
||||
|
||||
static int diff_stop_after_2_files(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
const git_diff_delta *delta,
|
||||
float progress)
|
||||
{
|
||||
diff_expects *e = cb_data;
|
||||
|
@ -23,8 +23,8 @@ void test_diff_patch__cleanup(void)
|
||||
|
||||
static int check_removal_cb(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char line_origin,
|
||||
const char *formatted_output,
|
||||
size_t output_len)
|
||||
|
@ -264,10 +264,12 @@ void test_diff_tree__larger_hunks(void)
|
||||
git_tree *a, *b;
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff = NULL;
|
||||
git_diff_iterator *iter = NULL;
|
||||
git_diff_delta *delta;
|
||||
diff_expects exp;
|
||||
int error, num_files = 0;
|
||||
size_t d, num_d, h, num_h, l, num_l, header_len, line_len;
|
||||
const git_diff_delta *delta;
|
||||
git_diff_patch *patch;
|
||||
const git_diff_range *range;
|
||||
const char *header, *line;
|
||||
char origin;
|
||||
|
||||
g_repo = cl_git_sandbox_init("diff");
|
||||
|
||||
@ -277,61 +279,38 @@ void test_diff_tree__larger_hunks(void)
|
||||
opts.context_lines = 1;
|
||||
opts.interhunk_lines = 0;
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
|
||||
cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, a, b, &diff));
|
||||
cl_git_pass(git_diff_iterator_new(&iter, diff));
|
||||
|
||||
/* this should be exact */
|
||||
cl_assert(git_diff_iterator_progress(iter) == 0.0f);
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
|
||||
cl_assert(patch && delta);
|
||||
|
||||
/* You wouldn't actually structure an iterator loop this way, but
|
||||
* I have here for testing purposes of the return value
|
||||
*/
|
||||
while (!(error = git_diff_iterator_next_file(&delta, iter))) {
|
||||
git_diff_range *range;
|
||||
const char *header;
|
||||
size_t header_len;
|
||||
int actual_hunks = 0, num_hunks;
|
||||
float expected_progress;
|
||||
num_h = git_diff_patch_num_hunks(patch);
|
||||
for (h = 0; h < num_h; h++) {
|
||||
cl_git_pass(git_diff_patch_get_hunk(
|
||||
&range, &header, &header_len, &num_l, patch, h));
|
||||
|
||||
num_files++;
|
||||
|
||||
expected_progress = (float)num_files / 2.0f;
|
||||
cl_assert(expected_progress == git_diff_iterator_progress(iter));
|
||||
|
||||
num_hunks = git_diff_iterator_num_hunks_in_file(iter);
|
||||
|
||||
while (!(error = git_diff_iterator_next_hunk(
|
||||
&range, &header, &header_len, iter)))
|
||||
{
|
||||
int actual_lines = 0;
|
||||
int num_lines = git_diff_iterator_num_lines_in_hunk(iter);
|
||||
char origin;
|
||||
const char *line;
|
||||
size_t line_len;
|
||||
|
||||
while (!(error = git_diff_iterator_next_line(
|
||||
&origin, &line, &line_len, iter)))
|
||||
{
|
||||
actual_lines++;
|
||||
for (l = 0; l < num_l; ++l) {
|
||||
cl_git_pass(git_diff_patch_get_line_in_hunk(
|
||||
&origin, &line, &line_len, NULL, NULL, patch, h, l));
|
||||
cl_assert(line);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert_equal_i(actual_lines, num_lines);
|
||||
|
||||
actual_hunks++;
|
||||
cl_git_fail(git_diff_patch_get_line_in_hunk(
|
||||
&origin, &line, &line_len, NULL, NULL, patch, h, num_l));
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert_equal_i(actual_hunks, num_hunks);
|
||||
cl_git_fail(git_diff_patch_get_hunk(
|
||||
&range, &header, &header_len, &num_l, patch, num_h));
|
||||
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert_equal_i(2, num_files);
|
||||
cl_assert(git_diff_iterator_progress(iter) == 1.0f);
|
||||
cl_git_fail(git_diff_get_patch(&patch, &delta, diff, num_d));
|
||||
|
||||
cl_assert_equal_i(2, num_d);
|
||||
|
||||
git_diff_iterator_free(iter);
|
||||
git_diff_list_free(diff);
|
||||
diff = NULL;
|
||||
|
||||
|
@ -678,7 +678,7 @@ void test_diff_workdir__larger_hunks(void)
|
||||
const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
|
||||
git_tree *a, *b;
|
||||
git_diff_options opts = {0};
|
||||
int i, error;
|
||||
size_t i, d, num_d, h, num_h, l, num_l, header_len, line_len;
|
||||
|
||||
g_repo = cl_git_sandbox_init("diff");
|
||||
|
||||
@ -690,9 +690,10 @@ void test_diff_workdir__larger_hunks(void)
|
||||
|
||||
for (i = 0; i <= 2; ++i) {
|
||||
git_diff_list *diff = NULL;
|
||||
git_diff_iterator *iter = NULL;
|
||||
git_diff_delta *delta;
|
||||
int num_files = 0;
|
||||
git_diff_patch *patch;
|
||||
const git_diff_range *range;
|
||||
const char *header, *line;
|
||||
char origin;
|
||||
|
||||
/* okay, this is a bit silly, but oh well */
|
||||
switch (i) {
|
||||
@ -707,54 +708,36 @@ void test_diff_workdir__larger_hunks(void)
|
||||
break;
|
||||
}
|
||||
|
||||
cl_git_pass(git_diff_iterator_new(&iter, diff));
|
||||
num_d = git_diff_num_deltas(diff);
|
||||
cl_assert_equal_i(2, (int)num_d);
|
||||
|
||||
cl_assert(git_diff_iterator_progress(iter) == 0.0f);
|
||||
for (d = 0; d < num_d; ++d) {
|
||||
cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d));
|
||||
cl_assert(patch);
|
||||
|
||||
while (!(error = git_diff_iterator_next_file(&delta, iter))) {
|
||||
git_diff_range *range;
|
||||
const char *header;
|
||||
size_t header_len;
|
||||
int actual_hunks = 0, num_hunks;
|
||||
float expected_progress;
|
||||
num_h = git_diff_patch_num_hunks(patch);
|
||||
for (h = 0; h < num_h; h++) {
|
||||
cl_git_pass(git_diff_patch_get_hunk(
|
||||
&range, &header, &header_len, &num_l, patch, h));
|
||||
|
||||
num_files++;
|
||||
|
||||
expected_progress = (float)num_files / 2.0f;
|
||||
cl_assert(expected_progress == git_diff_iterator_progress(iter));
|
||||
|
||||
num_hunks = git_diff_iterator_num_hunks_in_file(iter);
|
||||
|
||||
while (!(error = git_diff_iterator_next_hunk(
|
||||
&range, &header, &header_len, iter)))
|
||||
{
|
||||
int actual_lines = 0;
|
||||
int num_lines = git_diff_iterator_num_lines_in_hunk(iter);
|
||||
char origin;
|
||||
const char *line;
|
||||
size_t line_len;
|
||||
|
||||
while (!(error = git_diff_iterator_next_line(
|
||||
&origin, &line, &line_len, iter)))
|
||||
{
|
||||
actual_lines++;
|
||||
for (l = 0; l < num_l; ++l) {
|
||||
cl_git_pass(git_diff_patch_get_line_in_hunk(
|
||||
&origin, &line, &line_len, NULL, NULL, patch, h, l));
|
||||
cl_assert(line);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert_equal_i(actual_lines, num_lines);
|
||||
|
||||
actual_hunks++;
|
||||
/* confirm fail after the last item */
|
||||
cl_git_fail(git_diff_patch_get_line_in_hunk(
|
||||
&origin, &line, &line_len, NULL, NULL, patch, h, num_l));
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert_equal_i(actual_hunks, num_hunks);
|
||||
/* confirm fail after the last item */
|
||||
cl_git_fail(git_diff_patch_get_hunk(
|
||||
&range, &header, &header_len, &num_l, patch, num_h));
|
||||
|
||||
git_diff_patch_free(patch);
|
||||
}
|
||||
|
||||
cl_assert_equal_i(GIT_ITEROVER, error);
|
||||
cl_assert_equal_i(2, num_files);
|
||||
cl_assert(git_diff_iterator_progress(iter) == 1.0f);
|
||||
|
||||
git_diff_iterator_free(iter);
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user