Commit 74fa4bfa by Russell Belfer

Update diff to use iterators

This is a major reorganization of the diff code.  This changes
the diff functions to use the iterators for traversing the
content.  This allowed a lot of code to be simplified.  Also,
this moved the functions relating to outputting a diff into a
new file (diff_output.c).

This includes a number of other changes - adding utility
functions, extending iterators, etc. plus more tests for the
diff code.  This also takes the example diff.c program much
further in terms of emulating git-diff command line options.
parent 760db29c
...@@ -116,7 +116,7 @@ void usage(const char *message, const char *arg) ...@@ -116,7 +116,7 @@ void usage(const char *message, const char *arg)
fprintf(stderr, "%s: %s\n", message, arg); fprintf(stderr, "%s: %s\n", message, arg);
else if (message) else if (message)
fprintf(stderr, "%s\n", message); fprintf(stderr, "%s\n", message);
fprintf(stderr, "usage: diff <tree-oid> <tree-oid>\n"); fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
exit(1); exit(1);
} }
...@@ -127,7 +127,7 @@ int main(int argc, char *argv[]) ...@@ -127,7 +127,7 @@ int main(int argc, char *argv[])
git_tree *t1 = NULL, *t2 = NULL; git_tree *t1 = NULL, *t2 = NULL;
git_diff_options opts = {0}; git_diff_options opts = {0};
git_diff_list *diff; git_diff_list *diff;
int i, color = -1, compact = 0; int i, color = -1, compact = 0, cached = 0;
char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL; char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
/* parse arguments as copied from git-diff */ /* parse arguments as copied from git-diff */
...@@ -146,6 +146,8 @@ int main(int argc, char *argv[]) ...@@ -146,6 +146,8 @@ int main(int argc, char *argv[])
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
!strcmp(a, "--patch")) !strcmp(a, "--patch"))
compact = 0; compact = 0;
else if (!strcmp(a, "--cached"))
cached = 1;
else if (!strcmp(a, "--name-status")) else if (!strcmp(a, "--name-status"))
compact = 1; compact = 1;
else if (!strcmp(a, "--color")) else if (!strcmp(a, "--color"))
...@@ -162,6 +164,10 @@ int main(int argc, char *argv[]) ...@@ -162,6 +164,10 @@ int main(int argc, char *argv[])
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE; opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space")) else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE; opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
else if (!strcmp(a, "--ignored"))
opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
else if (!strcmp(a, "--untracked"))
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
else if (!check_uint16_param(a, "-U", &opts.context_lines) && else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
!check_uint16_param(a, "--unified=", &opts.context_lines) && !check_uint16_param(a, "--unified=", &opts.context_lines) &&
!check_uint16_param(a, "--inter-hunk-context=", !check_uint16_param(a, "--inter-hunk-context=",
...@@ -171,9 +177,6 @@ int main(int argc, char *argv[]) ...@@ -171,9 +177,6 @@ int main(int argc, char *argv[])
usage("Unknown arg", a); usage("Unknown arg", a);
} }
if (!treeish1)
usage("Must provide at least one tree identifier (for now)", NULL);
/* open repo */ /* open repo */
check(git_repository_discover(path, sizeof(path), dir, 0, "/"), check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
...@@ -181,20 +184,40 @@ int main(int argc, char *argv[]) ...@@ -181,20 +184,40 @@ int main(int argc, char *argv[])
check(git_repository_open(&repo, path), check(git_repository_open(&repo, path),
"Could not open repository"); "Could not open repository");
check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree"); if (treeish1)
check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
if (treeish2) if (treeish2)
check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree"); check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
if (!treeish2) /* <sha1> <sha2> */
check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Generating diff"); /* <sha1> --cached */
/* <sha1> */
/* --cached */
/* nothing */
if (t1 && t2)
check(git_diff_tree_to_tree(repo, &opts, t1, t2, &diff), "Diff");
else if (t1 && cached)
check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
else if (t1) {
git_diff_list *diff2;
check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
check(git_diff_workdir_to_index(repo, &opts, &diff2), "Diff");
check(git_diff_merge(diff, diff2), "Merge diffs");
git_diff_list_free(diff2);
}
else if (cached) {
check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
}
else else
check(git_diff_tree_to_tree(repo, &opts, t1, t2, &diff), "Generating diff"); check(git_diff_workdir_to_index(repo, &opts, &diff), "Diff");
if (color >= 0) if (color >= 0)
fputs(colors[0], stdout); fputs(colors[0], stdout);
if (compact) if (compact)
check(git_diff_print_compact(diff, &color, printer), "Displaying diff summary"); check(git_diff_print_compact(diff, &color, printer), "Displaying diff");
else else
check(git_diff_print_patch(diff, &color, printer), "Displaying diff"); check(git_diff_print_patch(diff, &color, printer), "Displaying diff");
......
...@@ -37,7 +37,9 @@ enum { ...@@ -37,7 +37,9 @@ enum {
GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3), GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4), GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
GIT_DIFF_IGNORE_SUBMODULES = (1 << 5), GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
GIT_DIFF_PATIENCE = (1 << 6) GIT_DIFF_PATIENCE = (1 << 6),
GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8)
}; };
/** /**
...@@ -63,6 +65,26 @@ typedef struct { ...@@ -63,6 +65,26 @@ typedef struct {
*/ */
typedef struct git_diff_list git_diff_list; typedef struct git_diff_list git_diff_list;
enum {
GIT_DIFF_FILE_VALID_OID = (1 << 0),
GIT_DIFF_FILE_FREE_PATH = (1 << 1),
GIT_DIFF_FILE_BINARY = (1 << 2),
GIT_DIFF_FILE_NOT_BINARY = (1 << 3),
GIT_DIFF_FILE_FREE_DATA = (1 << 4),
GIT_DIFF_FILE_UNMAP_DATA = (1 << 5)
};
/**
* Description of one side of a diff.
*/
typedef struct {
git_oid oid;
char *path;
uint16_t mode;
git_off_t size;
unsigned int flags;
} git_diff_file;
/** /**
* Description of changes to one file. * Description of changes to one file.
* *
...@@ -77,17 +99,11 @@ typedef struct git_diff_list git_diff_list; ...@@ -77,17 +99,11 @@ typedef struct git_diff_list git_diff_list;
* It will just use the git attributes for those files. * It will just use the git attributes for those files.
*/ */
typedef struct { typedef struct {
git_diff_file old;
git_diff_file new;
git_status_t status; /**< value from tree.h */ git_status_t status; /**< value from tree.h */
unsigned int old_attr; unsigned int similarity; /**< for RENAMED and COPIED, value from 0 to 100 */
unsigned int new_attr; int binary;
git_oid old_oid;
git_oid new_oid;
git_blob *old_blob;
git_blob *new_blob;
const char *path;
const char *new_path; /**< NULL unless status is RENAMED or COPIED */
int similarity; /**< for RENAMED and COPIED, value from 0 to 100 */
int binary; /**< files in diff are binary? */
} git_diff_delta; } git_diff_delta;
/** /**
...@@ -170,7 +186,18 @@ typedef int (*git_diff_output_fn)( ...@@ -170,7 +186,18 @@ typedef int (*git_diff_output_fn)(
/**@{*/ /**@{*/
/** /**
* Deallocate a diff list.
*/
GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
/**
* Compute a difference between two tree objects. * Compute a difference between two tree objects.
*
* @param repo The repository containing the trees.
* @param opts Structure with options to influence diff or NULL for defaults.
* @param old A git_tree object to diff from.
* @param new A git_tree object to diff to.
* @param diff A pointer to a git_diff_list pointer that will be allocated.
*/ */
GIT_EXTERN(int) git_diff_tree_to_tree( GIT_EXTERN(int) git_diff_tree_to_tree(
git_repository *repo, git_repository *repo,
...@@ -181,7 +208,11 @@ GIT_EXTERN(int) git_diff_tree_to_tree( ...@@ -181,7 +208,11 @@ GIT_EXTERN(int) git_diff_tree_to_tree(
/** /**
* Compute a difference between a tree and the index. * Compute a difference between a tree and the index.
* @todo NOT IMPLEMENTED *
* @param repo The repository containing the tree and index.
* @param opts Structure with options to influence diff or NULL for defaults.
* @param old A git_tree object to diff from.
* @param diff A pointer to a git_diff_list pointer that will be allocated.
*/ */
GIT_EXTERN(int) git_diff_index_to_tree( GIT_EXTERN(int) git_diff_index_to_tree(
git_repository *repo, git_repository *repo,
...@@ -190,28 +221,56 @@ GIT_EXTERN(int) git_diff_index_to_tree( ...@@ -190,28 +221,56 @@ GIT_EXTERN(int) git_diff_index_to_tree(
git_diff_list **diff); git_diff_list **diff);
/** /**
* Compute a difference between the working directory and a tree. * Compute a difference between the working directory and the index.
* @todo NOT IMPLEMENTED *
* @param repo The repository.
* @param opts Structure with options to influence diff or NULL for defaults.
* @param diff A pointer to a git_diff_list pointer that will be allocated.
*/ */
GIT_EXTERN(int) git_diff_workdir_to_tree( GIT_EXTERN(int) git_diff_workdir_to_index(
git_repository *repo, git_repository *repo,
const git_diff_options *opts, /**< can be NULL for defaults */ const git_diff_options *opts, /**< can be NULL for defaults */
git_tree *old,
git_diff_list **diff); git_diff_list **diff);
/** /**
* Compute a difference between the working directory and the index. * Compute a difference between the working directory and a tree.
* @todo NOT IMPLEMENTED *
* This returns strictly the differences between the tree and the
* files contained in the working directory, regardless of the state
* of files in the index. There is no direct equivalent in C git.
*
* This is *NOT* the same as 'git diff HEAD' or 'git diff <SHA>'. Those
* commands diff the tree, the index, and the workdir. To emulate those
* functions, call `git_diff_index_to_tree` and `git_diff_workdir_to_index`,
* then call `git_diff_merge` on the results.
*
* @param repo The repository containing the tree.
* @param opts Structure with options to influence diff or NULL for defaults.
* @param old A git_tree object to diff from.
* @param diff A pointer to a git_diff_list pointer that will be allocated.
*/ */
GIT_EXTERN(int) git_diff_workdir_to_index( GIT_EXTERN(int) git_diff_workdir_to_tree(
git_repository *repo, git_repository *repo,
const git_diff_options *opts, /**< can be NULL for defaults */ const git_diff_options *opts, /**< can be NULL for defaults */
git_tree *old,
git_diff_list **diff); git_diff_list **diff);
/** /**
* Deallocate a diff list. * Merge one diff list into another.
*
* This merges items from the "from" list into the "onto" list. The
* resulting diff list will have all items that appear in either list.
* If an item appears in both lists, then it will be "merged" to appear
* as if the old version was from the "onto" list and the new version
* is from the "from" list (with the exception that if the item has a
* pending DELETE in the middle, then it will show as deleted).
*
* @param onto Diff to merge into.
* @param from Diff to merge.
*/ */
GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff); GIT_EXTERN(int) git_diff_merge(
git_diff_list *onto,
const git_diff_list *from);
/**@}*/ /**@}*/
......
...@@ -160,6 +160,11 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, unsigned int le ...@@ -160,6 +160,11 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, unsigned int le
GIT_EXTERN(int) git_oid_streq(const git_oid *a, const char *str); GIT_EXTERN(int) git_oid_streq(const git_oid *a, const char *str);
/** /**
* Check is an oid is all zeros.
*/
GIT_EXTERN(int) git_oid_iszero(const git_oid *a);
/**
* OID Shortener object * OID Shortener object
*/ */
typedef struct git_oid_shorten git_oid_shorten; typedef struct git_oid_shorten git_oid_shorten;
......
...@@ -31,7 +31,7 @@ GIT_BEGIN_DECL ...@@ -31,7 +31,7 @@ GIT_BEGIN_DECL
#define GIT_STATUS_WT_MODIFIED (1 << 4) #define GIT_STATUS_WT_MODIFIED (1 << 4)
#define GIT_STATUS_WT_DELETED (1 << 5) #define GIT_STATUS_WT_DELETED (1 << 5)
#define GIT_STATUS_IGNORED (1 << 6) #define GIT_STATUS_WT_IGNORED (1 << 6)
/** /**
* Gather file statuses and run a callback for each one. * Gather file statuses and run a callback for each one.
......
...@@ -10,15 +10,15 @@ ...@@ -10,15 +10,15 @@
#include <stdio.h> #include <stdio.h>
#include "vector.h" #include "vector.h"
#include "buffer.h" #include "buffer.h"
#include "iterator.h"
#include "repository.h"
struct git_diff_list { struct git_diff_list {
git_repository *repo; git_repository *repo;
git_diff_options opts; git_diff_options opts;
git_vector files; /* vector of git_diff_file_delta */ git_vector deltas; /* vector of git_diff_file_delta */
git_iterator_type_t old_src;
/* the following are just used while processing the diff list */ git_iterator_type_t new_src;
git_buf pfx;
git_status_t status;
}; };
#endif #endif
......
...@@ -79,10 +79,6 @@ git_off_t git_futils_filesize(git_file fd) ...@@ -79,10 +79,6 @@ git_off_t git_futils_filesize(git_file fd)
return sb.st_size; return sb.st_size;
} }
#define GIT_MODE_PERMS_MASK 0777
#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
mode_t git_futils_canonical_mode(mode_t raw_mode) mode_t git_futils_canonical_mode(mode_t raw_mode)
{ {
if (S_ISREG(raw_mode)) if (S_ISREG(raw_mode))
...@@ -181,6 +177,15 @@ int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len) ...@@ -181,6 +177,15 @@ int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin); return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
} }
int git_futils_mmap_ro_file(git_map *out, const char *path)
{
git_file fd = p_open(path, O_RDONLY /* | O_NOATIME */);
size_t len = git_futils_filesize(fd);
int result = git_futils_mmap_ro(out, fd, 0, len);
p_close(fd);
return result;
}
void git_futils_mmap_free(git_map *out) void git_futils_mmap_free(git_map *out)
{ {
p_munmap(out); p_munmap(out);
......
...@@ -81,6 +81,10 @@ extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t ...@@ -81,6 +81,10 @@ extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t
*/ */
extern git_off_t git_futils_filesize(git_file fd); extern git_off_t git_futils_filesize(git_file fd);
#define GIT_MODE_PERMS_MASK 0777
#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
/** /**
* Convert a mode_t from the OS to a legal git mode_t value. * Convert a mode_t from the OS to a legal git mode_t value.
*/ */
...@@ -109,6 +113,19 @@ extern int git_futils_mmap_ro( ...@@ -109,6 +113,19 @@ extern int git_futils_mmap_ro(
size_t len); size_t len);
/** /**
* Read-only map an entire file.
*
* @param out buffer to populate with the mapping information.
* @param path path to file to be opened.
* @return
* - GIT_SUCCESS on success;
* - GIT_EOSERR on an unspecified OS related error.
*/
extern int git_futils_mmap_ro_file(
git_map *out,
const char *path);
/**
* Release the memory associated with a previous memory mapping. * Release the memory associated with a previous memory mapping.
* @param map the mapping description previously configured. * @param map the mapping description previously configured.
*/ */
......
...@@ -143,6 +143,16 @@ static void tree_iterator__free(git_iterator *self) ...@@ -143,6 +143,16 @@ static void tree_iterator__free(git_iterator *self)
git_buf_free(&ti->path); git_buf_free(&ti->path);
} }
static int tree_iterator__reset(git_iterator *self)
{
tree_iterator *ti = (tree_iterator *)self;
while (ti->stack && ti->stack->next)
tree_iterator__pop_frame(ti);
if (ti->stack)
ti->stack->index = 0;
return tree_iterator__expand_tree(ti);
}
int git_iterator_for_tree( int git_iterator_for_tree(
git_repository *repo, git_tree *tree, git_iterator **iter) git_repository *repo, git_tree *tree, git_iterator **iter)
{ {
...@@ -155,6 +165,7 @@ int git_iterator_for_tree( ...@@ -155,6 +165,7 @@ int git_iterator_for_tree(
ti->base.current = tree_iterator__current; ti->base.current = tree_iterator__current;
ti->base.at_end = tree_iterator__at_end; ti->base.at_end = tree_iterator__at_end;
ti->base.advance = tree_iterator__advance; ti->base.advance = tree_iterator__advance;
ti->base.reset = tree_iterator__reset;
ti->base.free = tree_iterator__free; ti->base.free = tree_iterator__free;
ti->repo = repo; ti->repo = repo;
ti->stack = tree_iterator__alloc_frame(tree); ti->stack = tree_iterator__alloc_frame(tree);
...@@ -199,6 +210,13 @@ static int index_iterator__advance( ...@@ -199,6 +210,13 @@ static int index_iterator__advance(
return GIT_SUCCESS; return GIT_SUCCESS;
} }
static int index_iterator__reset(git_iterator *self)
{
index_iterator *ii = (index_iterator *)self;
ii->current = 0;
return GIT_SUCCESS;
}
static void index_iterator__free(git_iterator *self) static void index_iterator__free(git_iterator *self)
{ {
index_iterator *ii = (index_iterator *)self; index_iterator *ii = (index_iterator *)self;
...@@ -217,6 +235,7 @@ int git_iterator_for_index(git_repository *repo, git_iterator **iter) ...@@ -217,6 +235,7 @@ int git_iterator_for_index(git_repository *repo, git_iterator **iter)
ii->base.current = index_iterator__current; ii->base.current = index_iterator__current;
ii->base.at_end = index_iterator__at_end; ii->base.at_end = index_iterator__at_end;
ii->base.advance = index_iterator__advance; ii->base.advance = index_iterator__advance;
ii->base.reset = index_iterator__reset;
ii->base.free = index_iterator__free; ii->base.free = index_iterator__free;
ii->current = 0; ii->current = 0;
...@@ -251,7 +270,7 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame(void) ...@@ -251,7 +270,7 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame(void)
workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame)); workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
if (wf == NULL) if (wf == NULL)
return wf; return wf;
if (git_vector_init(&wf->entries, 0, git__strcmp_cb) != GIT_SUCCESS) { if (git_vector_init(&wf->entries, 0, git_path_with_stat_cmp) != GIT_SUCCESS) {
git__free(wf); git__free(wf);
return NULL; return NULL;
} }
...@@ -261,7 +280,7 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame(void) ...@@ -261,7 +280,7 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame(void)
static void workdir_iterator__free_frame(workdir_iterator_frame *wf) static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
{ {
unsigned int i; unsigned int i;
char *path; git_path_with_stat *path;
git_vector_foreach(&wf->entries, i, path) git_vector_foreach(&wf->entries, i, path)
git__free(path); git__free(path);
...@@ -278,10 +297,7 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi) ...@@ -278,10 +297,7 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
if (wf == NULL) if (wf == NULL)
return GIT_ENOMEM; return GIT_ENOMEM;
/* allocate dir entries with extra byte (the "1" param) so we error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
* can suffix directory names with a "/".
*/
error = git_path_dirload(wi->path.ptr, wi->root_len, 1, &wf->entries);
if (error < GIT_SUCCESS || wf->entries.length == 0) { if (error < GIT_SUCCESS || wf->entries.length == 0) {
workdir_iterator__free_frame(wf); workdir_iterator__free_frame(wf);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
...@@ -319,7 +335,7 @@ static int workdir_iterator__advance( ...@@ -319,7 +335,7 @@ static int workdir_iterator__advance(
int error; int error;
workdir_iterator *wi = (workdir_iterator *)self; workdir_iterator *wi = (workdir_iterator *)self;
workdir_iterator_frame *wf; workdir_iterator_frame *wf;
const char *next; git_path_with_stat *next;
if (entry != NULL) if (entry != NULL)
*entry = NULL; *entry = NULL;
...@@ -330,7 +346,7 @@ static int workdir_iterator__advance( ...@@ -330,7 +346,7 @@ static int workdir_iterator__advance(
while ((wf = wi->stack) != NULL) { while ((wf = wi->stack) != NULL) {
next = git_vector_get(&wf->entries, ++wf->index); next = git_vector_get(&wf->entries, ++wf->index);
if (next != NULL) { if (next != NULL) {
if (strcmp(next, DOT_GIT) == 0) if (strcmp(next->path, DOT_GIT "/") == 0)
continue; continue;
/* else found a good entry */ /* else found a good entry */
break; break;
...@@ -355,6 +371,20 @@ static int workdir_iterator__advance( ...@@ -355,6 +371,20 @@ static int workdir_iterator__advance(
return error; return error;
} }
static int workdir_iterator__reset(git_iterator *self)
{
workdir_iterator *wi = (workdir_iterator *)self;
while (wi->stack != NULL && wi->stack->next != NULL) {
workdir_iterator_frame *wf = wi->stack;
wi->stack = wf->next;
workdir_iterator__free_frame(wf);
git_ignore__pop_dir(&wi->ignores);
}
if (wi->stack)
wi->stack->index = 0;
return GIT_SUCCESS;
}
static void workdir_iterator__free(git_iterator *self) static void workdir_iterator__free(git_iterator *self)
{ {
workdir_iterator *wi = (workdir_iterator *)self; workdir_iterator *wi = (workdir_iterator *)self;
...@@ -372,39 +402,35 @@ static void workdir_iterator__free(git_iterator *self) ...@@ -372,39 +402,35 @@ static void workdir_iterator__free(git_iterator *self)
static int workdir_iterator__update_entry(workdir_iterator *wi) static int workdir_iterator__update_entry(workdir_iterator *wi)
{ {
int error; int error;
struct stat st; git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
char *relpath = git_vector_get(&wi->stack->entries, wi->stack->index);
error = git_buf_joinpath( git_buf_truncate(&wi->path, wi->root_len);
&wi->path, git_repository_workdir(wi->repo), relpath); error = git_buf_put(&wi->path, ps->path, ps->path_len);
if (error < GIT_SUCCESS) if (error < GIT_SUCCESS)
return error; return error;
memset(&wi->entry, 0, sizeof(wi->entry)); memset(&wi->entry, 0, sizeof(wi->entry));
wi->entry.path = relpath; wi->entry.path = ps->path;
/* skip over .git directory */ /* skip over .git directory */
if (strcmp(relpath, DOT_GIT) == 0) if (strcmp(ps->path, DOT_GIT "/") == 0)
return workdir_iterator__advance((git_iterator *)wi, NULL); return workdir_iterator__advance((git_iterator *)wi, NULL);
/* if there is an error processing the entry, treat as ignored */ /* if there is an error processing the entry, treat as ignored */
wi->is_ignored = 1; wi->is_ignored = 1;
if (p_lstat(wi->path.ptr, &st) < 0)
return GIT_SUCCESS;
/* TODO: remove shared code for struct stat conversion with index.c */ /* TODO: remove shared code for struct stat conversion with index.c */
wi->entry.ctime.seconds = (git_time_t)st.st_ctime; wi->entry.ctime.seconds = (git_time_t)ps->st.st_ctime;
wi->entry.mtime.seconds = (git_time_t)st.st_mtime; wi->entry.mtime.seconds = (git_time_t)ps->st.st_mtime;
wi->entry.dev = st.st_rdev; wi->entry.dev = ps->st.st_rdev;
wi->entry.ino = st.st_ino; wi->entry.ino = ps->st.st_ino;
wi->entry.mode = git_futils_canonical_mode(st.st_mode); wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
wi->entry.uid = st.st_uid; wi->entry.uid = ps->st.st_uid;
wi->entry.gid = st.st_gid; wi->entry.gid = ps->st.st_gid;
wi->entry.file_size = st.st_size; wi->entry.file_size = ps->st.st_size;
/* if this is a file type we don't handle, treat as ignored */ /* if this is a file type we don't handle, treat as ignored */
if (st.st_mode == 0) if (wi->entry.mode == 0)
return GIT_SUCCESS; return GIT_SUCCESS;
/* okay, we are far enough along to look up real ignore rule */ /* okay, we are far enough along to look up real ignore rule */
...@@ -412,18 +438,10 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) ...@@ -412,18 +438,10 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
if (error != GIT_SUCCESS) if (error != GIT_SUCCESS)
return GIT_SUCCESS; return GIT_SUCCESS;
if (S_ISDIR(st.st_mode)) { /* detect submodules */
if (git_path_contains(&wi->path, DOT_GIT) == GIT_SUCCESS) { if (S_ISDIR(wi->entry.mode) &&
/* create submodule entry */ git_path_contains(&wi->path, DOT_GIT) == GIT_SUCCESS)
wi->entry.mode = S_IFGITLINK; wi->entry.mode = S_IFGITLINK;
} else {
/* create directory entry that can be advanced into as needed */
size_t pathlen = strlen(wi->entry.path);
wi->entry.path[pathlen] = '/';
wi->entry.path[pathlen + 1] = '\0';
wi->entry.mode = S_IFDIR;
}
}
return GIT_SUCCESS; return GIT_SUCCESS;
} }
...@@ -439,11 +457,14 @@ int git_iterator_for_workdir(git_repository *repo, git_iterator **iter) ...@@ -439,11 +457,14 @@ int git_iterator_for_workdir(git_repository *repo, git_iterator **iter)
wi->base.current = workdir_iterator__current; wi->base.current = workdir_iterator__current;
wi->base.at_end = workdir_iterator__at_end; wi->base.at_end = workdir_iterator__at_end;
wi->base.advance = workdir_iterator__advance; wi->base.advance = workdir_iterator__advance;
wi->base.reset = workdir_iterator__reset;
wi->base.free = workdir_iterator__free; wi->base.free = workdir_iterator__free;
wi->repo = repo; wi->repo = repo;
error = git_buf_sets(&wi->path, git_repository_workdir(repo)); error = git_buf_sets(&wi->path, git_repository_workdir(repo));
if (error == GIT_SUCCESS) if (error == GIT_SUCCESS)
error = git_path_to_dir(&wi->path);
if (error == GIT_SUCCESS)
error = git_ignore__for_path(repo, "", &wi->ignores); error = git_ignore__for_path(repo, "", &wi->ignores);
if (error != GIT_SUCCESS) { if (error != GIT_SUCCESS) {
git__free(wi); git__free(wi);
......
...@@ -23,6 +23,7 @@ struct git_iterator { ...@@ -23,6 +23,7 @@ struct git_iterator {
int (*current)(git_iterator *, const git_index_entry **); int (*current)(git_iterator *, const git_index_entry **);
int (*at_end)(git_iterator *); int (*at_end)(git_iterator *);
int (*advance)(git_iterator *, const git_index_entry **); int (*advance)(git_iterator *, const git_index_entry **);
int (*reset)(git_iterator *);
void (*free)(git_iterator *); void (*free)(git_iterator *);
}; };
...@@ -60,6 +61,11 @@ GIT_INLINE(int) git_iterator_advance( ...@@ -60,6 +61,11 @@ GIT_INLINE(int) git_iterator_advance(
return iter->advance(iter, entry); return iter->advance(iter, entry);
} }
GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
{
return iter->reset(iter);
}
GIT_INLINE(void) git_iterator_free(git_iterator *iter) GIT_INLINE(void) git_iterator_free(git_iterator *iter)
{ {
iter->free(iter); iter->free(iter);
......
...@@ -190,6 +190,16 @@ int git_oid_streq(const git_oid *a, const char *str) ...@@ -190,6 +190,16 @@ int git_oid_streq(const git_oid *a, const char *str)
return git_oid_cmp(a, &id) == 0 ? GIT_SUCCESS : GIT_ERROR; return git_oid_cmp(a, &id) == 0 ? GIT_SUCCESS : GIT_ERROR;
} }
int git_oid_iszero(const git_oid *oid_a)
{
const unsigned char *a = oid_a->id;
unsigned int i;
for (i = 0; i < GIT_OID_RAWSZ; ++i, ++a)
if (*a != 0)
return 0;
return 1;
}
typedef short node_index; typedef short node_index;
typedef union { typedef union {
......
...@@ -583,3 +583,46 @@ int git_path_dirload( ...@@ -583,3 +583,46 @@ int git_path_dirload(
return GIT_SUCCESS; return GIT_SUCCESS;
} }
int git_path_with_stat_cmp(const void *a, const void *b)
{
const git_path_with_stat *psa = a, *psb = b;
return git__strcmp_cb(psa->path, psb->path);
}
int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
git_vector *contents)
{
int error;
unsigned int i;
git_path_with_stat *ps;
git_buf full = GIT_BUF_INIT;
if ((error = git_buf_set(&full, path, prefix_len)) != GIT_SUCCESS)
return error;
if ((error = git_path_dirload(path, prefix_len,
sizeof(git_path_with_stat) + 1, contents)) != GIT_SUCCESS) {
git_buf_free(&full);
return error;
}
git_vector_foreach(contents, i, ps) {
size_t path_len = strlen((char *)ps);
memmove(ps->path, ps, path_len + 1);
ps->path_len = path_len;
git_buf_joinpath(&full, full.ptr, ps->path);
p_lstat(full.ptr, &ps->st);
git_buf_truncate(&full, prefix_len);
if (S_ISDIR(ps->st.st_mode)) {
ps->path[path_len] = '/';
ps->path[path_len + 1] = '\0';
}
}
return error;
}
...@@ -246,4 +246,26 @@ extern int git_path_dirload( ...@@ -246,4 +246,26 @@ extern int git_path_dirload(
size_t alloc_extra, size_t alloc_extra,
git_vector *contents); git_vector *contents);
typedef struct {
struct stat st;
size_t path_len;
char path[GIT_FLEX_ARRAY];
} git_path_with_stat;
extern int git_path_with_stat_cmp(const void *a, const void *b);
/**
* Load all directory entries along with stat info into a vector.
*
* This is just like git_path_dirload except that each entry in the
* vector is a git_path_with_stat structure that contains both the
* path and the stat info, plus directories will have a / suffixed
* to their path name.
*/
extern int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
git_vector *contents);
#endif #endif
...@@ -66,4 +66,6 @@ extern int p_rename(const char *from, const char *to); ...@@ -66,4 +66,6 @@ extern int p_rename(const char *from, const char *to);
# include "unix/posix.h" # include "unix/posix.h"
#endif #endif
#define p_readdir_r(d,e,r) readdir_r(d,e,r)
#endif #endif
...@@ -131,7 +131,7 @@ static int status_entry_update_ignore(struct status_entry *e, git_ignores *ignor ...@@ -131,7 +131,7 @@ static int status_entry_update_ignore(struct status_entry *e, git_ignores *ignor
if ((error = git_ignore__lookup(ignores, path, &ignored)) == GIT_SUCCESS && if ((error = git_ignore__lookup(ignores, path, &ignored)) == GIT_SUCCESS &&
ignored) ignored)
e->status_flags = e->status_flags =
(e->status_flags & ~GIT_STATUS_WT_NEW) | GIT_STATUS_IGNORED; (e->status_flags & ~GIT_STATUS_WT_NEW) | GIT_STATUS_WT_IGNORED;
return error; return error;
} }
......
...@@ -21,6 +21,5 @@ ...@@ -21,6 +21,5 @@
#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__) #define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
#define p_mkstemp(p) mkstemp(p) #define p_mkstemp(p) mkstemp(p)
#define p_setenv(n,v,o) setenv(n,v,o) #define p_setenv(n,v,o) setenv(n,v,o)
#define p_readdir_r(d,e,r) readdir_r(d,e,r)
#endif #endif
...@@ -220,4 +220,14 @@ void git_vector_clear(git_vector *v) ...@@ -220,4 +220,14 @@ void git_vector_clear(git_vector *v)
v->sorted = 1; v->sorted = 1;
} }
void git_vector_swap(git_vector *a, git_vector *b)
{
git_vector t;
if (!a || !b || a == b)
return;
memcpy(&t, a, sizeof(t));
memcpy(a, b, sizeof(t));
memcpy(b, &t, sizeof(t));
}
...@@ -24,6 +24,7 @@ typedef struct git_vector { ...@@ -24,6 +24,7 @@ typedef struct git_vector {
int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp); int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp);
void git_vector_free(git_vector *v); void git_vector_free(git_vector *v);
void git_vector_clear(git_vector *v); void git_vector_clear(git_vector *v);
void git_vector_swap(git_vector *a, git_vector *b);
int git_vector_search(git_vector *v, const void *entry); int git_vector_search(git_vector *v, const void *entry);
int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key); int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key);
...@@ -38,6 +39,11 @@ GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position) ...@@ -38,6 +39,11 @@ GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)
return (position < v->length) ? v->contents[position] : NULL; return (position < v->length) ? v->contents[position] : NULL;
} }
GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int position)
{
return (position < v->length) ? v->contents[position] : NULL;
}
GIT_INLINE(void *) git_vector_last(git_vector *v) GIT_INLINE(void *) git_vector_last(git_vector *v)
{ {
return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL; return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
......
...@@ -58,8 +58,11 @@ git__DIR *git__opendir(const char *dir) ...@@ -58,8 +58,11 @@ git__DIR *git__opendir(const char *dir)
return new; return new;
} }
int git__readdir_r( int git__readdir_ext(
git__DIR *d, struct git__dirent *entry, struct git__dirent **result) git__DIR *d,
struct git__dirent *entry,
struct git__dirent **result,
int *is_dir)
{ {
if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE) if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE)
return -1; return -1;
...@@ -80,13 +83,16 @@ int git__readdir_r( ...@@ -80,13 +83,16 @@ int git__readdir_r(
entry->d_name, GIT_PATH_MAX, NULL, NULL); entry->d_name, GIT_PATH_MAX, NULL, NULL);
*result = entry; *result = entry;
if (is_dir != NULL)
*is_dir = ((d->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
return 0; return 0;
} }
struct git__dirent *git__readdir(git__DIR *d) struct git__dirent *git__readdir(git__DIR *d)
{ {
struct git__dirent *result; struct git__dirent *result;
if (git__readdir_r(d, &d->entry, &result) < 0) if (git__readdir_ext(d, &d->entry, &result, NULL) < 0)
return NULL; return NULL;
return result; return result;
} }
......
...@@ -24,7 +24,8 @@ typedef struct { ...@@ -24,7 +24,8 @@ typedef struct {
extern git__DIR *git__opendir(const char *); extern git__DIR *git__opendir(const char *);
extern struct git__dirent *git__readdir(git__DIR *); extern struct git__dirent *git__readdir(git__DIR *);
extern int git__readdir_r(git__DIR*, struct git__dirent*, struct git__dirent**); extern int git__readdir_ext(
git__DIR *, struct git__dirent *, struct git__dirent **, int *);
extern void git__rewinddir(git__DIR *); extern void git__rewinddir(git__DIR *);
extern int git__closedir(git__DIR *); extern int git__closedir(git__DIR *);
...@@ -33,10 +34,9 @@ extern int git__closedir(git__DIR *); ...@@ -33,10 +34,9 @@ extern int git__closedir(git__DIR *);
# define DIR git__DIR # define DIR git__DIR
# define opendir git__opendir # define opendir git__opendir
# define readdir git__readdir # define readdir git__readdir
# define readdir_r(d,e,r) git__readdir_ext((d),(e),(r),NULL)
# define rewinddir git__rewinddir # define rewinddir git__rewinddir
# define closedir git__closedir # define closedir git__closedir
# endif # endif
#define p_readdir_r(d,e,r) git__readdir_r(d,e,r)
#endif /* INCLUDE_dir_h__ */ #endif /* INCLUDE_dir_h__ */
...@@ -69,12 +69,12 @@ extern "C" { ...@@ -69,12 +69,12 @@ extern "C" {
typedef struct s_mmfile { typedef struct s_mmfile {
char *ptr; char *ptr;
long size; size_t size;
} mmfile_t; } mmfile_t;
typedef struct s_mmbuffer { typedef struct s_mmbuffer {
char *ptr; char *ptr;
long size; size_t size;
} mmbuffer_t; } mmbuffer_t;
typedef struct s_xpparam { typedef struct s_xpparam {
......
...@@ -38,10 +38,13 @@ GIT_INLINE(void) cl_assert_strequal_internal( ...@@ -38,10 +38,13 @@ GIT_INLINE(void) cl_assert_strequal_internal(
if (!match) { if (!match) {
char buf[4096]; char buf[4096];
snprintf(buf, 4096, "'%s' != '%s'", a, b); snprintf(buf, 4096, "'%s' != '%s'", a, b);
clar__assert(0, file, line, buf, err, 1); clar__assert(0, file, line, err, buf, 1);
} }
} }
#define cl_assert_intequal(a,b) \
do { if ((a) != (b)) { char buf[128]; snprintf(buf,128,"%d != %d",(a),(b)); clar__assert(0,__FILE__,__LINE__,#a " != " #b,buf,1); } } while (0)
/* /*
* Some utility macros for building long strings * Some utility macros for building long strings
*/ */
......
...@@ -29,12 +29,14 @@ int diff_file_fn( ...@@ -29,12 +29,14 @@ int diff_file_fn(
diff_expects *e = cb_data; diff_expects *e = cb_data;
(void)progress; (void)progress;
e->files++; e->files++;
if (delta->old_attr == 0) switch (delta->status) {
e->file_adds++; case GIT_STATUS_ADDED: e->file_adds++; break;
else if (delta->new_attr == 0) case GIT_STATUS_DELETED: e->file_dels++; break;
e->file_dels++; case GIT_STATUS_MODIFIED: e->file_mods++; break;
else case GIT_STATUS_IGNORED: e->file_ignored++; break;
e->file_mods++; case GIT_STATUS_UNTRACKED: e->file_untracked++; break;
default: break;
}
return 0; return 0;
} }
......
...@@ -9,6 +9,8 @@ typedef struct { ...@@ -9,6 +9,8 @@ typedef struct {
int file_adds; int file_adds;
int file_dels; int file_dels;
int file_mods; int file_mods;
int file_ignored;
int file_untracked;
int hunks; int hunks;
int hunk_new_lines; int hunk_new_lines;
......
...@@ -338,7 +338,7 @@ static void workdir_iterator_test( ...@@ -338,7 +338,7 @@ static void workdir_iterator_test(
void test_diff_iterator__workdir_0(void) void test_diff_iterator__workdir_0(void)
{ {
workdir_iterator_test("attr", 24, 2, NULL, "ign"); workdir_iterator_test("attr", 25, 2, NULL, "ign");
} }
static const char *status_paths[] = { static const char *status_paths[] = {
...@@ -351,10 +351,10 @@ static const char *status_paths[] = { ...@@ -351,10 +351,10 @@ static const char *status_paths[] = {
"staged_delete_modified_file", "staged_delete_modified_file",
"staged_new_file", "staged_new_file",
"staged_new_file_modified_file", "staged_new_file_modified_file",
"subdir.txt",
"subdir/current_file", "subdir/current_file",
"subdir/modified_file", "subdir/modified_file",
"subdir/new_file", "subdir/new_file",
"subdir.txt",
NULL NULL
}; };
......
...@@ -100,7 +100,7 @@ void test_diff_tree__options(void) ...@@ -100,7 +100,7 @@ void test_diff_tree__options(void)
git_diff_options opts = {0}; git_diff_options opts = {0};
git_diff_list *diff = NULL; git_diff_list *diff = NULL;
diff_expects exp; diff_expects actual;
int test_ab_or_cd[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1 }; int test_ab_or_cd[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1 };
git_diff_options test_options[] = { git_diff_options test_options[] = {
/* a vs b tests */ /* a vs b tests */
...@@ -123,25 +123,26 @@ void test_diff_tree__options(void) ...@@ -123,25 +123,26 @@ void test_diff_tree__options(void)
*/ */
diff_expects test_expects[] = { diff_expects test_expects[] = {
/* a vs b tests */ /* a vs b tests */
{ 5, 3, 0, 2, 4, 0, 0, 51, 2, 46, 3 }, { 5, 3, 0, 2, 0, 0, 4, 0, 0, 51, 2, 46, 3 },
{ 5, 3, 0, 2, 4, 0, 0, 53, 4, 46, 3 }, { 5, 3, 0, 2, 0, 0, 4, 0, 0, 53, 4, 46, 3 },
{ 5, 0, 3, 2, 4, 0, 0, 52, 3, 3, 46 }, { 5, 0, 3, 2, 0, 0, 4, 0, 0, 52, 3, 3, 46 },
{ 5, 3, 0, 2, 5, 0, 0, 54, 3, 48, 3 }, { 5, 3, 0, 2, 0, 0, 5, 0, 0, 54, 3, 48, 3 },
/* c vs d tests */ /* c vs d tests */
{ 1, 0, 0, 1, 1, 0, 0, 22, 9, 10, 3 }, { 1, 0, 0, 1, 0, 0, 1, 0, 0, 22, 9, 10, 3 },
{ 1, 0, 0, 1, 1, 0, 0, 19, 12, 7, 0 }, { 1, 0, 0, 1, 0, 0, 1, 0, 0, 19, 12, 7, 0 },
{ 1, 0, 0, 1, 1, 0, 0, 20, 11, 8, 1 }, { 1, 0, 0, 1, 0, 0, 1, 0, 0, 20, 11, 8, 1 },
{ 1, 0, 0, 1, 1, 0, 0, 20, 11, 8, 1 }, { 1, 0, 0, 1, 0, 0, 1, 0, 0, 20, 11, 8, 1 },
{ 1, 0, 0, 1, 1, 0, 0, 18, 11, 0, 7 }, { 1, 0, 0, 1, 0, 0, 1, 0, 0, 18, 11, 0, 7 },
{ 0 }, { 0 },
}; };
diff_expects *expected;
int i; int i;
cl_assert(a); cl_assert(a);
cl_assert(b); cl_assert(b);
for (i = 0; test_expects[i].files > 0; i++) { for (i = 0; test_expects[i].files > 0; i++) {
memset(&exp, 0, sizeof(exp)); /* clear accumulator */ memset(&actual, 0, sizeof(actual)); /* clear accumulator */
opts = test_options[i]; opts = test_options[i];
if (test_ab_or_cd[i] == 0) if (test_ab_or_cd[i] == 0)
...@@ -150,17 +151,18 @@ void test_diff_tree__options(void) ...@@ -150,17 +151,18 @@ void test_diff_tree__options(void)
cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, c, d, &diff)); cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, c, d, &diff));
cl_git_pass(git_diff_foreach( cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); diff, &actual, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == test_expects[i].files); expected = &test_expects[i];
cl_assert(exp.file_adds == test_expects[i].file_adds); cl_assert_intequal(actual.files, expected->files);
cl_assert(exp.file_dels == test_expects[i].file_dels); cl_assert_intequal(actual.file_adds, expected->file_adds);
cl_assert(exp.file_mods == test_expects[i].file_mods); cl_assert_intequal(actual.file_dels, expected->file_dels);
cl_assert(exp.hunks == test_expects[i].hunks); cl_assert_intequal(actual.file_mods, expected->file_mods);
cl_assert(exp.lines == test_expects[i].lines); cl_assert_intequal(actual.hunks, expected->hunks);
cl_assert(exp.line_ctxt == test_expects[i].line_ctxt); cl_assert_intequal(actual.lines, expected->lines);
cl_assert(exp.line_adds == test_expects[i].line_adds); cl_assert_intequal(actual.line_ctxt, expected->line_ctxt);
cl_assert(exp.line_dels == test_expects[i].line_dels); cl_assert_intequal(actual.line_adds, expected->line_adds);
cl_assert_intequal(actual.line_dels, expected->line_dels);
git_diff_list_free(diff); git_diff_list_free(diff);
diff = NULL; diff = NULL;
......
#include "clar_libgit2.h"
#include "diff_helpers.h"
static git_repository *g_repo = NULL;
void test_diff_workdir__initialize(void)
{
cl_fixture_sandbox("status");
cl_git_pass(p_rename("status/.gitted", "status/.git"));
cl_git_pass(git_repository_open(&g_repo, "status/.git"));
}
void test_diff_workdir__cleanup(void)
{
git_repository_free(g_repo);
g_repo = NULL;
cl_fixture_cleanup("status");
}
void test_diff_workdir__to_index(void)
{
git_diff_options opts = {0};
git_diff_list *diff = NULL;
diff_expects exp;
opts.context_lines = 3;
opts.interhunk_lines = 1;
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
/* to generate these values:
* - cd to tests/resources/status,
* - mv .gitted .git
* - git diff --name-status
* - git diff
* - mv .git .gitted
*/
cl_assert_intequal(12, exp.files);
cl_assert_intequal(0, exp.file_adds);
cl_assert_intequal(4, exp.file_dels);
cl_assert_intequal(4, exp.file_mods);
cl_assert_intequal(1, exp.file_ignored);
cl_assert_intequal(3, exp.file_untracked);
cl_assert_intequal(8, exp.hunks);
cl_assert_intequal(14, exp.lines);
cl_assert_intequal(5, exp.line_ctxt);
cl_assert_intequal(4, exp.line_adds);
cl_assert_intequal(5, exp.line_dels);
git_diff_list_free(diff);
}
void test_diff_workdir__to_tree(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
const char *b_commit = "0017bd4ab1ec3"; /* the start */
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
git_diff_options opts = {0};
git_diff_list *diff = NULL;
git_diff_list *diff2 = NULL;
diff_expects exp;
opts.context_lines = 3;
opts.interhunk_lines = 1;
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
memset(&exp, 0, sizeof(exp));
/* You can't really generate the equivalent of git_diff_workdir_to_tree()
* using C git. It really wants to interpose the index into the diff.
*
* To validate the following results with command line git, I ran the
* following:
* - git ls-tree 26a125
* - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
* The results are documented at the bottom of this file in the
* long comment entitled "PREPARATION OF TEST DATA".
*/
cl_git_pass(git_diff_workdir_to_tree(g_repo, &opts, a, &diff));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 13);
cl_assert(exp.file_adds == 0);
cl_assert(exp.file_dels == 4);
cl_assert(exp.file_mods == 4);
cl_assert(exp.file_ignored == 1);
cl_assert(exp.file_untracked == 4);
/* Since there is no git diff equivalent, let's just assume that the
* text diffs produced by git_diff_foreach are accurate here. We will
* do more apples-to-apples test comparison below.
*/
git_diff_list_free(diff);
diff = NULL;
memset(&exp, 0, sizeof(exp));
/* This is a compatible emulation of "git diff <sha>" which looks like
* a workdir to tree diff (even though it is not really). This is what
* you would get from "git diff --name-status 26a125ee1bf"
*/
cl_git_pass(git_diff_index_to_tree(g_repo, &opts, a, &diff));
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff2));
cl_git_pass(git_diff_merge(diff, diff2));
git_diff_list_free(diff2);
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 14);
cl_assert(exp.file_adds == 2);
cl_assert(exp.file_dels == 5);
cl_assert(exp.file_mods == 4);
cl_assert(exp.file_ignored == 1);
cl_assert(exp.file_untracked == 2);
cl_assert(exp.hunks == 11);
cl_assert(exp.lines == 17);
cl_assert(exp.line_ctxt == 4);
cl_assert(exp.line_adds == 8);
cl_assert(exp.line_dels == 5);
git_diff_list_free(diff);
diff = NULL;
memset(&exp, 0, sizeof(exp));
/* Again, emulating "git diff <sha>" for testing purposes using
* "git diff --name-status 0017bd4ab1ec3" instead.
*/
cl_git_pass(git_diff_index_to_tree(g_repo, &opts, b, &diff));
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff2));
cl_git_pass(git_diff_merge(diff, diff2));
git_diff_list_free(diff2);
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 15);
cl_assert(exp.file_adds == 5);
cl_assert(exp.file_dels == 4);
cl_assert(exp.file_mods == 3);
cl_assert(exp.file_ignored == 1);
cl_assert(exp.file_untracked == 2);
cl_assert(exp.hunks == 12);
cl_assert(exp.lines == 19);
cl_assert(exp.line_ctxt == 3);
cl_assert(exp.line_adds == 12);
cl_assert(exp.line_dels == 4);
git_tree_free(a);
git_tree_free(b);
}
/* PREPARATION OF TEST DATA
*
* Since there is no command line equivalent of git_diff_workdir_to_tree,
* it was a bit of a pain to confirm that I was getting the expected
* results in the first part of this tests. Here is what I ended up
* doing to set my expectation for the file counts and results:
*
* Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
*
* A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
* B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
* C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
* D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
* E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
* F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
* G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
* H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
* I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
* J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
* K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
* L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
*
* --------
*
* find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
*
* A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
* M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
* C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
* N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
* D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
* F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
* H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
* O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
* P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
* I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
* K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
* Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
* L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
*
* --------
*
* A - current_file (UNMODIFIED) -> not in results
* B D file_deleted
* M I ignored_file (IGNORED)
* C M modified_file
* N U new_file (UNTRACKED)
* D M staged_changes
* E D staged_changes_file_deleted
* F M staged_changes_modified_file
* G D staged_delete_file_deleted
* H - staged_delete_modified_file (UNMODIFIED) -> not in results
* O U staged_new_file
* P U staged_new_file_modified_file
* I - subdir/current_file (UNMODIFIED) -> not in results
* J D subdir/deleted_file
* K M subdir/modified_file
* Q U subdir/new_file
* L - subdir.txt (UNMODIFIED) -> not in results
*
* Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
*/
...@@ -29,7 +29,7 @@ static const char *entry_paths0[] = { ...@@ -29,7 +29,7 @@ static const char *entry_paths0[] = {
static const unsigned int entry_statuses0[] = { static const unsigned int entry_statuses0[] = {
GIT_STATUS_WT_DELETED, GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED, GIT_STATUS_WT_IGNORED,
GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_MODIFIED, GIT_STATUS_INDEX_MODIFIED,
......
...@@ -143,7 +143,7 @@ void test_status_worktree__ignores(void) ...@@ -143,7 +143,7 @@ void test_status_worktree__ignores(void)
for (i = 0; i < (int)entry_count0; i++) { for (i = 0; i < (int)entry_count0; i++) {
cl_git_pass(git_status_should_ignore(_repository, entry_paths0[i], &ignored)); cl_git_pass(git_status_should_ignore(_repository, entry_paths0[i], &ignored));
cl_assert(ignored == (entry_statuses0[i] == GIT_STATUS_IGNORED)); cl_assert(ignored == (entry_statuses0[i] == GIT_STATUS_WT_IGNORED));
} }
cl_git_pass(git_status_should_ignore(_repository, "nonexistent_file", &ignored)); cl_git_pass(git_status_should_ignore(_repository, "nonexistent_file", &ignored));
......
...@@ -83,7 +83,7 @@ static const char *entry_paths0[] = { ...@@ -83,7 +83,7 @@ static const char *entry_paths0[] = {
static const unsigned int entry_statuses0[] = { static const unsigned int entry_statuses0[] = {
GIT_STATUS_WT_DELETED, GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED, GIT_STATUS_WT_IGNORED,
GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_MODIFIED, GIT_STATUS_INDEX_MODIFIED,
...@@ -205,7 +205,7 @@ static const char *entry_paths2[] = { ...@@ -205,7 +205,7 @@ static const char *entry_paths2[] = {
static const unsigned int entry_statuses2[] = { static const unsigned int entry_statuses2[] = {
GIT_STATUS_WT_DELETED, GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED, GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED, GIT_STATUS_WT_IGNORED,
GIT_STATUS_WT_DELETED, GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED, GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED, GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
...@@ -291,7 +291,7 @@ static const unsigned int entry_statuses3[] = { ...@@ -291,7 +291,7 @@ static const unsigned int entry_statuses3[] = {
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED, GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED, GIT_STATUS_WT_IGNORED,
GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_MODIFIED, GIT_STATUS_INDEX_MODIFIED,
......
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