mirror of
https://git.proxmox.com/git/libgit2
synced 2025-06-23 23:01:20 +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_DELETED = (1u << 9),
|
||||
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
|
||||
GIT_STATUS_WT_RENAMED = (1u << 11),
|
||||
|
||||
GIT_STATUS_IGNORED = (1u << 14),
|
||||
} git_status_t;
|
||||
@ -58,44 +59,20 @@ typedef enum {
|
||||
typedef int (*git_status_cb)(
|
||||
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.
|
||||
*
|
||||
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the
|
||||
* rough equivalent of `git status --porcelain` where each file
|
||||
* will receive a callback indicating its status in the index and
|
||||
* in the workdir.
|
||||
* - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index
|
||||
* side of status. The status of the index contents relative to
|
||||
* the HEAD will be given.
|
||||
* - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the
|
||||
* workdir side of status, reporting the status of workdir content
|
||||
* relative to the index.
|
||||
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only
|
||||
* 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`.
|
||||
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly
|
||||
* matches `git status --porcelain` where each file gets a callback
|
||||
* indicating its status in the index and in the working directory.
|
||||
* - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
|
||||
* comparison, not looking at working directory changes.
|
||||
* - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
|
||||
* working directory comparison, not comparing the index to the HEAD.
|
||||
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR runs index-only then workdir-only,
|
||||
* issuing (up to) two callbacks per file (first index, then workdir).
|
||||
* This is slightly more efficient than separate calls and can make it
|
||||
* easier to emulate plain `git status` text output.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
|
||||
@ -110,26 +87,30 @@ typedef enum {
|
||||
* - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
|
||||
* on untracked files. These will only be made if the workdir files are
|
||||
* included in the status "show" option.
|
||||
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should get
|
||||
* callbacks. Again, these callbacks will only be made if the workdir
|
||||
* files are included in the status "show" option. Right now, there is
|
||||
* no option to include all files in directories that are ignored
|
||||
* completely.
|
||||
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks.
|
||||
* Again, these callbacks will only be made if the workdir files are
|
||||
* included in the status "show" option.
|
||||
* - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
|
||||
* made even on unmodified files.
|
||||
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories which
|
||||
* appear to be submodules should just be skipped over.
|
||||
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the contents of
|
||||
* untracked directories should be included in the status. Normally if
|
||||
* an entire directory is new, then just the top-level directory will be
|
||||
* included (with a trailing slash on the entry name). Given this flag,
|
||||
* the directory itself will not be included, but all the files in it
|
||||
* will.
|
||||
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be
|
||||
* skipped. This only applies if there are no pending typechanges to
|
||||
* the submodule (either from or to another type).
|
||||
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in
|
||||
* untracked directories should be included. Normally if an entire
|
||||
* directory is new, then just the top-level directory is included (with
|
||||
* a trailing slash on the entry name). This flag says to include all
|
||||
* of the individual files in the directory instead.
|
||||
* - 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
|
||||
* ignored directories should be included in the status. This is like
|
||||
* 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
|
||||
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
|
||||
@ -137,13 +118,15 @@ typedef enum {
|
||||
* together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
|
||||
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
|
||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
|
||||
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
||||
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
||||
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
|
||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
|
||||
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
|
||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
|
||||
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
||||
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
||||
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;
|
||||
|
||||
#define GIT_STATUS_OPT_DEFAULTS \
|
||||
@ -177,6 +160,47 @@ typedef struct {
|
||||
#define GIT_STATUS_OPTIONS_VERSION 1
|
||||
#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.
|
||||
*
|
||||
@ -215,6 +239,49 @@ GIT_EXTERN(int) git_status_file(
|
||||
git_repository *repo,
|
||||
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.
|
||||
*
|
||||
|
@ -174,6 +174,9 @@ typedef struct git_reference_iterator git_reference_iterator;
|
||||
/** Merge heads, the input to merge */
|
||||
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. */
|
||||
typedef enum {
|
||||
|
106
src/diff.c
106
src/diff.c
@ -134,6 +134,7 @@ static int diff_delta__from_two(
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
int notify_res;
|
||||
const char *canonical_path = old_entry->path;
|
||||
|
||||
if (status == GIT_DELTA_UNMODIFIED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
|
||||
@ -153,7 +154,7 @@ static int diff_delta__from_two(
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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(
|
||||
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->pfxcomp = git__prefixcmp_icase;
|
||||
diff->entrycomp = git_index_entry__cmp_icase;
|
||||
|
||||
diff->deltas._cmp = git_diff_delta__casecmp;
|
||||
}
|
||||
|
||||
return diff;
|
||||
@ -1119,17 +1129,40 @@ int git_diff_tree_to_index(
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
int error = 0;
|
||||
bool reset_index_ignore_case = false;
|
||||
|
||||
assert(diff && repo);
|
||||
|
||||
if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
|
||||
return error;
|
||||
|
||||
if (index->ignore_case) {
|
||||
git_index__set_ignore_case(index, false);
|
||||
reset_index_ignore_case = true;
|
||||
}
|
||||
|
||||
DIFF_FROM_ITERATORS(
|
||||
git_iterator_for_tree(&a, old_tree, 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;
|
||||
}
|
||||
|
||||
@ -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(
|
||||
git_diff_list *idx2head,
|
||||
git_diff_list *wd2idx,
|
||||
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
||||
git_diff_list *head2idx,
|
||||
git_diff_list *idx2wd,
|
||||
int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
|
||||
void *payload)
|
||||
{
|
||||
int cmp;
|
||||
git_diff_delta *i2h, *w2i;
|
||||
git_diff_delta *h2i, *i2w;
|
||||
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;
|
||||
j_max = wd2idx ? wd2idx->deltas.length : 0;
|
||||
i_max = head2idx ? head2idx->deltas.length : 0;
|
||||
j_max = idx2wd ? idx2wd->deltas.length : 0;
|
||||
|
||||
/* Get appropriate strcmp function */
|
||||
strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL;
|
||||
/* At some point, tree-to-index diffs will probably never ignore case,
|
||||
* 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
|
||||
* still be using the canonical case-preserving name.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
icase_mismatch =
|
||||
(head2idx != NULL && idx2wd != NULL &&
|
||||
((head2idx->opts.flags ^ idx2wd->opts.flags) & GIT_DIFF_DELTAS_ARE_ICASE));
|
||||
|
||||
/* Assert both iterators use matching ignore-case. If this function ever
|
||||
* 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
|
||||
*/
|
||||
if (idx2head && wd2idx) {
|
||||
assert(idx2head->strcomp == wd2idx->strcomp);
|
||||
/* 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; ) {
|
||||
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
|
||||
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
|
||||
h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
|
||||
i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
|
||||
|
||||
cmp = !w2i ? -1 : !i2h ? 1 :
|
||||
strcomp(i2h->old_file.path, w2i->old_file.path);
|
||||
cmp = !i2w ? -1 : !h2i ? 1 :
|
||||
strcomp(h2i->new_file.path, i2w->old_file.path);
|
||||
|
||||
if (cmp < 0) {
|
||||
if (cb(i2h, NULL, payload))
|
||||
if (cb(h2i, NULL, payload))
|
||||
return GIT_EUSER;
|
||||
i++;
|
||||
} else if (cmp > 0) {
|
||||
if (cb(NULL, w2i, payload))
|
||||
if (cb(NULL, i2w, payload))
|
||||
return GIT_EUSER;
|
||||
j++;
|
||||
} else {
|
||||
if (cb(i2h, w2i, payload))
|
||||
if (cb(h2i, i2w, payload))
|
||||
return GIT_EUSER;
|
||||
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;
|
||||
}
|
||||
|
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 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(
|
||||
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),
|
||||
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);
|
||||
|
||||
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 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);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -175,10 +175,11 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
|
||||
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 &&
|
||||
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;
|
||||
|
||||
cleanup:
|
||||
@ -284,6 +285,7 @@ int git_diff_foreach(
|
||||
git_xdiff_init(&xo, &diff->opts);
|
||||
|
||||
git_vector_foreach(&diff->deltas, idx, patch.delta) {
|
||||
|
||||
/* check flags against patch status */
|
||||
if (git_diff_delta__should_skip(&diff->opts, patch.delta))
|
||||
continue;
|
||||
|
109
src/diff_print.c
109
src/diff_print.c
@ -41,7 +41,7 @@ static int diff_print_info_init(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char pick_suffix(int mode)
|
||||
static char diff_pick_suffix(int mode)
|
||||
{
|
||||
if (S_ISDIR(mode))
|
||||
return '/';
|
||||
@ -76,10 +76,11 @@ static int callback_error(void)
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
||||
static int print_compact(
|
||||
static int diff_print_one_compact(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
git_buf *out = pi->buf;
|
||||
char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
@ -87,34 +88,35 @@ static int print_compact(
|
||||
if (code == ' ')
|
||||
return 0;
|
||||
|
||||
old_suffix = pick_suffix(delta->old_file.mode);
|
||||
new_suffix = pick_suffix(delta->new_file.mode);
|
||||
old_suffix = diff_pick_suffix(delta->old_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 &&
|
||||
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);
|
||||
else if (delta->old_file.mode != delta->new_file.mode &&
|
||||
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);
|
||||
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
|
||||
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;
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
/* print a git_diff_list to a print callback in compact format */
|
||||
int git_diff_print_compact(
|
||||
git_diff_list *diff,
|
||||
git_diff_data_cb print_cb,
|
||||
@ -125,17 +127,18 @@ int git_diff_print_compact(
|
||||
diff_print_info pi;
|
||||
|
||||
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);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int print_raw(
|
||||
static int diff_print_one_raw(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
git_buf *out = pi->buf;
|
||||
char code = git_diff_status_char(delta->status);
|
||||
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||
|
||||
@ -144,36 +147,37 @@ static int print_raw(
|
||||
if (code == ' ')
|
||||
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(end_oid, pi->oid_strlen, &delta->new_file.oid);
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
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
|
||||
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);
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
if (git_buf_oom(out))
|
||||
return -1;
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
/* print a git_diff_list to a print callback in raw output format */
|
||||
int git_diff_print_raw(
|
||||
git_diff_list *diff,
|
||||
git_diff_data_cb print_cb,
|
||||
@ -184,15 +188,16 @@ int git_diff_print_raw(
|
||||
diff_print_info pi;
|
||||
|
||||
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);
|
||||
|
||||
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];
|
||||
|
||||
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 */
|
||||
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);
|
||||
} else {
|
||||
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) {
|
||||
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 {
|
||||
git_buf_printf(pi->buf, "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, "old mode %o\n", delta->old_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 0;
|
||||
}
|
||||
|
||||
static int print_patch_file(
|
||||
static int diff_print_patch_file(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
@ -247,7 +252,7 @@ static int print_patch_file(
|
||||
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);
|
||||
|
||||
if (print_oid_range(pi, delta) < 0)
|
||||
if (diff_print_oid_range(pi, delta) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_oid_iszero(&delta->old_file.oid)) {
|
||||
@ -288,7 +293,7 @@ static int print_patch_file(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_patch_hunk(
|
||||
static int diff_print_patch_hunk(
|
||||
const git_diff_delta *d,
|
||||
const git_diff_range *r,
|
||||
const char *header,
|
||||
@ -311,7 +316,7 @@ static int print_patch_hunk(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_patch_line(
|
||||
static int diff_print_patch_line(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char line_origin, /* GIT_DIFF_LINE value from above */
|
||||
@ -343,6 +348,7 @@ static int print_patch_line(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* print a git_diff_list to an output callback in patch format */
|
||||
int git_diff_print_patch(
|
||||
git_diff_list *diff,
|
||||
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)))
|
||||
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);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* print a git_diff_patch to an output callback */
|
||||
int git_diff_patch_print(
|
||||
git_diff_patch *patch,
|
||||
git_diff_data_cb print_cb,
|
||||
@ -389,13 +383,28 @@ int git_diff_patch_print(
|
||||
if (!(error = diff_print_info_init(
|
||||
&pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
|
||||
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);
|
||||
|
||||
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(
|
||||
char **string,
|
||||
git_diff_patch *patch)
|
||||
@ -403,7 +412,7 @@ int git_diff_patch_to_str(
|
||||
int error;
|
||||
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,
|
||||
* 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))
|
||||
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 (git_oid_iszero(&a_file->oid) &&
|
||||
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)
|
||||
return git_vector_insert(&index->entries, entry);
|
||||
|
||||
/* exists, replace it */
|
||||
git__free((*existing)->path);
|
||||
/* exists, replace it (preserving name from existing entry) */
|
||||
git__free(entry->path);
|
||||
entry->path = (*existing)->path;
|
||||
git__free(*existing);
|
||||
*existing = entry;
|
||||
|
||||
|
452
src/status.c
452
src/status.c
@ -11,6 +11,7 @@
|
||||
#include "hash.h"
|
||||
#include "vector.h"
|
||||
#include "tree.h"
|
||||
#include "status.h"
|
||||
#include "git2/status.h"
|
||||
#include "repository.h"
|
||||
#include "ignore.h"
|
||||
@ -19,11 +20,11 @@
|
||||
#include "git2/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_COPIED:
|
||||
st = GIT_STATUS_INDEX_NEW;
|
||||
@ -36,6 +37,9 @@ static unsigned int index_delta2status(git_delta_t index_status)
|
||||
break;
|
||||
case GIT_DELTA_RENAMED:
|
||||
st = GIT_STATUS_INDEX_RENAMED;
|
||||
|
||||
if (!git_oid_equal(&head2idx->old_file.oid, &head2idx->new_file.oid))
|
||||
st |= GIT_STATUS_INDEX_MODIFIED;
|
||||
break;
|
||||
case GIT_DELTA_TYPECHANGE:
|
||||
st = GIT_STATUS_INDEX_TYPECHANGE;
|
||||
@ -47,13 +51,13 @@ static unsigned int index_delta2status(git_delta_t index_status)
|
||||
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_RENAMED:
|
||||
case GIT_DELTA_COPIED:
|
||||
case GIT_DELTA_UNTRACKED:
|
||||
st = GIT_STATUS_WT_NEW;
|
||||
@ -67,6 +71,31 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
|
||||
case GIT_DELTA_IGNORED:
|
||||
st = GIT_STATUS_IGNORED;
|
||||
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:
|
||||
st = GIT_STATUS_WT_TYPECHANGE;
|
||||
break;
|
||||
@ -77,43 +106,285 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
|
||||
return st;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_status_cb cb;
|
||||
void *payload;
|
||||
const git_status_options *opts;
|
||||
} status_user_callback;
|
||||
|
||||
static int status_invoke_cb(
|
||||
git_diff_delta *h2i, git_diff_delta *i2w, void *payload)
|
||||
static bool status_is_included(
|
||||
git_status_list *status,
|
||||
git_diff_delta *head2idx,
|
||||
git_diff_delta *idx2wd)
|
||||
{
|
||||
status_user_callback *usercb = payload;
|
||||
const char *path = NULL;
|
||||
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 (!(status->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES))
|
||||
return 1;
|
||||
|
||||
/* if excluding submodules and this is a submodule everywhere */
|
||||
if (usercb->opts &&
|
||||
(usercb->opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
|
||||
{
|
||||
bool in_tree = (h2i && h2i->status != GIT_DELTA_ADDED);
|
||||
bool in_index = (h2i && h2i->status != GIT_DELTA_DELETED);
|
||||
bool in_wd = (i2w && i2w->status != GIT_DELTA_DELETED);
|
||||
|
||||
if ((!in_tree || h2i->old_file.mode == GIT_FILEMODE_COMMIT) &&
|
||||
(!in_index || h2i->new_file.mode == GIT_FILEMODE_COMMIT) &&
|
||||
(!in_wd || i2w->new_file.mode == GIT_FILEMODE_COMMIT))
|
||||
return 0;
|
||||
if (head2idx) {
|
||||
if (head2idx->status != GIT_DELTA_ADDED &&
|
||||
head2idx->old_file.mode != GIT_FILEMODE_COMMIT)
|
||||
return 1;
|
||||
if (head2idx->status != GIT_DELTA_DELETED &&
|
||||
head2idx->new_file.mode != GIT_FILEMODE_COMMIT)
|
||||
return 1;
|
||||
}
|
||||
if (idx2wd) {
|
||||
if (idx2wd->status != GIT_DELTA_ADDED &&
|
||||
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(
|
||||
@ -122,97 +393,34 @@ int git_status_foreach_ext(
|
||||
git_status_cb cb,
|
||||
void *payload)
|
||||
{
|
||||
int err = 0;
|
||||
git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_list *head2idx = NULL, *idx2wd = NULL;
|
||||
git_tree *head = NULL;
|
||||
git_status_show_t show =
|
||||
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
status_user_callback usercb;
|
||||
git_status_list *status;
|
||||
const git_status_entry *status_entry;
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
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 &&
|
||||
(err = git_repository__ensure_not_bare(repo, "status")) < 0)
|
||||
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 (cb(path, status_entry->status, payload) != 0) {
|
||||
error = GIT_EUSER;
|
||||
giterr_clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
|
||||
err = git_diff_index_to_workdir(&idx2wd, repo, NULL, &diffopt);
|
||||
if (err < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
git_status_list_free(status);
|
||||
|
||||
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();
|
||||
|
||||
return err;
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_status_foreach(
|
||||
git_repository *repo,
|
||||
git_status_cb callback,
|
||||
void *payload)
|
||||
int git_status_foreach(git_repository *repo, git_status_cb cb, void *payload)
|
||||
{
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
|
||||
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);
|
||||
return git_status_foreach_ext(repo, NULL, cb, payload);
|
||||
}
|
||||
|
||||
struct status_file_info {
|
||||
@ -264,7 +472,7 @@ int git_status_file(
|
||||
if (index->ignore_case)
|
||||
sfi.fnm_flags = FNM_CASEFOLD;
|
||||
|
||||
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
|
||||
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS |
|
||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||
|
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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__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"
|
||||
|
||||
typedef struct {
|
||||
|
@ -183,10 +183,10 @@ clar_run_test(
|
||||
}
|
||||
|
||||
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;
|
||||
size_t i, namelen;
|
||||
size_t i, matchlen;
|
||||
|
||||
if (!suite->enabled)
|
||||
return;
|
||||
@ -200,21 +200,21 @@ clar_run_suite(const struct clar_suite *suite, const char *name)
|
||||
_clar.active_suite = suite->name;
|
||||
_clar.suite_errors = 0;
|
||||
|
||||
if (name) {
|
||||
if (filter) {
|
||||
size_t suitelen = strlen(suite->name);
|
||||
namelen = strlen(name);
|
||||
if (namelen <= suitelen) {
|
||||
name = NULL;
|
||||
matchlen = strlen(filter);
|
||||
if (matchlen <= suitelen) {
|
||||
filter = NULL;
|
||||
} else {
|
||||
name += suitelen;
|
||||
while (*name == ':')
|
||||
++name;
|
||||
namelen = strlen(name);
|
||||
filter += suitelen;
|
||||
while (*filter == ':')
|
||||
++filter;
|
||||
matchlen = strlen(filter);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
_clar.active_test = test[i].name;
|
||||
@ -230,7 +230,7 @@ clar_usage(const char *arg)
|
||||
{
|
||||
printf("Usage: %s [options]\n\n", arg);
|
||||
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(" -xname\tExclude the suite with `name`\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 */
|
||||
int offset = (argument[2] == '=') ? 3 : 2, found = 0;
|
||||
char action = argument[1];
|
||||
size_t j, len, cmplen;
|
||||
size_t j, arglen, suitelen, cmplen;
|
||||
|
||||
argument += offset;
|
||||
len = strlen(argument);
|
||||
arglen = strlen(argument);
|
||||
|
||||
if (len == 0)
|
||||
if (arglen == 0)
|
||||
clar_usage(argv[0]);
|
||||
|
||||
for (j = 0; j < _clar_suite_count; ++j) {
|
||||
cmplen = strlen(_clar_suites[j].name);
|
||||
if (cmplen > len)
|
||||
cmplen = len;
|
||||
suitelen = strlen(_clar_suites[j].name);
|
||||
cmplen = (arglen < suitelen) ? arglen : suitelen;
|
||||
|
||||
if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
|
||||
int exact = !strcmp(argument, _clar_suites[j].name);
|
||||
int exact = (arglen >= suitelen);
|
||||
|
||||
++found;
|
||||
|
||||
@ -419,7 +418,16 @@ void clar__assert_equal_s(
|
||||
|
||||
if (!match) {
|
||||
char buf[4096];
|
||||
snprint_eq(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
clar__fail(file, line, err, buf, should_abort);
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,9 @@ static int
|
||||
find_tmp_path(char *buffer, size_t length)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
static const size_t var_count = 4;
|
||||
static const size_t var_count = 5;
|
||||
static const char *env_vars[] = {
|
||||
"TMPDIR", "TMP", "TEMP", "USERPROFILE"
|
||||
"CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
|
||||
};
|
||||
|
||||
size_t i;
|
||||
@ -43,6 +43,12 @@ find_tmp_path(char *buffer, size_t length)
|
||||
}
|
||||
|
||||
#else
|
||||
DWORD env_len;
|
||||
|
||||
if ((env_len = GetEnvironmentVariable("CLAR_TMP", buffer, length)) > 0 &&
|
||||
env_len < length)
|
||||
return 0;
|
||||
|
||||
if (GetTempPath((DWORD)length, buffer))
|
||||
return 0;
|
||||
#endif
|
||||
@ -61,9 +67,7 @@ static void clar_unsandbox(void)
|
||||
if (_clar_path[0] == '\0')
|
||||
return;
|
||||
|
||||
#ifdef _WIN32
|
||||
chdir("..");
|
||||
#endif
|
||||
|
||||
fs_rm(_clar_path);
|
||||
}
|
||||
|
@ -26,3 +26,16 @@ void test_core_string__1(void)
|
||||
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(
|
||||
git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
|
||||
|
||||
git_diff_list_free(diff);
|
||||
git_tree_free(tree);
|
||||
git_index_free(index);
|
||||
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(&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);
|
||||
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 */
|
||||
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);
|
||||
|
||||
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)
|
||||
|
@ -113,24 +113,18 @@
|
||||
{
|
||||
mac-ssl-leak-1
|
||||
Memcheck:Leak
|
||||
fun:malloc
|
||||
fun:CRYPTO_malloc
|
||||
...
|
||||
fun:ERR_load_strings
|
||||
}
|
||||
{
|
||||
mac-ssl-leak-2
|
||||
Memcheck:Leak
|
||||
fun:malloc
|
||||
fun:CRYPTO_malloc
|
||||
...
|
||||
fun:SSL_library_init
|
||||
}
|
||||
{
|
||||
mac-ssl-leak-3
|
||||
Memcheck:Leak
|
||||
fun:malloc
|
||||
fun:strdup
|
||||
...
|
||||
fun:si_module_with_name
|
||||
fun:getaddrinfo
|
||||
@ -143,6 +137,14 @@
|
||||
...
|
||||
fun:ssl3_get_server_certificate
|
||||
}
|
||||
{
|
||||
mac-ssl-leak-5
|
||||
Memcheck:Leak
|
||||
fun:malloc
|
||||
fun:CRYPTO_malloc
|
||||
...
|
||||
fun:ERR_put_error
|
||||
}
|
||||
{
|
||||
clar-printf-buf
|
||||
Memcheck:Leak
|
||||
|
Loading…
Reference in New Issue
Block a user