mirror of
https://git.proxmox.com/git/libgit2
synced 2025-06-24 07:03:21 +00:00
Merge pull request #1651 from arrbee/status_indexed_updates
Status indexed updates
This commit is contained in:
commit
c09810eedf
@ -42,6 +42,7 @@ typedef enum {
|
|||||||
GIT_STATUS_WT_MODIFIED = (1u << 8),
|
GIT_STATUS_WT_MODIFIED = (1u << 8),
|
||||||
GIT_STATUS_WT_DELETED = (1u << 9),
|
GIT_STATUS_WT_DELETED = (1u << 9),
|
||||||
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
|
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
|
||||||
|
GIT_STATUS_WT_RENAMED = (1u << 11),
|
||||||
|
|
||||||
GIT_STATUS_IGNORED = (1u << 14),
|
GIT_STATUS_IGNORED = (1u << 14),
|
||||||
} git_status_t;
|
} git_status_t;
|
||||||
@ -58,44 +59,20 @@ typedef enum {
|
|||||||
typedef int (*git_status_cb)(
|
typedef int (*git_status_cb)(
|
||||||
const char *path, unsigned int status_flags, void *payload);
|
const char *path, unsigned int status_flags, void *payload);
|
||||||
|
|
||||||
/**
|
|
||||||
* Gather file statuses and run a callback for each one.
|
|
||||||
*
|
|
||||||
* The callback is passed the path of the file, the status (a combination of
|
|
||||||
* the `git_status_t` values above) and the `payload` data pointer passed
|
|
||||||
* into this function.
|
|
||||||
*
|
|
||||||
* If the callback returns a non-zero value, this function will stop looping
|
|
||||||
* and return GIT_EUSER.
|
|
||||||
*
|
|
||||||
* @param repo A repository object
|
|
||||||
* @param callback The function to call on each file
|
|
||||||
* @param payload Pointer to pass through to callback function
|
|
||||||
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_status_foreach(
|
|
||||||
git_repository *repo,
|
|
||||||
git_status_cb callback,
|
|
||||||
void *payload);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For extended status, select the files on which to report status.
|
* For extended status, select the files on which to report status.
|
||||||
*
|
*
|
||||||
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the
|
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly
|
||||||
* rough equivalent of `git status --porcelain` where each file
|
* matches `git status --porcelain` where each file gets a callback
|
||||||
* will receive a callback indicating its status in the index and
|
* indicating its status in the index and in the working directory.
|
||||||
* in the workdir.
|
* - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
|
||||||
* - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index
|
* comparison, not looking at working directory changes.
|
||||||
* side of status. The status of the index contents relative to
|
* - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
|
||||||
* the HEAD will be given.
|
* working directory comparison, not comparing the index to the HEAD.
|
||||||
* - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the
|
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR runs index-only then workdir-only,
|
||||||
* workdir side of status, reporting the status of workdir content
|
* issuing (up to) two callbacks per file (first index, then workdir).
|
||||||
* relative to the index.
|
* This is slightly more efficient than separate calls and can make it
|
||||||
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only
|
* easier to emulate plain `git status` text output.
|
||||||
* followed by workdir-only, causing two callbacks to be issued
|
|
||||||
* per file (first index then workdir). This is slightly more
|
|
||||||
* efficient than making separate calls. This makes it easier to
|
|
||||||
* emulate the output of a plain `git status`.
|
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
|
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
|
||||||
@ -110,26 +87,30 @@ typedef enum {
|
|||||||
* - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
|
* - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
|
||||||
* on untracked files. These will only be made if the workdir files are
|
* on untracked files. These will only be made if the workdir files are
|
||||||
* included in the status "show" option.
|
* included in the status "show" option.
|
||||||
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should get
|
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks.
|
||||||
* callbacks. Again, these callbacks will only be made if the workdir
|
* Again, these callbacks will only be made if the workdir files are
|
||||||
* files are included in the status "show" option. Right now, there is
|
* included in the status "show" option.
|
||||||
* no option to include all files in directories that are ignored
|
|
||||||
* completely.
|
|
||||||
* - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
|
* - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
|
||||||
* made even on unmodified files.
|
* made even on unmodified files.
|
||||||
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories which
|
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be
|
||||||
* appear to be submodules should just be skipped over.
|
* skipped. This only applies if there are no pending typechanges to
|
||||||
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the contents of
|
* the submodule (either from or to another type).
|
||||||
* untracked directories should be included in the status. Normally if
|
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in
|
||||||
* an entire directory is new, then just the top-level directory will be
|
* untracked directories should be included. Normally if an entire
|
||||||
* included (with a trailing slash on the entry name). Given this flag,
|
* directory is new, then just the top-level directory is included (with
|
||||||
* the directory itself will not be included, but all the files in it
|
* a trailing slash on the entry name). This flag says to include all
|
||||||
* will.
|
* of the individual files in the directory instead.
|
||||||
* - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
|
* - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
|
||||||
* will be treated as a literal path, and not as a pathspec.
|
* should be treated as a literal path, and not as a pathspec pattern.
|
||||||
* - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
|
* - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
|
||||||
* ignored directories should be included in the status. This is like
|
* ignored directories should be included in the status. This is like
|
||||||
* doing `git ls-files -o -i --exclude-standard` with core git.
|
* doing `git ls-files -o -i --exclude-standard` with core git.
|
||||||
|
* - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
|
||||||
|
* should be processed between the head and the index and enables
|
||||||
|
* the GIT_STATUS_INDEX_RENAMED as a possible status flag.
|
||||||
|
* - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates tha rename
|
||||||
|
* detection should be run between the index and the working directory
|
||||||
|
* and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
|
||||||
*
|
*
|
||||||
* Calling `git_status_foreach()` is like calling the extended version
|
* Calling `git_status_foreach()` is like calling the extended version
|
||||||
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
|
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
|
||||||
@ -144,6 +125,8 @@ typedef enum {
|
|||||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
||||||
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
||||||
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
|
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
|
||||||
|
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
|
||||||
|
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
|
||||||
} git_status_opt_t;
|
} git_status_opt_t;
|
||||||
|
|
||||||
#define GIT_STATUS_OPT_DEFAULTS \
|
#define GIT_STATUS_OPT_DEFAULTS \
|
||||||
@ -177,6 +160,47 @@ typedef struct {
|
|||||||
#define GIT_STATUS_OPTIONS_VERSION 1
|
#define GIT_STATUS_OPTIONS_VERSION 1
|
||||||
#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION}
|
#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A status entry, providing the differences between the file as it exists
|
||||||
|
* in HEAD and the index, and providing the differences between the index
|
||||||
|
* and the working directory.
|
||||||
|
*
|
||||||
|
* The `status` value provides the status flags for this file.
|
||||||
|
*
|
||||||
|
* The `head_to_index` value provides detailed information about the
|
||||||
|
* differences between the file in HEAD and the file in the index.
|
||||||
|
*
|
||||||
|
* The `index_to_workdir` value provides detailed information about the
|
||||||
|
* differences between the file in the index and the file in the
|
||||||
|
* working directory.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
git_status_t status;
|
||||||
|
git_diff_delta *head_to_index;
|
||||||
|
git_diff_delta *index_to_workdir;
|
||||||
|
} git_status_entry;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather file statuses and run a callback for each one.
|
||||||
|
*
|
||||||
|
* The callback is passed the path of the file, the status (a combination of
|
||||||
|
* the `git_status_t` values above) and the `payload` data pointer passed
|
||||||
|
* into this function.
|
||||||
|
*
|
||||||
|
* If the callback returns a non-zero value, this function will stop looping
|
||||||
|
* and return GIT_EUSER.
|
||||||
|
*
|
||||||
|
* @param repo A repository object
|
||||||
|
* @param callback The function to call on each file
|
||||||
|
* @param payload Pointer to pass through to callback function
|
||||||
|
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_status_foreach(
|
||||||
|
git_repository *repo,
|
||||||
|
git_status_cb callback,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gather file status information and run callbacks as requested.
|
* Gather file status information and run callbacks as requested.
|
||||||
*
|
*
|
||||||
@ -215,6 +239,49 @@ GIT_EXTERN(int) git_status_file(
|
|||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *path);
|
const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather file status information and populate the `git_status_list`.
|
||||||
|
*
|
||||||
|
* @param out Pointer to store the status results in
|
||||||
|
* @param repo Repository object
|
||||||
|
* @param opts Status options structure
|
||||||
|
* @return 0 on success or error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_status_list_new(
|
||||||
|
git_status_list **out,
|
||||||
|
git_repository *repo,
|
||||||
|
const git_status_options *opts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the count of status entries in this list.
|
||||||
|
*
|
||||||
|
* @param statuslist Existing status list object
|
||||||
|
* @return the number of status entries
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(size_t) git_status_list_entrycount(
|
||||||
|
git_status_list *statuslist);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a pointer to one of the entries in the status list.
|
||||||
|
*
|
||||||
|
* The entry is not modifiable and should not be freed.
|
||||||
|
*
|
||||||
|
* @param statuslist Existing status list object
|
||||||
|
* @param idx Position of the entry
|
||||||
|
* @return Pointer to the entry; NULL if out of bounds
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const git_status_entry *) git_status_byindex(
|
||||||
|
git_status_list *statuslist,
|
||||||
|
size_t idx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free an existing status list
|
||||||
|
*
|
||||||
|
* @param statuslist Existing status list object
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_status_list_free(
|
||||||
|
git_status_list *statuslist);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if the ignore rules apply to a given file.
|
* Test if the ignore rules apply to a given file.
|
||||||
*
|
*
|
||||||
|
@ -174,6 +174,9 @@ typedef struct git_reference_iterator git_reference_iterator;
|
|||||||
/** Merge heads, the input to merge */
|
/** Merge heads, the input to merge */
|
||||||
typedef struct git_merge_head git_merge_head;
|
typedef struct git_merge_head git_merge_head;
|
||||||
|
|
||||||
|
/** Representation of a status collection */
|
||||||
|
typedef struct git_status_list git_status_list;
|
||||||
|
|
||||||
|
|
||||||
/** Basic type of any Git reference. */
|
/** Basic type of any Git reference. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
106
src/diff.c
106
src/diff.c
@ -134,6 +134,7 @@ static int diff_delta__from_two(
|
|||||||
{
|
{
|
||||||
git_diff_delta *delta;
|
git_diff_delta *delta;
|
||||||
int notify_res;
|
int notify_res;
|
||||||
|
const char *canonical_path = old_entry->path;
|
||||||
|
|
||||||
if (status == GIT_DELTA_UNMODIFIED &&
|
if (status == GIT_DELTA_UNMODIFIED &&
|
||||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
|
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
|
||||||
@ -153,7 +154,7 @@ static int diff_delta__from_two(
|
|||||||
new_mode = temp_mode;
|
new_mode = temp_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
delta = diff_delta__alloc(diff, status, old_entry->path);
|
delta = diff_delta__alloc(diff, status, canonical_path);
|
||||||
GITERR_CHECK_ALLOC(delta);
|
GITERR_CHECK_ALLOC(delta);
|
||||||
|
|
||||||
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
|
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
|
||||||
@ -253,6 +254,13 @@ int git_diff_delta__cmp(const void *a, const void *b)
|
|||||||
return val ? val : ((int)da->status - (int)db->status);
|
return val ? val : ((int)da->status - (int)db->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_diff_delta__casecmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const git_diff_delta *da = a, *db = b;
|
||||||
|
int val = strcasecmp(diff_delta__path(da), diff_delta__path(db));
|
||||||
|
return val ? val : ((int)da->status - (int)db->status);
|
||||||
|
}
|
||||||
|
|
||||||
bool git_diff_delta__should_skip(
|
bool git_diff_delta__should_skip(
|
||||||
const git_diff_options *opts, const git_diff_delta *delta)
|
const git_diff_options *opts, const git_diff_delta *delta)
|
||||||
{
|
{
|
||||||
@ -356,6 +364,8 @@ static git_diff_list *diff_list_alloc(
|
|||||||
diff->strncomp = git__strncasecmp;
|
diff->strncomp = git__strncasecmp;
|
||||||
diff->pfxcomp = git__prefixcmp_icase;
|
diff->pfxcomp = git__prefixcmp_icase;
|
||||||
diff->entrycomp = git_index_entry__cmp_icase;
|
diff->entrycomp = git_index_entry__cmp_icase;
|
||||||
|
|
||||||
|
diff->deltas._cmp = git_diff_delta__casecmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff;
|
return diff;
|
||||||
@ -1119,17 +1129,40 @@ int git_diff_tree_to_index(
|
|||||||
const git_diff_options *opts)
|
const git_diff_options *opts)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
bool reset_index_ignore_case = false;
|
||||||
|
|
||||||
assert(diff && repo);
|
assert(diff && repo);
|
||||||
|
|
||||||
if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
|
if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
if (index->ignore_case) {
|
||||||
|
git_index__set_ignore_case(index, false);
|
||||||
|
reset_index_ignore_case = true;
|
||||||
|
}
|
||||||
|
|
||||||
DIFF_FROM_ITERATORS(
|
DIFF_FROM_ITERATORS(
|
||||||
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
|
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
|
||||||
git_iterator_for_index(&b, index, 0, pfx, pfx)
|
git_iterator_for_index(&b, index, 0, pfx, pfx)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (reset_index_ignore_case) {
|
||||||
|
git_index__set_ignore_case(index, true);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
git_diff_list *d = *diff;
|
||||||
|
|
||||||
|
d->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
|
||||||
|
d->strcomp = git__strcasecmp;
|
||||||
|
d->strncomp = git__strncasecmp;
|
||||||
|
d->pfxcomp = git__prefixcmp_icase;
|
||||||
|
d->entrycomp = git_index_entry__cmp_icase;
|
||||||
|
|
||||||
|
d->deltas._cmp = git_diff_delta__casecmp;
|
||||||
|
git_vector_sort(&d->deltas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1196,51 +1229,78 @@ size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int git_diff__paired_foreach(
|
int git_diff__paired_foreach(
|
||||||
git_diff_list *idx2head,
|
git_diff_list *head2idx,
|
||||||
git_diff_list *wd2idx,
|
git_diff_list *idx2wd,
|
||||||
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
|
||||||
void *payload)
|
void *payload)
|
||||||
{
|
{
|
||||||
int cmp;
|
int cmp;
|
||||||
git_diff_delta *i2h, *w2i;
|
git_diff_delta *h2i, *i2w;
|
||||||
size_t i, j, i_max, j_max;
|
size_t i, j, i_max, j_max;
|
||||||
int (*strcomp)(const char *, const char *);
|
int (*strcomp)(const char *, const char *) = git__strcmp;
|
||||||
|
bool icase_mismatch;
|
||||||
|
|
||||||
i_max = idx2head ? idx2head->deltas.length : 0;
|
i_max = head2idx ? head2idx->deltas.length : 0;
|
||||||
j_max = wd2idx ? wd2idx->deltas.length : 0;
|
j_max = idx2wd ? idx2wd->deltas.length : 0;
|
||||||
|
|
||||||
/* Get appropriate strcmp function */
|
/* At some point, tree-to-index diffs will probably never ignore case,
|
||||||
strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL;
|
* even if that isn't true now. Index-to-workdir diffs may or may not
|
||||||
|
* ignore case, but the index filename for the idx2wd diff should
|
||||||
/* Assert both iterators use matching ignore-case. If this function ever
|
* still be using the canonical case-preserving name.
|
||||||
* supports merging diffs that are not sorted by the same function, then
|
*
|
||||||
* it will need to spool and sort on one of the results before merging
|
* Therefore the main thing we need to do here is make sure the diffs
|
||||||
|
* are traversed in a compatible order. To do this, we temporarily
|
||||||
|
* resort a mismatched diff to get the order correct.
|
||||||
*/
|
*/
|
||||||
if (idx2head && wd2idx) {
|
icase_mismatch =
|
||||||
assert(idx2head->strcomp == wd2idx->strcomp);
|
(head2idx != NULL && idx2wd != NULL &&
|
||||||
|
((head2idx->opts.flags ^ idx2wd->opts.flags) & GIT_DIFF_DELTAS_ARE_ICASE));
|
||||||
|
|
||||||
|
/* force case-sensitive delta sort */
|
||||||
|
if (icase_mismatch) {
|
||||||
|
if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
|
||||||
|
head2idx->deltas._cmp = git_diff_delta__cmp;
|
||||||
|
git_vector_sort(&head2idx->deltas);
|
||||||
|
} else {
|
||||||
|
idx2wd->deltas._cmp = git_diff_delta__cmp;
|
||||||
|
git_vector_sort(&idx2wd->deltas);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)
|
||||||
|
strcomp = git__strcasecmp;
|
||||||
|
|
||||||
for (i = 0, j = 0; i < i_max || j < j_max; ) {
|
for (i = 0, j = 0; i < i_max || j < j_max; ) {
|
||||||
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
|
h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
|
||||||
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
|
i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
|
||||||
|
|
||||||
cmp = !w2i ? -1 : !i2h ? 1 :
|
cmp = !i2w ? -1 : !h2i ? 1 :
|
||||||
strcomp(i2h->old_file.path, w2i->old_file.path);
|
strcomp(h2i->new_file.path, i2w->old_file.path);
|
||||||
|
|
||||||
if (cmp < 0) {
|
if (cmp < 0) {
|
||||||
if (cb(i2h, NULL, payload))
|
if (cb(h2i, NULL, payload))
|
||||||
return GIT_EUSER;
|
return GIT_EUSER;
|
||||||
i++;
|
i++;
|
||||||
} else if (cmp > 0) {
|
} else if (cmp > 0) {
|
||||||
if (cb(NULL, w2i, payload))
|
if (cb(NULL, i2w, payload))
|
||||||
return GIT_EUSER;
|
return GIT_EUSER;
|
||||||
j++;
|
j++;
|
||||||
} else {
|
} else {
|
||||||
if (cb(i2h, w2i, payload))
|
if (cb(h2i, i2w, payload))
|
||||||
return GIT_EUSER;
|
return GIT_EUSER;
|
||||||
i++; j++;
|
i++; j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* restore case-insensitive delta sort */
|
||||||
|
if (icase_mismatch) {
|
||||||
|
if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
|
||||||
|
head2idx->deltas._cmp = git_diff_delta__casecmp;
|
||||||
|
git_vector_sort(&head2idx->deltas);
|
||||||
|
} else {
|
||||||
|
idx2wd->deltas._cmp = git_diff_delta__casecmp;
|
||||||
|
git_vector_sort(&idx2wd->deltas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
10
src/diff.h
10
src/diff.h
@ -74,6 +74,7 @@ extern void git_diff__cleanup_modes(
|
|||||||
extern void git_diff_list_addref(git_diff_list *diff);
|
extern void git_diff_list_addref(git_diff_list *diff);
|
||||||
|
|
||||||
extern int git_diff_delta__cmp(const void *a, const void *b);
|
extern int git_diff_delta__cmp(const void *a, const void *b);
|
||||||
|
extern int git_diff_delta__casecmp(const void *a, const void *b);
|
||||||
|
|
||||||
extern bool git_diff_delta__should_skip(
|
extern bool git_diff_delta__should_skip(
|
||||||
const git_diff_options *opts, const git_diff_delta *delta);
|
const git_diff_options *opts, const git_diff_delta *delta);
|
||||||
@ -94,17 +95,16 @@ extern int git_diff__paired_foreach(
|
|||||||
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
||||||
void *payload);
|
void *payload);
|
||||||
|
|
||||||
int git_diff_find_similar__hashsig_for_file(
|
extern int git_diff_find_similar__hashsig_for_file(
|
||||||
void **out, const git_diff_file *f, const char *path, void *p);
|
void **out, const git_diff_file *f, const char *path, void *p);
|
||||||
|
|
||||||
int git_diff_find_similar__hashsig_for_buf(
|
extern int git_diff_find_similar__hashsig_for_buf(
|
||||||
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
|
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
|
||||||
|
|
||||||
void git_diff_find_similar__hashsig_free(void *sig, void *payload);
|
extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
|
||||||
|
|
||||||
int git_diff_find_similar__calc_similarity(
|
extern int git_diff_find_similar__calc_similarity(
|
||||||
int *score, void *siga, void *sigb, void *payload);
|
int *score, void *siga, void *sigb, void *payload);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -175,10 +175,11 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if we were previously missing an oid, reassess UNMODIFIED state */
|
/* if we were previously missing an oid, update MODIFIED->UNMODIFIED */
|
||||||
if (incomplete_data &&
|
if (incomplete_data &&
|
||||||
patch->ofile.file.mode == patch->nfile.file.mode &&
|
patch->ofile.file.mode == patch->nfile.file.mode &&
|
||||||
git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid))
|
git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid) &&
|
||||||
|
patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
|
||||||
patch->delta->status = GIT_DELTA_UNMODIFIED;
|
patch->delta->status = GIT_DELTA_UNMODIFIED;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
@ -284,6 +285,7 @@ int git_diff_foreach(
|
|||||||
git_xdiff_init(&xo, &diff->opts);
|
git_xdiff_init(&xo, &diff->opts);
|
||||||
|
|
||||||
git_vector_foreach(&diff->deltas, idx, patch.delta) {
|
git_vector_foreach(&diff->deltas, idx, patch.delta) {
|
||||||
|
|
||||||
/* check flags against patch status */
|
/* check flags against patch status */
|
||||||
if (git_diff_delta__should_skip(&diff->opts, patch.delta))
|
if (git_diff_delta__should_skip(&diff->opts, patch.delta))
|
||||||
continue;
|
continue;
|
||||||
|
109
src/diff_print.c
109
src/diff_print.c
@ -41,7 +41,7 @@ static int diff_print_info_init(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char pick_suffix(int mode)
|
static char diff_pick_suffix(int mode)
|
||||||
{
|
{
|
||||||
if (S_ISDIR(mode))
|
if (S_ISDIR(mode))
|
||||||
return '/';
|
return '/';
|
||||||
@ -76,10 +76,11 @@ static int callback_error(void)
|
|||||||
return GIT_EUSER;
|
return GIT_EUSER;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_compact(
|
static int diff_print_one_compact(
|
||||||
const git_diff_delta *delta, float progress, void *data)
|
const git_diff_delta *delta, float progress, void *data)
|
||||||
{
|
{
|
||||||
diff_print_info *pi = data;
|
diff_print_info *pi = data;
|
||||||
|
git_buf *out = pi->buf;
|
||||||
char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
|
char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
|
||||||
|
|
||||||
GIT_UNUSED(progress);
|
GIT_UNUSED(progress);
|
||||||
@ -87,34 +88,35 @@ static int print_compact(
|
|||||||
if (code == ' ')
|
if (code == ' ')
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
old_suffix = pick_suffix(delta->old_file.mode);
|
old_suffix = diff_pick_suffix(delta->old_file.mode);
|
||||||
new_suffix = pick_suffix(delta->new_file.mode);
|
new_suffix = diff_pick_suffix(delta->new_file.mode);
|
||||||
|
|
||||||
git_buf_clear(pi->buf);
|
git_buf_clear(out);
|
||||||
|
|
||||||
if (delta->old_file.path != delta->new_file.path &&
|
if (delta->old_file.path != delta->new_file.path &&
|
||||||
pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0)
|
pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0)
|
||||||
git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
|
git_buf_printf(out, "%c\t%s%c -> %s%c\n", code,
|
||||||
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
|
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
|
||||||
else if (delta->old_file.mode != delta->new_file.mode &&
|
else if (delta->old_file.mode != delta->new_file.mode &&
|
||||||
delta->old_file.mode != 0 && delta->new_file.mode != 0)
|
delta->old_file.mode != 0 && delta->new_file.mode != 0)
|
||||||
git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
|
git_buf_printf(out, "%c\t%s%c (%o -> %o)\n", code,
|
||||||
delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
|
delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
|
||||||
else if (old_suffix != ' ')
|
else if (old_suffix != ' ')
|
||||||
git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
|
git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
|
||||||
else
|
else
|
||||||
git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
|
git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path);
|
||||||
|
|
||||||
if (git_buf_oom(pi->buf))
|
if (git_buf_oom(out))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
git_buf_cstr(out), git_buf_len(out), pi->payload))
|
||||||
return callback_error();
|
return callback_error();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_list to a print callback in compact format */
|
||||||
int git_diff_print_compact(
|
int git_diff_print_compact(
|
||||||
git_diff_list *diff,
|
git_diff_list *diff,
|
||||||
git_diff_data_cb print_cb,
|
git_diff_data_cb print_cb,
|
||||||
@ -125,17 +127,18 @@ int git_diff_print_compact(
|
|||||||
diff_print_info pi;
|
diff_print_info pi;
|
||||||
|
|
||||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||||
error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi);
|
error = git_diff_foreach(diff, diff_print_one_compact, NULL, NULL, &pi);
|
||||||
|
|
||||||
git_buf_free(&buf);
|
git_buf_free(&buf);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_raw(
|
static int diff_print_one_raw(
|
||||||
const git_diff_delta *delta, float progress, void *data)
|
const git_diff_delta *delta, float progress, void *data)
|
||||||
{
|
{
|
||||||
diff_print_info *pi = data;
|
diff_print_info *pi = data;
|
||||||
|
git_buf *out = pi->buf;
|
||||||
char code = git_diff_status_char(delta->status);
|
char code = git_diff_status_char(delta->status);
|
||||||
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||||
|
|
||||||
@ -144,36 +147,37 @@ static int print_raw(
|
|||||||
if (code == ' ')
|
if (code == ' ')
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
git_buf_clear(pi->buf);
|
git_buf_clear(out);
|
||||||
|
|
||||||
git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
|
git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
|
||||||
git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
|
git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
|
||||||
|
|
||||||
git_buf_printf(
|
git_buf_printf(
|
||||||
pi->buf, ":%06o %06o %s... %s... %c",
|
out, ":%06o %06o %s... %s... %c",
|
||||||
delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
|
delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
|
||||||
|
|
||||||
if (delta->similarity > 0)
|
if (delta->similarity > 0)
|
||||||
git_buf_printf(pi->buf, "%03u", delta->similarity);
|
git_buf_printf(out, "%03u", delta->similarity);
|
||||||
|
|
||||||
if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
|
if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
|
||||||
git_buf_printf(
|
git_buf_printf(
|
||||||
pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
|
out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
|
||||||
else
|
else
|
||||||
git_buf_printf(
|
git_buf_printf(
|
||||||
pi->buf, "\t%s\n", delta->old_file.path ?
|
out, "\t%s\n", delta->old_file.path ?
|
||||||
delta->old_file.path : delta->new_file.path);
|
delta->old_file.path : delta->new_file.path);
|
||||||
|
|
||||||
if (git_buf_oom(pi->buf))
|
if (git_buf_oom(out))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
git_buf_cstr(out), git_buf_len(out), pi->payload))
|
||||||
return callback_error();
|
return callback_error();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_list to a print callback in raw output format */
|
||||||
int git_diff_print_raw(
|
int git_diff_print_raw(
|
||||||
git_diff_list *diff,
|
git_diff_list *diff,
|
||||||
git_diff_data_cb print_cb,
|
git_diff_data_cb print_cb,
|
||||||
@ -184,15 +188,16 @@ int git_diff_print_raw(
|
|||||||
diff_print_info pi;
|
diff_print_info pi;
|
||||||
|
|
||||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||||
error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi);
|
error = git_diff_foreach(diff, diff_print_one_raw, NULL, NULL, &pi);
|
||||||
|
|
||||||
git_buf_free(&buf);
|
git_buf_free(&buf);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
|
static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
|
||||||
{
|
{
|
||||||
|
git_buf *out = pi->buf;
|
||||||
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||||
|
|
||||||
git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
|
git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
|
||||||
@ -200,27 +205,27 @@ static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
|
|||||||
|
|
||||||
/* TODO: Match git diff more closely */
|
/* TODO: Match git diff more closely */
|
||||||
if (delta->old_file.mode == delta->new_file.mode) {
|
if (delta->old_file.mode == delta->new_file.mode) {
|
||||||
git_buf_printf(pi->buf, "index %s..%s %o\n",
|
git_buf_printf(out, "index %s..%s %o\n",
|
||||||
start_oid, end_oid, delta->old_file.mode);
|
start_oid, end_oid, delta->old_file.mode);
|
||||||
} else {
|
} else {
|
||||||
if (delta->old_file.mode == 0) {
|
if (delta->old_file.mode == 0) {
|
||||||
git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
|
git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
|
||||||
} else if (delta->new_file.mode == 0) {
|
} else if (delta->new_file.mode == 0) {
|
||||||
git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
|
git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
|
||||||
} else {
|
} else {
|
||||||
git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
|
git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
|
||||||
git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
|
git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
|
||||||
}
|
}
|
||||||
git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
|
git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (git_buf_oom(pi->buf))
|
if (git_buf_oom(out))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_patch_file(
|
static int diff_print_patch_file(
|
||||||
const git_diff_delta *delta, float progress, void *data)
|
const git_diff_delta *delta, float progress, void *data)
|
||||||
{
|
{
|
||||||
diff_print_info *pi = data;
|
diff_print_info *pi = data;
|
||||||
@ -247,7 +252,7 @@ static int print_patch_file(
|
|||||||
git_buf_clear(pi->buf);
|
git_buf_clear(pi->buf);
|
||||||
git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
|
git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
|
||||||
|
|
||||||
if (print_oid_range(pi, delta) < 0)
|
if (diff_print_oid_range(pi, delta) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (git_oid_iszero(&delta->old_file.oid)) {
|
if (git_oid_iszero(&delta->old_file.oid)) {
|
||||||
@ -288,7 +293,7 @@ static int print_patch_file(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_patch_hunk(
|
static int diff_print_patch_hunk(
|
||||||
const git_diff_delta *d,
|
const git_diff_delta *d,
|
||||||
const git_diff_range *r,
|
const git_diff_range *r,
|
||||||
const char *header,
|
const char *header,
|
||||||
@ -311,7 +316,7 @@ static int print_patch_hunk(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_patch_line(
|
static int diff_print_patch_line(
|
||||||
const git_diff_delta *delta,
|
const git_diff_delta *delta,
|
||||||
const git_diff_range *range,
|
const git_diff_range *range,
|
||||||
char line_origin, /* GIT_DIFF_LINE value from above */
|
char line_origin, /* GIT_DIFF_LINE value from above */
|
||||||
@ -343,6 +348,7 @@ static int print_patch_line(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_list to an output callback in patch format */
|
||||||
int git_diff_print_patch(
|
int git_diff_print_patch(
|
||||||
git_diff_list *diff,
|
git_diff_list *diff,
|
||||||
git_diff_data_cb print_cb,
|
git_diff_data_cb print_cb,
|
||||||
@ -354,27 +360,15 @@ int git_diff_print_patch(
|
|||||||
|
|
||||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||||
error = git_diff_foreach(
|
error = git_diff_foreach(
|
||||||
diff, print_patch_file, print_patch_hunk, print_patch_line, &pi);
|
diff, diff_print_patch_file, diff_print_patch_hunk,
|
||||||
|
diff_print_patch_line, &pi);
|
||||||
|
|
||||||
git_buf_free(&buf);
|
git_buf_free(&buf);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_patch to an output callback */
|
||||||
static int print_to_buffer_cb(
|
|
||||||
const git_diff_delta *delta,
|
|
||||||
const git_diff_range *range,
|
|
||||||
char line_origin,
|
|
||||||
const char *content,
|
|
||||||
size_t content_len,
|
|
||||||
void *payload)
|
|
||||||
{
|
|
||||||
git_buf *output = payload;
|
|
||||||
GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
|
|
||||||
return git_buf_put(output, content, content_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int git_diff_patch_print(
|
int git_diff_patch_print(
|
||||||
git_diff_patch *patch,
|
git_diff_patch *patch,
|
||||||
git_diff_data_cb print_cb,
|
git_diff_data_cb print_cb,
|
||||||
@ -389,13 +383,28 @@ int git_diff_patch_print(
|
|||||||
if (!(error = diff_print_info_init(
|
if (!(error = diff_print_info_init(
|
||||||
&pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
|
&pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
|
||||||
error = git_diff_patch__invoke_callbacks(
|
error = git_diff_patch__invoke_callbacks(
|
||||||
patch, print_patch_file, print_patch_hunk, print_patch_line, &pi);
|
patch, diff_print_patch_file, diff_print_patch_hunk,
|
||||||
|
diff_print_patch_line, &pi);
|
||||||
|
|
||||||
git_buf_free(&temp);
|
git_buf_free(&temp);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int diff_print_to_buffer_cb(
|
||||||
|
const git_diff_delta *delta,
|
||||||
|
const git_diff_range *range,
|
||||||
|
char line_origin,
|
||||||
|
const char *content,
|
||||||
|
size_t content_len,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
git_buf *output = payload;
|
||||||
|
GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
|
||||||
|
return git_buf_put(output, content, content_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_patch to a string buffer */
|
||||||
int git_diff_patch_to_str(
|
int git_diff_patch_to_str(
|
||||||
char **string,
|
char **string,
|
||||||
git_diff_patch *patch)
|
git_diff_patch *patch)
|
||||||
@ -403,7 +412,7 @@ int git_diff_patch_to_str(
|
|||||||
int error;
|
int error;
|
||||||
git_buf output = GIT_BUF_INIT;
|
git_buf output = GIT_BUF_INIT;
|
||||||
|
|
||||||
error = git_diff_patch_print(patch, print_to_buffer_cb, &output);
|
error = git_diff_patch_print(patch, diff_print_to_buffer_cb, &output);
|
||||||
|
|
||||||
/* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
|
/* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
|
||||||
* meaning a memory allocation failure, so just map to -1...
|
* meaning a memory allocation failure, so just map to -1...
|
||||||
|
@ -483,7 +483,7 @@ static int similarity_measure(
|
|||||||
if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
|
if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* if exact match is requested, force calculation of missing OIDs */
|
/* if exact match is requested, force calculation of missing OIDs now */
|
||||||
if (exact_match) {
|
if (exact_match) {
|
||||||
if (git_oid_iszero(&a_file->oid) &&
|
if (git_oid_iszero(&a_file->oid) &&
|
||||||
diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
|
diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
|
||||||
|
@ -734,8 +734,9 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
|
|||||||
if (!replace || !existing)
|
if (!replace || !existing)
|
||||||
return git_vector_insert(&index->entries, entry);
|
return git_vector_insert(&index->entries, entry);
|
||||||
|
|
||||||
/* exists, replace it */
|
/* exists, replace it (preserving name from existing entry) */
|
||||||
git__free((*existing)->path);
|
git__free(entry->path);
|
||||||
|
entry->path = (*existing)->path;
|
||||||
git__free(*existing);
|
git__free(*existing);
|
||||||
*existing = entry;
|
*existing = entry;
|
||||||
|
|
||||||
|
452
src/status.c
452
src/status.c
@ -11,6 +11,7 @@
|
|||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
#include "status.h"
|
||||||
#include "git2/status.h"
|
#include "git2/status.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
#include "ignore.h"
|
#include "ignore.h"
|
||||||
@ -19,11 +20,11 @@
|
|||||||
#include "git2/diff.h"
|
#include "git2/diff.h"
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
|
|
||||||
static unsigned int index_delta2status(git_delta_t index_status)
|
static unsigned int index_delta2status(const git_diff_delta *head2idx)
|
||||||
{
|
{
|
||||||
unsigned int st = GIT_STATUS_CURRENT;
|
git_status_t st = GIT_STATUS_CURRENT;
|
||||||
|
|
||||||
switch (index_status) {
|
switch (head2idx->status) {
|
||||||
case GIT_DELTA_ADDED:
|
case GIT_DELTA_ADDED:
|
||||||
case GIT_DELTA_COPIED:
|
case GIT_DELTA_COPIED:
|
||||||
st = GIT_STATUS_INDEX_NEW;
|
st = GIT_STATUS_INDEX_NEW;
|
||||||
@ -36,6 +37,9 @@ static unsigned int index_delta2status(git_delta_t index_status)
|
|||||||
break;
|
break;
|
||||||
case GIT_DELTA_RENAMED:
|
case GIT_DELTA_RENAMED:
|
||||||
st = GIT_STATUS_INDEX_RENAMED;
|
st = GIT_STATUS_INDEX_RENAMED;
|
||||||
|
|
||||||
|
if (!git_oid_equal(&head2idx->old_file.oid, &head2idx->new_file.oid))
|
||||||
|
st |= GIT_STATUS_INDEX_MODIFIED;
|
||||||
break;
|
break;
|
||||||
case GIT_DELTA_TYPECHANGE:
|
case GIT_DELTA_TYPECHANGE:
|
||||||
st = GIT_STATUS_INDEX_TYPECHANGE;
|
st = GIT_STATUS_INDEX_TYPECHANGE;
|
||||||
@ -47,13 +51,13 @@ static unsigned int index_delta2status(git_delta_t index_status)
|
|||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int workdir_delta2status(git_delta_t workdir_status)
|
static unsigned int workdir_delta2status(
|
||||||
|
git_diff_list *diff, git_diff_delta *idx2wd)
|
||||||
{
|
{
|
||||||
unsigned int st = GIT_STATUS_CURRENT;
|
git_status_t st = GIT_STATUS_CURRENT;
|
||||||
|
|
||||||
switch (workdir_status) {
|
switch (idx2wd->status) {
|
||||||
case GIT_DELTA_ADDED:
|
case GIT_DELTA_ADDED:
|
||||||
case GIT_DELTA_RENAMED:
|
|
||||||
case GIT_DELTA_COPIED:
|
case GIT_DELTA_COPIED:
|
||||||
case GIT_DELTA_UNTRACKED:
|
case GIT_DELTA_UNTRACKED:
|
||||||
st = GIT_STATUS_WT_NEW;
|
st = GIT_STATUS_WT_NEW;
|
||||||
@ -67,6 +71,31 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
|
|||||||
case GIT_DELTA_IGNORED:
|
case GIT_DELTA_IGNORED:
|
||||||
st = GIT_STATUS_IGNORED;
|
st = GIT_STATUS_IGNORED;
|
||||||
break;
|
break;
|
||||||
|
case GIT_DELTA_RENAMED:
|
||||||
|
st = GIT_STATUS_WT_RENAMED;
|
||||||
|
|
||||||
|
if (!git_oid_equal(&idx2wd->old_file.oid, &idx2wd->new_file.oid)) {
|
||||||
|
/* if OIDs don't match, we might need to calculate them now to
|
||||||
|
* discern between RENAMED vs RENAMED+MODIFED
|
||||||
|
*/
|
||||||
|
if (git_oid_iszero(&idx2wd->old_file.oid) &&
|
||||||
|
diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
|
||||||
|
!git_diff__oid_for_file(
|
||||||
|
diff->repo, idx2wd->old_file.path, idx2wd->old_file.mode,
|
||||||
|
idx2wd->old_file.size, &idx2wd->old_file.oid))
|
||||||
|
idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||||
|
|
||||||
|
if (git_oid_iszero(&idx2wd->new_file.oid) &&
|
||||||
|
diff->new_src == GIT_ITERATOR_TYPE_WORKDIR &&
|
||||||
|
!git_diff__oid_for_file(
|
||||||
|
diff->repo, idx2wd->new_file.path, idx2wd->new_file.mode,
|
||||||
|
idx2wd->new_file.size, &idx2wd->new_file.oid))
|
||||||
|
idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||||
|
|
||||||
|
if (!git_oid_equal(&idx2wd->old_file.oid, &idx2wd->new_file.oid))
|
||||||
|
st |= GIT_STATUS_WT_MODIFIED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case GIT_DELTA_TYPECHANGE:
|
case GIT_DELTA_TYPECHANGE:
|
||||||
st = GIT_STATUS_WT_TYPECHANGE;
|
st = GIT_STATUS_WT_TYPECHANGE;
|
||||||
break;
|
break;
|
||||||
@ -77,43 +106,285 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
|
|||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
static bool status_is_included(
|
||||||
git_status_cb cb;
|
git_status_list *status,
|
||||||
void *payload;
|
git_diff_delta *head2idx,
|
||||||
const git_status_options *opts;
|
git_diff_delta *idx2wd)
|
||||||
} status_user_callback;
|
|
||||||
|
|
||||||
static int status_invoke_cb(
|
|
||||||
git_diff_delta *h2i, git_diff_delta *i2w, void *payload)
|
|
||||||
{
|
{
|
||||||
status_user_callback *usercb = payload;
|
if (!(status->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES))
|
||||||
const char *path = NULL;
|
return 1;
|
||||||
unsigned int status = 0;
|
|
||||||
|
|
||||||
if (i2w) {
|
|
||||||
path = i2w->old_file.path;
|
|
||||||
status |= workdir_delta2status(i2w->status);
|
|
||||||
}
|
|
||||||
if (h2i) {
|
|
||||||
path = h2i->old_file.path;
|
|
||||||
status |= index_delta2status(h2i->status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if excluding submodules and this is a submodule everywhere */
|
/* if excluding submodules and this is a submodule everywhere */
|
||||||
if (usercb->opts &&
|
if (head2idx) {
|
||||||
(usercb->opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
|
if (head2idx->status != GIT_DELTA_ADDED &&
|
||||||
{
|
head2idx->old_file.mode != GIT_FILEMODE_COMMIT)
|
||||||
bool in_tree = (h2i && h2i->status != GIT_DELTA_ADDED);
|
return 1;
|
||||||
bool in_index = (h2i && h2i->status != GIT_DELTA_DELETED);
|
if (head2idx->status != GIT_DELTA_DELETED &&
|
||||||
bool in_wd = (i2w && i2w->status != GIT_DELTA_DELETED);
|
head2idx->new_file.mode != GIT_FILEMODE_COMMIT)
|
||||||
|
return 1;
|
||||||
if ((!in_tree || h2i->old_file.mode == GIT_FILEMODE_COMMIT) &&
|
}
|
||||||
(!in_index || h2i->new_file.mode == GIT_FILEMODE_COMMIT) &&
|
if (idx2wd) {
|
||||||
(!in_wd || i2w->new_file.mode == GIT_FILEMODE_COMMIT))
|
if (idx2wd->status != GIT_DELTA_ADDED &&
|
||||||
return 0;
|
idx2wd->old_file.mode != GIT_FILEMODE_COMMIT)
|
||||||
|
return 1;
|
||||||
|
if (idx2wd->status != GIT_DELTA_DELETED &&
|
||||||
|
idx2wd->new_file.mode != GIT_FILEMODE_COMMIT)
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return usercb->cb(path, status, usercb->payload);
|
/* only get here if every valid mode is GIT_FILEMODE_COMMIT */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static git_status_t status_compute(
|
||||||
|
git_status_list *status,
|
||||||
|
git_diff_delta *head2idx,
|
||||||
|
git_diff_delta *idx2wd)
|
||||||
|
{
|
||||||
|
git_status_t st = GIT_STATUS_CURRENT;
|
||||||
|
|
||||||
|
if (head2idx)
|
||||||
|
st |= index_delta2status(head2idx);
|
||||||
|
|
||||||
|
if (idx2wd)
|
||||||
|
st |= workdir_delta2status(status->idx2wd, idx2wd);
|
||||||
|
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int status_collect(
|
||||||
|
git_diff_delta *head2idx,
|
||||||
|
git_diff_delta *idx2wd,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
git_status_list *status = payload;
|
||||||
|
git_status_entry *status_entry;
|
||||||
|
|
||||||
|
if (!status_is_included(status, head2idx, idx2wd))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
status_entry = git__malloc(sizeof(git_status_entry));
|
||||||
|
GITERR_CHECK_ALLOC(status_entry);
|
||||||
|
|
||||||
|
status_entry->status = status_compute(status, head2idx, idx2wd);
|
||||||
|
status_entry->head_to_index = head2idx;
|
||||||
|
status_entry->index_to_workdir = idx2wd;
|
||||||
|
|
||||||
|
return git_vector_insert(&status->paired, status_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIT_INLINE(int) status_entry_cmp_base(
|
||||||
|
const void *a,
|
||||||
|
const void *b,
|
||||||
|
int (*strcomp)(const char *a, const char *b))
|
||||||
|
{
|
||||||
|
const git_status_entry *entry_a = a;
|
||||||
|
const git_status_entry *entry_b = b;
|
||||||
|
const git_diff_delta *delta_a, *delta_b;
|
||||||
|
|
||||||
|
delta_a = entry_a->index_to_workdir ? entry_a->index_to_workdir :
|
||||||
|
entry_a->head_to_index;
|
||||||
|
delta_b = entry_b->index_to_workdir ? entry_b->index_to_workdir :
|
||||||
|
entry_b->head_to_index;
|
||||||
|
|
||||||
|
if (!delta_a && delta_b)
|
||||||
|
return -1;
|
||||||
|
if (delta_a && !delta_b)
|
||||||
|
return 1;
|
||||||
|
if (!delta_a && !delta_b)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return strcomp(delta_a->new_file.path, delta_b->new_file.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int status_entry_icmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return status_entry_cmp_base(a, b, git__strcasecmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int status_entry_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return status_entry_cmp_base(a, b, git__strcmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static git_status_list *git_status_list_alloc(git_index *index)
|
||||||
|
{
|
||||||
|
git_status_list *status = NULL;
|
||||||
|
int (*entrycmp)(const void *a, const void *b);
|
||||||
|
|
||||||
|
if (!(status = git__calloc(1, sizeof(git_status_list))))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
entrycmp = index->ignore_case ? status_entry_icmp : status_entry_cmp;
|
||||||
|
|
||||||
|
if (git_vector_init(&status->paired, 0, entrycmp) < 0) {
|
||||||
|
git__free(status);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
static int newfile_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const git_diff_delta *delta_a = a;
|
||||||
|
const git_diff_delta *delta_b = b;
|
||||||
|
|
||||||
|
return git__strcmp(delta_a->new_file.path, delta_b->new_file.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int newfile_casecmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const git_diff_delta *delta_a = a;
|
||||||
|
const git_diff_delta *delta_b = b;
|
||||||
|
|
||||||
|
return git__strcasecmp(delta_a->new_file.path, delta_b->new_file.path);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
int git_status_list_new(
|
||||||
|
git_status_list **out,
|
||||||
|
git_repository *repo,
|
||||||
|
const git_status_options *opts)
|
||||||
|
{
|
||||||
|
git_index *index = NULL;
|
||||||
|
git_status_list *status = NULL;
|
||||||
|
git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
|
||||||
|
git_diff_find_options findopts_i2w = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||||
|
git_tree *head = NULL;
|
||||||
|
git_status_show_t show =
|
||||||
|
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||||
|
int error = 0;
|
||||||
|
unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS;
|
||||||
|
|
||||||
|
assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
|
||||||
|
|
||||||
|
*out = NULL;
|
||||||
|
|
||||||
|
GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
|
||||||
|
|
||||||
|
if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 ||
|
||||||
|
(error = git_repository_index(&index, repo)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* if there is no HEAD, that's okay - we'll make an empty iterator */
|
||||||
|
if (((error = git_repository_head_tree(&head, repo)) < 0) &&
|
||||||
|
error != GIT_ENOTFOUND && error != GIT_EORPHANEDHEAD) {
|
||||||
|
git_index_free(index); /* release index */
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = git_status_list_alloc(index);
|
||||||
|
GITERR_CHECK_ALLOC(status);
|
||||||
|
|
||||||
|
if (opts) {
|
||||||
|
memcpy(&status->opts, opts, sizeof(git_status_options));
|
||||||
|
memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
|
||||||
|
}
|
||||||
|
|
||||||
|
diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
|
||||||
|
|
||||||
|
if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
|
||||||
|
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||||
|
if ((flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
|
||||||
|
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
|
||||||
|
if ((flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
|
||||||
|
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
|
||||||
|
if ((flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
|
||||||
|
diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
|
||||||
|
if ((flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0)
|
||||||
|
diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
|
||||||
|
if ((flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0)
|
||||||
|
diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS;
|
||||||
|
if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
|
||||||
|
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
|
||||||
|
|
||||||
|
findopts_i2w.flags |= GIT_DIFF_FIND_FOR_UNTRACKED;
|
||||||
|
|
||||||
|
if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
|
||||||
|
if ((error = git_diff_tree_to_index(
|
||||||
|
&status->head2idx, repo, head, NULL, &diffopt)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 &&
|
||||||
|
(error = git_diff_find_similar(status->head2idx, NULL)) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
|
||||||
|
if ((error = git_diff_index_to_workdir(
|
||||||
|
&status->idx2wd, repo, NULL, &diffopt)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
|
||||||
|
(error = git_diff_find_similar(status->idx2wd, &findopts_i2w)) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
|
||||||
|
if ((error = git_diff__paired_foreach(
|
||||||
|
status->head2idx, NULL, status_collect, status)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
git_diff_list_free(status->head2idx);
|
||||||
|
status->head2idx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = git_diff__paired_foreach(
|
||||||
|
status->head2idx, status->idx2wd, status_collect, status)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 ||
|
||||||
|
(flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0)
|
||||||
|
git_vector_sort(&status->paired);
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (error < 0) {
|
||||||
|
git_status_list_free(status);
|
||||||
|
status = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = status;
|
||||||
|
|
||||||
|
git_tree_free(head);
|
||||||
|
git_index_free(index);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t git_status_list_entrycount(git_status_list *status)
|
||||||
|
{
|
||||||
|
assert(status);
|
||||||
|
|
||||||
|
return status->paired.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const git_status_entry *git_status_byindex(git_status_list *status, size_t i)
|
||||||
|
{
|
||||||
|
assert(status);
|
||||||
|
|
||||||
|
return git_vector_get(&status->paired, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_status_list_free(git_status_list *status)
|
||||||
|
{
|
||||||
|
git_status_entry *status_entry;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (status == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
git_diff_list_free(status->head2idx);
|
||||||
|
git_diff_list_free(status->idx2wd);
|
||||||
|
|
||||||
|
git_vector_foreach(&status->paired, i, status_entry)
|
||||||
|
git__free(status_entry);
|
||||||
|
|
||||||
|
git_vector_free(&status->paired);
|
||||||
|
|
||||||
|
git__memzero(status, sizeof(*status));
|
||||||
|
git__free(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_status_foreach_ext(
|
int git_status_foreach_ext(
|
||||||
@ -122,97 +393,34 @@ int git_status_foreach_ext(
|
|||||||
git_status_cb cb,
|
git_status_cb cb,
|
||||||
void *payload)
|
void *payload)
|
||||||
{
|
{
|
||||||
int err = 0;
|
git_status_list *status;
|
||||||
git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
|
const git_status_entry *status_entry;
|
||||||
git_diff_list *head2idx = NULL, *idx2wd = NULL;
|
size_t i;
|
||||||
git_tree *head = NULL;
|
int error = 0;
|
||||||
git_status_show_t show =
|
|
||||||
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
|
||||||
status_user_callback usercb;
|
|
||||||
|
|
||||||
assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
|
if ((error = git_status_list_new(&status, repo, opts)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
|
git_vector_foreach(&status->paired, i, status_entry) {
|
||||||
|
const char *path = status_entry->head_to_index ?
|
||||||
|
status_entry->head_to_index->old_file.path :
|
||||||
|
status_entry->index_to_workdir->old_file.path;
|
||||||
|
|
||||||
if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
|
if (cb(path, status_entry->status, payload) != 0) {
|
||||||
(err = git_repository__ensure_not_bare(repo, "status")) < 0)
|
error = GIT_EUSER;
|
||||||
return err;
|
|
||||||
|
|
||||||
/* if there is no HEAD, that's okay - we'll make an empty iterator */
|
|
||||||
if (((err = git_repository_head_tree(&head, repo)) < 0) &&
|
|
||||||
!(err == GIT_ENOTFOUND || err == GIT_EORPHANEDHEAD))
|
|
||||||
return err;
|
|
||||||
|
|
||||||
memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
|
|
||||||
|
|
||||||
diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
|
|
||||||
|
|
||||||
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
|
|
||||||
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
|
|
||||||
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
|
|
||||||
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
|
|
||||||
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
|
|
||||||
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
|
|
||||||
if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
|
|
||||||
diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
|
|
||||||
if ((opts->flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0)
|
|
||||||
diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
|
|
||||||
if ((opts->flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0)
|
|
||||||
diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS;
|
|
||||||
if ((opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
|
|
||||||
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
|
|
||||||
|
|
||||||
if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
|
|
||||||
err = git_diff_tree_to_index(&head2idx, repo, head, NULL, &diffopt);
|
|
||||||
if (err < 0)
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
|
|
||||||
err = git_diff_index_to_workdir(&idx2wd, repo, NULL, &diffopt);
|
|
||||||
if (err < 0)
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
usercb.cb = cb;
|
|
||||||
usercb.payload = payload;
|
|
||||||
usercb.opts = opts;
|
|
||||||
|
|
||||||
if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
|
|
||||||
if ((err = git_diff__paired_foreach(
|
|
||||||
head2idx, NULL, status_invoke_cb, &usercb)) < 0)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
git_diff_list_free(head2idx);
|
|
||||||
head2idx = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = git_diff__paired_foreach(head2idx, idx2wd, status_invoke_cb, &usercb);
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
git_tree_free(head);
|
|
||||||
git_diff_list_free(head2idx);
|
|
||||||
git_diff_list_free(idx2wd);
|
|
||||||
|
|
||||||
if (err == GIT_EUSER)
|
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
git_status_list_free(status);
|
||||||
|
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_status_foreach(
|
int git_status_foreach(git_repository *repo, git_status_cb cb, void *payload)
|
||||||
git_repository *repo,
|
|
||||||
git_status_cb callback,
|
|
||||||
void *payload)
|
|
||||||
{
|
{
|
||||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
return git_status_foreach_ext(repo, NULL, cb, payload);
|
||||||
|
|
||||||
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
|
||||||
opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
|
|
||||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
|
||||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
|
||||||
|
|
||||||
return git_status_foreach_ext(repo, &opts, callback, payload);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct status_file_info {
|
struct status_file_info {
|
||||||
|
23
src/status.h
Normal file
23
src/status.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||||
|
* a Linking Exception. For full terms see the included COPYING file.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDE_status_h__
|
||||||
|
#define INCLUDE_status_h__
|
||||||
|
|
||||||
|
#include "diff.h"
|
||||||
|
#include "git2/status.h"
|
||||||
|
#include "git2/diff.h"
|
||||||
|
|
||||||
|
struct git_status_list {
|
||||||
|
git_status_options opts;
|
||||||
|
|
||||||
|
git_diff_list *head2idx;
|
||||||
|
git_diff_list *idx2wd;
|
||||||
|
|
||||||
|
git_vector paired;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
22
src/util.c
22
src/util.c
@ -279,6 +279,28 @@ int git__strcasecmp(const char *a, const char *b)
|
|||||||
return (tolower(*a) - tolower(*b));
|
return (tolower(*a) - tolower(*b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git__strcasesort_cmp(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
int cmp = 0;
|
||||||
|
|
||||||
|
while (*a && *b) {
|
||||||
|
if (*a != *b) {
|
||||||
|
if (tolower(*a) != tolower(*b))
|
||||||
|
break;
|
||||||
|
/* use case in sort order even if not in equivalence */
|
||||||
|
if (!cmp)
|
||||||
|
cmp = (int)(*(const uint8_t *)a) - (int)(*(const uint8_t *)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
++a, ++b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*a || *b)
|
||||||
|
return tolower(*a) - tolower(*b);
|
||||||
|
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
|
||||||
int git__strncmp(const char *a, const char *b, size_t sz)
|
int git__strncmp(const char *a, const char *b, size_t sz)
|
||||||
{
|
{
|
||||||
while (sz && *a && *b && *a == *b)
|
while (sz && *a && *b && *a == *b)
|
||||||
|
@ -194,6 +194,8 @@ extern int git__strcasecmp(const char *a, const char *b);
|
|||||||
extern int git__strncmp(const char *a, const char *b, size_t sz);
|
extern int git__strncmp(const char *a, const char *b, size_t sz);
|
||||||
extern int git__strncasecmp(const char *a, const char *b, size_t sz);
|
extern int git__strncasecmp(const char *a, const char *b, size_t sz);
|
||||||
|
|
||||||
|
extern int git__strcasesort_cmp(const char *a, const char *b);
|
||||||
|
|
||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -183,10 +183,10 @@ clar_run_test(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
clar_run_suite(const struct clar_suite *suite, const char *name)
|
clar_run_suite(const struct clar_suite *suite, const char *filter)
|
||||||
{
|
{
|
||||||
const struct clar_func *test = suite->tests;
|
const struct clar_func *test = suite->tests;
|
||||||
size_t i, namelen;
|
size_t i, matchlen;
|
||||||
|
|
||||||
if (!suite->enabled)
|
if (!suite->enabled)
|
||||||
return;
|
return;
|
||||||
@ -200,21 +200,21 @@ clar_run_suite(const struct clar_suite *suite, const char *name)
|
|||||||
_clar.active_suite = suite->name;
|
_clar.active_suite = suite->name;
|
||||||
_clar.suite_errors = 0;
|
_clar.suite_errors = 0;
|
||||||
|
|
||||||
if (name) {
|
if (filter) {
|
||||||
size_t suitelen = strlen(suite->name);
|
size_t suitelen = strlen(suite->name);
|
||||||
namelen = strlen(name);
|
matchlen = strlen(filter);
|
||||||
if (namelen <= suitelen) {
|
if (matchlen <= suitelen) {
|
||||||
name = NULL;
|
filter = NULL;
|
||||||
} else {
|
} else {
|
||||||
name += suitelen;
|
filter += suitelen;
|
||||||
while (*name == ':')
|
while (*filter == ':')
|
||||||
++name;
|
++filter;
|
||||||
namelen = strlen(name);
|
matchlen = strlen(filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < suite->test_count; ++i) {
|
for (i = 0; i < suite->test_count; ++i) {
|
||||||
if (name && strncmp(test[i].name, name, namelen))
|
if (filter && strncmp(test[i].name, filter, matchlen))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_clar.active_test = test[i].name;
|
_clar.active_test = test[i].name;
|
||||||
@ -230,7 +230,7 @@ clar_usage(const char *arg)
|
|||||||
{
|
{
|
||||||
printf("Usage: %s [options]\n\n", arg);
|
printf("Usage: %s [options]\n\n", arg);
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -sname\tRun only the suite with `name`\n");
|
printf(" -sname\tRun only the suite with `name` (can go to individual test name)\n");
|
||||||
printf(" -iname\tInclude the suite with `name`\n");
|
printf(" -iname\tInclude the suite with `name`\n");
|
||||||
printf(" -xname\tExclude the suite with `name`\n");
|
printf(" -xname\tExclude the suite with `name`\n");
|
||||||
printf(" -q \tOnly report tests that had an error\n");
|
printf(" -q \tOnly report tests that had an error\n");
|
||||||
@ -256,21 +256,20 @@ clar_parse_args(int argc, char **argv)
|
|||||||
case 'x': { /* given suite name */
|
case 'x': { /* given suite name */
|
||||||
int offset = (argument[2] == '=') ? 3 : 2, found = 0;
|
int offset = (argument[2] == '=') ? 3 : 2, found = 0;
|
||||||
char action = argument[1];
|
char action = argument[1];
|
||||||
size_t j, len, cmplen;
|
size_t j, arglen, suitelen, cmplen;
|
||||||
|
|
||||||
argument += offset;
|
argument += offset;
|
||||||
len = strlen(argument);
|
arglen = strlen(argument);
|
||||||
|
|
||||||
if (len == 0)
|
if (arglen == 0)
|
||||||
clar_usage(argv[0]);
|
clar_usage(argv[0]);
|
||||||
|
|
||||||
for (j = 0; j < _clar_suite_count; ++j) {
|
for (j = 0; j < _clar_suite_count; ++j) {
|
||||||
cmplen = strlen(_clar_suites[j].name);
|
suitelen = strlen(_clar_suites[j].name);
|
||||||
if (cmplen > len)
|
cmplen = (arglen < suitelen) ? arglen : suitelen;
|
||||||
cmplen = len;
|
|
||||||
|
|
||||||
if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
|
if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
|
||||||
int exact = !strcmp(argument, _clar_suites[j].name);
|
int exact = (arglen >= suitelen);
|
||||||
|
|
||||||
++found;
|
++found;
|
||||||
|
|
||||||
@ -419,7 +418,16 @@ void clar__assert_equal_s(
|
|||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
|
||||||
|
if (s1 && s2) {
|
||||||
|
int pos;
|
||||||
|
for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
|
||||||
|
/* find differing byte offset */;
|
||||||
|
snprint_eq(buf, sizeof(buf), "'%s' != '%s' (at byte %d)", s1, s2, pos);
|
||||||
|
} else {
|
||||||
snprint_eq(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
|
snprint_eq(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
clar__fail(file, line, err, buf, should_abort);
|
clar__fail(file, line, err, buf, should_abort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,9 @@ static int
|
|||||||
find_tmp_path(char *buffer, size_t length)
|
find_tmp_path(char *buffer, size_t length)
|
||||||
{
|
{
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
static const size_t var_count = 4;
|
static const size_t var_count = 5;
|
||||||
static const char *env_vars[] = {
|
static const char *env_vars[] = {
|
||||||
"TMPDIR", "TMP", "TEMP", "USERPROFILE"
|
"CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
@ -43,6 +43,12 @@ find_tmp_path(char *buffer, size_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
DWORD env_len;
|
||||||
|
|
||||||
|
if ((env_len = GetEnvironmentVariable("CLAR_TMP", buffer, length)) > 0 &&
|
||||||
|
env_len < length)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (GetTempPath((DWORD)length, buffer))
|
if (GetTempPath((DWORD)length, buffer))
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
@ -61,9 +67,7 @@ static void clar_unsandbox(void)
|
|||||||
if (_clar_path[0] == '\0')
|
if (_clar_path[0] == '\0')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
chdir("..");
|
chdir("..");
|
||||||
#endif
|
|
||||||
|
|
||||||
fs_rm(_clar_path);
|
fs_rm(_clar_path);
|
||||||
}
|
}
|
||||||
|
@ -26,3 +26,16 @@ void test_core_string__1(void)
|
|||||||
cl_assert(git__suffixcmp("zaz", "ac") > 0);
|
cl_assert(git__suffixcmp("zaz", "ac") > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* compare icase sorting with case equality */
|
||||||
|
void test_core_string__2(void)
|
||||||
|
{
|
||||||
|
cl_assert(git__strcasesort_cmp("", "") == 0);
|
||||||
|
cl_assert(git__strcasesort_cmp("foo", "foo") == 0);
|
||||||
|
cl_assert(git__strcasesort_cmp("foo", "bar") > 0);
|
||||||
|
cl_assert(git__strcasesort_cmp("bar", "foo") < 0);
|
||||||
|
cl_assert(git__strcasesort_cmp("foo", "FOO") > 0);
|
||||||
|
cl_assert(git__strcasesort_cmp("FOO", "foo") < 0);
|
||||||
|
cl_assert(git__strcasesort_cmp("foo", "BAR") > 0);
|
||||||
|
cl_assert(git__strcasesort_cmp("BAR", "foo") < 0);
|
||||||
|
cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0);
|
||||||
|
}
|
||||||
|
@ -899,6 +899,7 @@ void test_diff_rename__rejected_match_can_match_others(void)
|
|||||||
cl_git_pass(
|
cl_git_pass(
|
||||||
git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
|
git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
|
||||||
|
|
||||||
|
git_diff_list_free(diff);
|
||||||
git_tree_free(tree);
|
git_tree_free(tree);
|
||||||
git_index_free(index);
|
git_index_free(index);
|
||||||
git_reference_free(head);
|
git_reference_free(head);
|
||||||
@ -906,3 +907,93 @@ void test_diff_rename__rejected_match_can_match_others(void)
|
|||||||
git_buf_free(&one);
|
git_buf_free(&one);
|
||||||
git_buf_free(&two);
|
git_buf_free(&two);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_diff_rename__case_changes_are_split(void)
|
||||||
|
{
|
||||||
|
git_index *index;
|
||||||
|
git_tree *tree;
|
||||||
|
git_diff_list *diff = NULL;
|
||||||
|
diff_expects exp;
|
||||||
|
git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&index, g_repo));
|
||||||
|
|
||||||
|
cl_git_pass(
|
||||||
|
git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
|
||||||
|
|
||||||
|
cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/IKEEPSIX.txt"));
|
||||||
|
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "IKEEPSIX.txt"));
|
||||||
|
cl_git_pass(git_index_write(index));
|
||||||
|
|
||||||
|
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, NULL));
|
||||||
|
|
||||||
|
memset(&exp, 0, sizeof(exp));
|
||||||
|
cl_git_pass(git_diff_foreach(
|
||||||
|
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
|
||||||
|
cl_assert_equal_i(2, exp.files);
|
||||||
|
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||||
|
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
|
||||||
|
|
||||||
|
opts.flags = GIT_DIFF_FIND_ALL;
|
||||||
|
cl_git_pass(git_diff_find_similar(diff, &opts));
|
||||||
|
|
||||||
|
memset(&exp, 0, sizeof(exp));
|
||||||
|
cl_git_pass(git_diff_foreach(
|
||||||
|
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
|
||||||
|
cl_assert_equal_i(1, exp.files);
|
||||||
|
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
|
||||||
|
|
||||||
|
git_diff_list_free(diff);
|
||||||
|
git_index_free(index);
|
||||||
|
git_tree_free(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_diff_rename__unmodified_can_be_renamed(void)
|
||||||
|
{
|
||||||
|
git_index *index;
|
||||||
|
git_tree *tree;
|
||||||
|
git_diff_list *diff = NULL;
|
||||||
|
diff_expects exp;
|
||||||
|
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
|
||||||
|
git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&index, g_repo));
|
||||||
|
cl_git_pass(
|
||||||
|
git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
|
||||||
|
|
||||||
|
cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt"));
|
||||||
|
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt"));
|
||||||
|
cl_git_pass(git_index_write(index));
|
||||||
|
|
||||||
|
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
|
||||||
|
|
||||||
|
memset(&exp, 0, sizeof(exp));
|
||||||
|
cl_git_pass(git_diff_foreach(
|
||||||
|
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
|
||||||
|
cl_assert_equal_i(2, exp.files);
|
||||||
|
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||||
|
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
|
||||||
|
|
||||||
|
opts.flags = GIT_DIFF_FIND_ALL;
|
||||||
|
cl_git_pass(git_diff_find_similar(diff, &opts));
|
||||||
|
|
||||||
|
memset(&exp, 0, sizeof(exp));
|
||||||
|
cl_git_pass(git_diff_foreach(
|
||||||
|
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
|
||||||
|
cl_assert_equal_i(1, exp.files);
|
||||||
|
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
|
||||||
|
|
||||||
|
memset(&exp, 0, sizeof(exp));
|
||||||
|
cl_git_pass(git_diff_foreach(
|
||||||
|
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
|
||||||
|
cl_assert_equal_i(1, exp.files);
|
||||||
|
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
|
||||||
|
|
||||||
|
git_diff_list_free(diff);
|
||||||
|
git_index_free(index);
|
||||||
|
git_tree_free(tree);
|
||||||
|
}
|
||||||
|
@ -416,3 +416,46 @@ void test_index_tests__remove_directory(void)
|
|||||||
git_repository_free(repo);
|
git_repository_free(repo);
|
||||||
cl_fixture_cleanup("index_test");
|
cl_fixture_cleanup("index_test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_index_tests__preserves_case(void)
|
||||||
|
{
|
||||||
|
git_repository *repo;
|
||||||
|
git_index *index;
|
||||||
|
const git_index_entry *entry;
|
||||||
|
int index_caps;
|
||||||
|
|
||||||
|
cl_set_cleanup(&cleanup_myrepo, NULL);
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
|
||||||
|
cl_git_pass(git_repository_index(&index, repo));
|
||||||
|
|
||||||
|
index_caps = git_index_caps(index);
|
||||||
|
|
||||||
|
cl_git_rewritefile("myrepo/test.txt", "hey there\n");
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "test.txt"));
|
||||||
|
|
||||||
|
cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
|
||||||
|
cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
|
||||||
|
|
||||||
|
if (index_caps & GIT_INDEXCAP_IGNORE_CASE)
|
||||||
|
cl_assert_equal_i(1, (int)git_index_entrycount(index));
|
||||||
|
else
|
||||||
|
cl_assert_equal_i(2, (int)git_index_entrycount(index));
|
||||||
|
|
||||||
|
/* Test access by path instead of index */
|
||||||
|
cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
|
||||||
|
/* The path should *not* have changed without an explicit remove */
|
||||||
|
cl_assert(git__strcmp(entry->path, "test.txt") == 0);
|
||||||
|
|
||||||
|
cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
|
||||||
|
if (index_caps & GIT_INDEXCAP_IGNORE_CASE)
|
||||||
|
/* The path should *not* have changed without an explicit remove */
|
||||||
|
cl_assert(git__strcmp(entry->path, "test.txt") == 0);
|
||||||
|
else
|
||||||
|
cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
|
||||||
|
|
||||||
|
git_index_free(index);
|
||||||
|
git_repository_free(repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
385
tests-clar/status/renames.c
Normal file
385
tests-clar/status/renames.c
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
#include "clar_libgit2.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "path.h"
|
||||||
|
#include "posix.h"
|
||||||
|
#include "status_helpers.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
|
static git_repository *g_repo = NULL;
|
||||||
|
|
||||||
|
void test_status_renames__initialize(void)
|
||||||
|
{
|
||||||
|
g_repo = cl_git_sandbox_init("renames");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__cleanup(void)
|
||||||
|
{
|
||||||
|
cl_git_sandbox_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rename_file(git_repository *repo, const char *oldname, const char *newname)
|
||||||
|
{
|
||||||
|
git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname);
|
||||||
|
git_buf_joinpath(&newpath, git_repository_workdir(repo), newname);
|
||||||
|
|
||||||
|
cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
|
||||||
|
|
||||||
|
git_buf_free(&oldpath);
|
||||||
|
git_buf_free(&newpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rename_and_edit_file(git_repository *repo, const char *oldname, const char *newname)
|
||||||
|
{
|
||||||
|
git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname);
|
||||||
|
git_buf_joinpath(&newpath, git_repository_workdir(repo), newname);
|
||||||
|
|
||||||
|
cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
|
||||||
|
cl_git_append2file(newpath.ptr, "Added at the end to keep similarity!");
|
||||||
|
|
||||||
|
git_buf_free(&oldpath);
|
||||||
|
git_buf_free(&newpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct status_entry {
|
||||||
|
git_status_t status;
|
||||||
|
const char *oldname;
|
||||||
|
const char *newname;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_status(
|
||||||
|
git_status_list *status_list,
|
||||||
|
struct status_entry *expected_list,
|
||||||
|
size_t expected_len)
|
||||||
|
{
|
||||||
|
const git_status_entry *actual;
|
||||||
|
const struct status_entry *expected;
|
||||||
|
const char *oldname, *newname;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
cl_assert_equal_sz(expected_len, git_status_list_entrycount(status_list));
|
||||||
|
|
||||||
|
for (i = 0; i < expected_len; i++) {
|
||||||
|
actual = git_status_byindex(status_list, i);
|
||||||
|
expected = &expected_list[i];
|
||||||
|
|
||||||
|
cl_assert_equal_i((int)expected->status, (int)actual->status);
|
||||||
|
|
||||||
|
oldname = actual->head_to_index ? actual->head_to_index->old_file.path :
|
||||||
|
actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
|
||||||
|
|
||||||
|
newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
|
||||||
|
actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
|
||||||
|
|
||||||
|
if (oldname)
|
||||||
|
cl_assert(git__strcmp(oldname, expected->oldname) == 0);
|
||||||
|
else
|
||||||
|
cl_assert(expected->oldname == NULL);
|
||||||
|
|
||||||
|
if (newname)
|
||||||
|
cl_assert(git__strcmp(newname, expected->newname) == 0);
|
||||||
|
else
|
||||||
|
cl_assert(expected->newname == NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__head2index_one(void)
|
||||||
|
{
|
||||||
|
git_index *index;
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
struct status_entry expected[] = {
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "newname.txt" },
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&index, g_repo));
|
||||||
|
|
||||||
|
rename_file(g_repo, "ikeepsix.txt", "newname.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "newname.txt"));
|
||||||
|
cl_git_pass(git_index_write(index));
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||||
|
test_status(statuslist, expected, 1);
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
|
||||||
|
git_index_free(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__head2index_two(void)
|
||||||
|
{
|
||||||
|
git_index *index;
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
struct status_entry expected[] = {
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
|
||||||
|
"sixserving.txt", "aaa.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
|
||||||
|
"untimely.txt", "bbb.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" },
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&index, g_repo));
|
||||||
|
|
||||||
|
rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
|
||||||
|
rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
|
||||||
|
rename_file(g_repo, "songof7cities.txt", "ccc.txt");
|
||||||
|
rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "ddd.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "aaa.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "ccc.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "bbb.txt"));
|
||||||
|
cl_git_pass(git_index_write(index));
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||||
|
test_status(statuslist, expected, 4);
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
|
||||||
|
git_index_free(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__index2workdir_one(void)
|
||||||
|
{
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
struct status_entry expected[] = {
|
||||||
|
{ GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname.txt" },
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
|
||||||
|
|
||||||
|
rename_file(g_repo, "ikeepsix.txt", "newname.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||||
|
test_status(statuslist, expected, 1);
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__index2workdir_two(void)
|
||||||
|
{
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
struct status_entry expected[] = {
|
||||||
|
{ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
|
||||||
|
"sixserving.txt", "aaa.txt" },
|
||||||
|
{ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
|
||||||
|
"untimely.txt", "bbb.txt" },
|
||||||
|
{ GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" },
|
||||||
|
{ GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" },
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
|
||||||
|
|
||||||
|
rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
|
||||||
|
rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
|
||||||
|
rename_file(g_repo, "songof7cities.txt", "ccc.txt");
|
||||||
|
rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||||
|
test_status(statuslist, expected, 4);
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__both_one(void)
|
||||||
|
{
|
||||||
|
git_index *index;
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
struct status_entry expected[] = {
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||||
|
"ikeepsix.txt", "newname-workdir.txt" },
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&index, g_repo));
|
||||||
|
|
||||||
|
rename_file(g_repo, "ikeepsix.txt", "newname-index.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "newname-index.txt"));
|
||||||
|
cl_git_pass(git_index_write(index));
|
||||||
|
|
||||||
|
rename_file(g_repo, "newname-index.txt", "newname-workdir.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||||
|
test_status(statuslist, expected, 1);
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
|
||||||
|
git_index_free(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__both_two(void)
|
||||||
|
{
|
||||||
|
git_index *index;
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
struct status_entry expected[] = {
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
|
||||||
|
GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
|
||||||
|
"ikeepsix.txt", "ikeepsix-both.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
|
||||||
|
"sixserving.txt", "sixserving-index.txt" },
|
||||||
|
{ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
|
||||||
|
"songof7cities.txt", "songof7cities-workdir.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||||
|
"untimely.txt", "untimely-both.txt" },
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&index, g_repo));
|
||||||
|
|
||||||
|
rename_and_edit_file(g_repo, "ikeepsix.txt", "ikeepsix-index.txt");
|
||||||
|
rename_and_edit_file(g_repo, "sixserving.txt", "sixserving-index.txt");
|
||||||
|
rename_file(g_repo, "untimely.txt", "untimely-index.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "ikeepsix-index.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "sixserving-index.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "untimely-index.txt"));
|
||||||
|
cl_git_pass(git_index_write(index));
|
||||||
|
|
||||||
|
rename_and_edit_file(g_repo, "ikeepsix-index.txt", "ikeepsix-both.txt");
|
||||||
|
rename_and_edit_file(g_repo, "songof7cities.txt", "songof7cities-workdir.txt");
|
||||||
|
rename_file(g_repo, "untimely-index.txt", "untimely-both.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||||
|
test_status(statuslist, expected, 4);
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
|
||||||
|
git_index_free(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__both_casechange_one(void)
|
||||||
|
{
|
||||||
|
git_index *index;
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
int index_caps;
|
||||||
|
struct status_entry expected_icase[] = {
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED,
|
||||||
|
"ikeepsix.txt", "IKeepSix.txt" },
|
||||||
|
};
|
||||||
|
struct status_entry expected_case[] = {
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||||
|
"ikeepsix.txt", "IKEEPSIX.txt" },
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&index, g_repo));
|
||||||
|
index_caps = git_index_caps(index);
|
||||||
|
|
||||||
|
rename_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
|
||||||
|
cl_git_pass(git_index_write(index));
|
||||||
|
|
||||||
|
/* on a case-insensitive file system, this change won't matter.
|
||||||
|
* on a case-sensitive one, it will.
|
||||||
|
*/
|
||||||
|
rename_file(g_repo, "IKeepSix.txt", "IKEEPSIX.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||||
|
|
||||||
|
test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ?
|
||||||
|
expected_icase : expected_case, 1);
|
||||||
|
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
|
||||||
|
git_index_free(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_renames__both_casechange_two(void)
|
||||||
|
{
|
||||||
|
git_index *index;
|
||||||
|
git_status_list *statuslist;
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
int index_caps;
|
||||||
|
struct status_entry expected_icase[] = {
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
|
||||||
|
GIT_STATUS_WT_MODIFIED,
|
||||||
|
"ikeepsix.txt", "IKeepSix.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_MODIFIED,
|
||||||
|
"sixserving.txt", "sixserving.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED,
|
||||||
|
"songof7cities.txt", "songof7.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||||
|
"untimely.txt", "untimeliest.txt" }
|
||||||
|
};
|
||||||
|
struct status_entry expected_case[] = {
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
|
||||||
|
GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
|
||||||
|
"ikeepsix.txt", "ikeepsix.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
|
||||||
|
"sixserving.txt", "SixServing.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED |
|
||||||
|
GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
|
||||||
|
"songof7cities.txt", "SONGOF7.txt" },
|
||||||
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||||
|
"untimely.txt", "untimeliest.txt" }
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||||
|
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&index, g_repo));
|
||||||
|
index_caps = git_index_caps(index);
|
||||||
|
|
||||||
|
rename_and_edit_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
|
||||||
|
rename_and_edit_file(g_repo, "sixserving.txt", "sixserving.txt");
|
||||||
|
rename_file(g_repo, "songof7cities.txt", "songof7.txt");
|
||||||
|
rename_file(g_repo, "untimely.txt", "untimelier.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
|
||||||
|
cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "songof7.txt"));
|
||||||
|
cl_git_pass(git_index_add_bypath(index, "untimelier.txt"));
|
||||||
|
cl_git_pass(git_index_write(index));
|
||||||
|
|
||||||
|
rename_and_edit_file(g_repo, "IKeepSix.txt", "ikeepsix.txt");
|
||||||
|
rename_file(g_repo, "sixserving.txt", "SixServing.txt");
|
||||||
|
rename_and_edit_file(g_repo, "songof7.txt", "SONGOF7.txt");
|
||||||
|
rename_file(g_repo, "untimelier.txt", "untimeliest.txt");
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
|
||||||
|
|
||||||
|
test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ?
|
||||||
|
expected_icase : expected_case, 4);
|
||||||
|
|
||||||
|
git_status_list_free(statuslist);
|
||||||
|
|
||||||
|
git_index_free(index);
|
||||||
|
}
|
@ -695,3 +695,51 @@ void test_status_worktree__file_status_honors_case_ignorecase_regarding_untracke
|
|||||||
/* Actually returns GIT_STATUS_IGNORED on Windows */
|
/* Actually returns GIT_STATUS_IGNORED on Windows */
|
||||||
cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND);
|
cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_status_worktree__simple_delete(void)
|
||||||
|
{
|
||||||
|
git_repository *repo = cl_git_sandbox_init("renames");
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||||
|
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH |
|
||||||
|
GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
|
||||||
|
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
cl_git_pass(
|
||||||
|
git_status_foreach_ext(repo, &opts, cb_status__count, &count) );
|
||||||
|
cl_assert_equal_i(0, count);
|
||||||
|
|
||||||
|
cl_must_pass(p_unlink("renames/untimely.txt"));
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
cl_git_pass(
|
||||||
|
git_status_foreach_ext(repo, &opts, cb_status__count, &count) );
|
||||||
|
cl_assert_equal_i(1, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_status_worktree__simple_delete_indexed(void)
|
||||||
|
{
|
||||||
|
git_repository *repo = cl_git_sandbox_init("renames");
|
||||||
|
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
git_status_list *status;
|
||||||
|
|
||||||
|
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||||
|
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH |
|
||||||
|
GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
|
||||||
|
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&status, repo, &opts));
|
||||||
|
cl_assert_equal_sz(0, git_status_list_entrycount(status));
|
||||||
|
git_status_list_free(status);
|
||||||
|
|
||||||
|
cl_must_pass(p_unlink("renames/untimely.txt"));
|
||||||
|
|
||||||
|
cl_git_pass(git_status_list_new(&status, repo, &opts));
|
||||||
|
cl_assert_equal_sz(1, git_status_list_entrycount(status));
|
||||||
|
cl_assert_equal_i(
|
||||||
|
GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status);
|
||||||
|
git_status_list_free(status);
|
||||||
|
}
|
||||||
|
@ -376,9 +376,12 @@ void test_submodule_status__iterator(void)
|
|||||||
|
|
||||||
git_iterator_free(iter);
|
git_iterator_free(iter);
|
||||||
|
|
||||||
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||||
|
GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
|
||||||
|
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
||||||
|
|
||||||
cl_git_pass(git_status_foreach_ext(g_repo, &opts, confirm_submodule_status, &exp));
|
cl_git_pass(git_status_foreach_ext(
|
||||||
|
g_repo, &opts, confirm_submodule_status, &exp));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_submodule_status__untracked_dirs_containing_ignored_files(void)
|
void test_submodule_status__untracked_dirs_containing_ignored_files(void)
|
||||||
|
@ -113,24 +113,18 @@
|
|||||||
{
|
{
|
||||||
mac-ssl-leak-1
|
mac-ssl-leak-1
|
||||||
Memcheck:Leak
|
Memcheck:Leak
|
||||||
fun:malloc
|
|
||||||
fun:CRYPTO_malloc
|
|
||||||
...
|
...
|
||||||
fun:ERR_load_strings
|
fun:ERR_load_strings
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
mac-ssl-leak-2
|
mac-ssl-leak-2
|
||||||
Memcheck:Leak
|
Memcheck:Leak
|
||||||
fun:malloc
|
|
||||||
fun:CRYPTO_malloc
|
|
||||||
...
|
...
|
||||||
fun:SSL_library_init
|
fun:SSL_library_init
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
mac-ssl-leak-3
|
mac-ssl-leak-3
|
||||||
Memcheck:Leak
|
Memcheck:Leak
|
||||||
fun:malloc
|
|
||||||
fun:strdup
|
|
||||||
...
|
...
|
||||||
fun:si_module_with_name
|
fun:si_module_with_name
|
||||||
fun:getaddrinfo
|
fun:getaddrinfo
|
||||||
@ -143,6 +137,14 @@
|
|||||||
...
|
...
|
||||||
fun:ssl3_get_server_certificate
|
fun:ssl3_get_server_certificate
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
mac-ssl-leak-5
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:malloc
|
||||||
|
fun:CRYPTO_malloc
|
||||||
|
...
|
||||||
|
fun:ERR_put_error
|
||||||
|
}
|
||||||
{
|
{
|
||||||
clar-printf-buf
|
clar-printf-buf
|
||||||
Memcheck:Leak
|
Memcheck:Leak
|
||||||
|
Loading…
Reference in New Issue
Block a user