mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-03 04:34:15 +00:00
Merge pull request #1507 from arrbee/fix-look-inside-untracked-directory
Update diff handling of "untracked" directories
This commit is contained in:
commit
5e2261aca8
@ -88,42 +88,61 @@ typedef enum {
|
||||
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
|
||||
/** Include unmodified files in the diff list */
|
||||
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked directory
|
||||
* will be marked with only a single entry in the diff list; this flag
|
||||
* adds all files under the directory as UNTRACKED entries, too.
|
||||
*/
|
||||
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
|
||||
|
||||
/** If the pathspec is set in the diff options, this flags means to
|
||||
* apply it as an exact match instead of as an fnmatch pattern.
|
||||
*/
|
||||
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
|
||||
|
||||
/** Use case insensitive filename comparisons */
|
||||
GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
|
||||
|
||||
/** When generating patch text, include the content of untracked files */
|
||||
GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
|
||||
|
||||
/** Disable updating of the `binary` flag in delta records. This is
|
||||
* useful when iterating over a diff if you don't need hunk and data
|
||||
* callbacks and want to avoid having to load file completely.
|
||||
*/
|
||||
GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
|
||||
|
||||
/** Normally, a type change between files will be converted into a
|
||||
* DELETED record for the old and an ADDED record for the new; this
|
||||
* options enabled the generation of TYPECHANGE delta records.
|
||||
*/
|
||||
GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
|
||||
* generally show as a DELETED blob. This flag tries to correctly
|
||||
* label blob->tree transitions as TYPECHANGE records with new_file's
|
||||
* mode set to tree. Note: the tree SHA will not be available.
|
||||
*/
|
||||
GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16),
|
||||
|
||||
/** Ignore file mode changes */
|
||||
GIT_DIFF_IGNORE_FILEMODE = (1 << 17),
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
|
||||
* will be marked with only a single entry in the diff list; this flag
|
||||
* adds all files under the directory as IGNORED entries, too.
|
||||
*/
|
||||
GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18),
|
||||
|
||||
/** Core Git scans inside untracked directories, labeling them IGNORED
|
||||
* if they are empty or only contain ignored files; a directory is
|
||||
* consider UNTRACKED only if it has an actual untracked file in it.
|
||||
* This scan is extra work for a case you often don't care about. This
|
||||
* flag makes libgit2 immediately label an untracked directory as
|
||||
* UNTRACKED without looking insde it (which differs from core Git).
|
||||
* Of course, ignore rules are still checked for the directory itself.
|
||||
*/
|
||||
GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19),
|
||||
} git_diff_option_t;
|
||||
|
||||
/**
|
||||
|
@ -103,20 +103,20 @@ typedef enum {
|
||||
* * WD_UNTRACKED - wd contains untracked files
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
||||
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
||||
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
||||
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
||||
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
||||
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
||||
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
||||
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
||||
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
||||
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
||||
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
||||
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
||||
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
||||
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
||||
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
||||
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
||||
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
||||
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
||||
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
||||
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
||||
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
||||
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
||||
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
||||
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
||||
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
||||
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
||||
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||
} git_submodule_status_t;
|
||||
|
||||
#define GIT_SUBMODULE_STATUS__IN_FLAGS \
|
||||
|
462
src/diff.c
462
src/diff.c
@ -327,8 +327,7 @@ static git_diff_list *diff_list_alloc(
|
||||
/* Use case-insensitive compare if either iterator has
|
||||
* the ignore_case bit set */
|
||||
if (!git_iterator_ignore_case(old_iter) &&
|
||||
!git_iterator_ignore_case(new_iter))
|
||||
{
|
||||
!git_iterator_ignore_case(new_iter)) {
|
||||
diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
|
||||
diff->strcomp = git__strcmp;
|
||||
@ -530,24 +529,30 @@ cleanup:
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
git_iterator *old_iter;
|
||||
git_iterator *new_iter;
|
||||
const git_index_entry *oitem;
|
||||
const git_index_entry *nitem;
|
||||
git_buf ignore_prefix;
|
||||
} diff_in_progress;
|
||||
|
||||
#define MODE_BITS_MASK 0000777
|
||||
|
||||
static int maybe_modified(
|
||||
git_iterator *old_iter,
|
||||
const git_index_entry *oitem,
|
||||
git_iterator *new_iter,
|
||||
const git_index_entry *nitem,
|
||||
git_diff_list *diff)
|
||||
git_diff_list *diff,
|
||||
diff_in_progress *info)
|
||||
{
|
||||
git_oid noid, *use_noid = NULL;
|
||||
git_delta_t status = GIT_DELTA_MODIFIED;
|
||||
const git_index_entry *oitem = info->oitem;
|
||||
const git_index_entry *nitem = info->nitem;
|
||||
unsigned int omode = oitem->mode;
|
||||
unsigned int nmode = nitem->mode;
|
||||
bool new_is_workdir = (new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
|
||||
bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
|
||||
const char *matched_pathspec;
|
||||
|
||||
GIT_UNUSED(old_iter);
|
||||
|
||||
if (!git_pathspec_match_path(
|
||||
&diff->pathspec, oitem->path,
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
|
||||
@ -692,6 +697,250 @@ static bool entry_is_prefixed(
|
||||
item->path[pathlen] == '/');
|
||||
}
|
||||
|
||||
static int diff_scan_inside_untracked_dir(
|
||||
git_diff_list *diff, diff_in_progress *info, git_delta_t *delta_type)
|
||||
{
|
||||
int error = 0;
|
||||
git_buf base = GIT_BUF_INIT;
|
||||
bool is_ignored;
|
||||
|
||||
*delta_type = GIT_DELTA_IGNORED;
|
||||
git_buf_sets(&base, info->nitem->path);
|
||||
|
||||
/* advance into untracked directory */
|
||||
if ((error = git_iterator_advance_into(&info->nitem, info->new_iter)) < 0) {
|
||||
|
||||
/* skip ahead if empty */
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
error = git_iterator_advance(&info->nitem, info->new_iter);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* look for actual untracked file */
|
||||
while (!diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) {
|
||||
is_ignored = git_iterator_current_is_ignored(info->new_iter);
|
||||
|
||||
/* need to recurse into non-ignored directories */
|
||||
if (!is_ignored && S_ISDIR(info->nitem->mode)) {
|
||||
if ((error = git_iterator_advance_into(
|
||||
&info->nitem, info->new_iter)) < 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* found a non-ignored item - treat parent dir as untracked */
|
||||
if (!is_ignored) {
|
||||
*delta_type = GIT_DELTA_UNTRACKED;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* finish off scan */
|
||||
while (!diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) {
|
||||
if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
git_buf_free(&base);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int handle_unmatched_new_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = 0;
|
||||
const git_index_entry *nitem = info->nitem;
|
||||
git_delta_t delta_type = GIT_DELTA_UNTRACKED;
|
||||
bool contains_oitem;
|
||||
|
||||
/* check if this is a prefix of the other side */
|
||||
contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
|
||||
|
||||
/* check if this is contained in an ignored parent directory */
|
||||
if (git_buf_len(&info->ignore_prefix)) {
|
||||
if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0)
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
else
|
||||
git_buf_clear(&info->ignore_prefix);
|
||||
}
|
||||
|
||||
if (S_ISDIR(nitem->mode)) {
|
||||
bool recurse_into_dir = contains_oitem;
|
||||
|
||||
/* if not already inside an ignored dir, check if this is ignored */
|
||||
if (delta_type != GIT_DELTA_IGNORED &&
|
||||
git_iterator_current_is_ignored(info->new_iter)) {
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
git_buf_sets(&info->ignore_prefix, nitem->path);
|
||||
}
|
||||
|
||||
/* check if user requests recursion into this type of dir */
|
||||
recurse_into_dir = contains_oitem ||
|
||||
(delta_type == GIT_DELTA_UNTRACKED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
|
||||
(delta_type == GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
|
||||
|
||||
/* do not advance into directories that contain a .git file */
|
||||
if (recurse_into_dir) {
|
||||
git_buf *full = NULL;
|
||||
if (git_iterator_current_workdir_path(&full, info->new_iter) < 0)
|
||||
return -1;
|
||||
if (full && git_path_contains_dir(full, DOT_GIT))
|
||||
recurse_into_dir = false;
|
||||
}
|
||||
|
||||
/* still have to look into untracked directories to match core git -
|
||||
* with no untracked files, directory is treated as ignored
|
||||
*/
|
||||
if (!recurse_into_dir &&
|
||||
delta_type == GIT_DELTA_UNTRACKED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_FAST_UNTRACKED_DIRS))
|
||||
{
|
||||
git_diff_delta *last;
|
||||
|
||||
/* attempt to insert record for this directory */
|
||||
if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
|
||||
return error;
|
||||
|
||||
/* if delta wasn't created (because of rules), just skip ahead */
|
||||
last = diff_delta__last_for_item(diff, nitem);
|
||||
if (!last)
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
|
||||
/* iterate into dir looking for an actual untracked file */
|
||||
if (diff_scan_inside_untracked_dir(diff, info, &delta_type) < 0)
|
||||
return -1;
|
||||
|
||||
/* it iteration changed delta type, the update the record */
|
||||
if (delta_type == GIT_DELTA_IGNORED) {
|
||||
last->status = GIT_DELTA_IGNORED;
|
||||
|
||||
/* remove the record if we don't want ignored records */
|
||||
if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED)) {
|
||||
git_vector_pop(&diff->deltas);
|
||||
git__free(last);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try to advance into directory if necessary */
|
||||
if (recurse_into_dir) {
|
||||
error = git_iterator_advance_into(&info->nitem, info->new_iter);
|
||||
|
||||
/* if real error or no error, proceed with iteration */
|
||||
if (error != GIT_ENOTFOUND)
|
||||
return error;
|
||||
giterr_clear();
|
||||
|
||||
/* if directory is empty, can't advance into it, so either skip
|
||||
* it or ignore it
|
||||
*/
|
||||
if (contains_oitem)
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
}
|
||||
}
|
||||
|
||||
/* In core git, the next two checks are effectively reversed --
|
||||
* i.e. when an file contained in an ignored directory is explicitly
|
||||
* ignored, it shows up as an ignored file in the diff list, even though
|
||||
* other untracked files in the same directory are skipped completely.
|
||||
*
|
||||
* To me, this seems odd. If the directory is ignored and the file is
|
||||
* untracked, we should skip it consistently, regardless of whether it
|
||||
* happens to match a pattern in the ignore file.
|
||||
*
|
||||
* To match the core git behavior, reverse the following two if checks
|
||||
* so that individual file ignores are checked before container
|
||||
* directory exclusions are used to skip the file.
|
||||
*/
|
||||
else if (delta_type == GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
|
||||
/* item contained in ignored directory, so skip over it */
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
|
||||
else if (git_iterator_current_is_ignored(info->new_iter))
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
|
||||
delta_type = GIT_DELTA_ADDED;
|
||||
|
||||
/* Actually create the record for this item if necessary */
|
||||
if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
|
||||
return error;
|
||||
|
||||
/* If user requested TYPECHANGE records, then check for that instead of
|
||||
* just generating an ADDED/UNTRACKED record
|
||||
*/
|
||||
if (delta_type != GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
|
||||
contains_oitem)
|
||||
{
|
||||
/* this entry was prefixed with a tree - make TYPECHANGE */
|
||||
git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
|
||||
if (last) {
|
||||
last->status = GIT_DELTA_TYPECHANGE;
|
||||
last->old_file.mode = GIT_FILEMODE_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
}
|
||||
|
||||
static int handle_unmatched_old_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/* if we are generating TYPECHANGE records then check for that
|
||||
* instead of just generating a DELETE record
|
||||
*/
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
|
||||
entry_is_prefixed(diff, info->nitem, info->oitem))
|
||||
{
|
||||
/* this entry has become a tree! convert to TYPECHANGE */
|
||||
git_diff_delta *last = diff_delta__last_for_item(diff, info->oitem);
|
||||
if (last) {
|
||||
last->status = GIT_DELTA_TYPECHANGE;
|
||||
last->new_file.mode = GIT_FILEMODE_TREE;
|
||||
}
|
||||
|
||||
/* If new_iter is a workdir iterator, then this situation
|
||||
* will certainly be followed by a series of untracked items.
|
||||
* Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
|
||||
*/
|
||||
if (S_ISDIR(info->nitem->mode) &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
}
|
||||
|
||||
return git_iterator_advance(&info->oitem, info->old_iter);
|
||||
}
|
||||
|
||||
static int handle_matched_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (!(error = maybe_modified(diff, info)) &&
|
||||
!(error = git_iterator_advance(&info->oitem, info->old_iter)))
|
||||
error = git_iterator_advance(&info->nitem, info->new_iter);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_diff__from_iterators(
|
||||
git_diff_list **diff_ptr,
|
||||
git_repository *repo,
|
||||
@ -700,8 +949,7 @@ int git_diff__from_iterators(
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
int error = 0;
|
||||
const git_index_entry *oitem, *nitem;
|
||||
git_buf ignore_prefix = GIT_BUF_INIT;
|
||||
diff_in_progress info;
|
||||
git_diff_list *diff;
|
||||
|
||||
*diff_ptr = NULL;
|
||||
@ -709,191 +957,51 @@ int git_diff__from_iterators(
|
||||
diff = diff_list_alloc(repo, old_iter, new_iter);
|
||||
GITERR_CHECK_ALLOC(diff);
|
||||
|
||||
info.repo = repo;
|
||||
info.old_iter = old_iter;
|
||||
info.new_iter = new_iter;
|
||||
git_buf_init(&info.ignore_prefix, 0);
|
||||
|
||||
/* make iterators have matching icase behavior */
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
|
||||
if (git_iterator_set_ignore_case(old_iter, true) < 0 ||
|
||||
git_iterator_set_ignore_case(new_iter, true) < 0)
|
||||
goto fail;
|
||||
if (!(error = git_iterator_set_ignore_case(old_iter, true)))
|
||||
error = git_iterator_set_ignore_case(new_iter, true);
|
||||
}
|
||||
|
||||
if (diff_list_apply_options(diff, opts) < 0 ||
|
||||
git_iterator_current(&oitem, old_iter) < 0 ||
|
||||
git_iterator_current(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
/* finish initialization */
|
||||
if (!error &&
|
||||
!(error = diff_list_apply_options(diff, opts)) &&
|
||||
!(error = git_iterator_current(&info.oitem, old_iter)))
|
||||
error = git_iterator_current(&info.nitem, new_iter);
|
||||
|
||||
/* run iterators building diffs */
|
||||
while (oitem || nitem) {
|
||||
int cmp = oitem ? (nitem ? diff->entrycomp(oitem, nitem) : -1) : 1;
|
||||
while (!error && (info.oitem || info.nitem)) {
|
||||
int cmp = info.oitem ?
|
||||
(info.nitem ? diff->entrycomp(info.oitem, info.nitem) : -1) : 1;
|
||||
|
||||
/* create DELETED records for old items not matched in new */
|
||||
if (cmp < 0) {
|
||||
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0)
|
||||
goto fail;
|
||||
|
||||
/* if we are generating TYPECHANGE records then check for that
|
||||
* instead of just generating a DELETE record
|
||||
*/
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
|
||||
entry_is_prefixed(diff, nitem, oitem))
|
||||
{
|
||||
/* this entry has become a tree! convert to TYPECHANGE */
|
||||
git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
|
||||
if (last) {
|
||||
last->status = GIT_DELTA_TYPECHANGE;
|
||||
last->new_file.mode = GIT_FILEMODE_TREE;
|
||||
}
|
||||
|
||||
/* If new_iter is a workdir iterator, then this situation
|
||||
* will certainly be followed by a series of untracked items.
|
||||
* Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
|
||||
*/
|
||||
if (S_ISDIR(nitem->mode) &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
|
||||
{
|
||||
if (git_iterator_advance(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (git_iterator_advance(&oitem, old_iter) < 0)
|
||||
goto fail;
|
||||
}
|
||||
if (cmp < 0)
|
||||
error = handle_unmatched_old_item(diff, &info);
|
||||
|
||||
/* create ADDED, TRACKED, or IGNORED records for new items not
|
||||
* matched in old (and/or descend into directories as needed)
|
||||
*/
|
||||
else if (cmp > 0) {
|
||||
git_delta_t delta_type = GIT_DELTA_UNTRACKED;
|
||||
bool contains_oitem = entry_is_prefixed(diff, oitem, nitem);
|
||||
|
||||
/* check if contained in ignored parent directory */
|
||||
if (git_buf_len(&ignore_prefix) &&
|
||||
diff->pfxcomp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
if (S_ISDIR(nitem->mode)) {
|
||||
/* recurse into directory only if there are tracked items in
|
||||
* it or if the user requested the contents of untracked
|
||||
* directories and it is not under an ignored directory.
|
||||
*/
|
||||
bool recurse_into_dir =
|
||||
(delta_type == GIT_DELTA_UNTRACKED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
|
||||
(delta_type == GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
|
||||
|
||||
/* do not advance into directories that contain a .git file */
|
||||
if (!contains_oitem && recurse_into_dir) {
|
||||
git_buf *full = NULL;
|
||||
if (git_iterator_current_workdir_path(&full, new_iter) < 0)
|
||||
goto fail;
|
||||
if (git_path_contains_dir(full, DOT_GIT))
|
||||
recurse_into_dir = false;
|
||||
}
|
||||
|
||||
/* if directory is ignored, remember ignore_prefix */
|
||||
if ((contains_oitem || recurse_into_dir) &&
|
||||
delta_type == GIT_DELTA_UNTRACKED &&
|
||||
git_iterator_current_is_ignored(new_iter))
|
||||
{
|
||||
git_buf_sets(&ignore_prefix, nitem->path);
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
/* skip recursion if we've just learned this is ignored */
|
||||
if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
|
||||
recurse_into_dir = false;
|
||||
}
|
||||
|
||||
if (contains_oitem || recurse_into_dir) {
|
||||
/* advance into directory */
|
||||
error = git_iterator_advance_into(&nitem, new_iter);
|
||||
|
||||
/* if directory is empty, can't advance into it, so skip */
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
error = git_iterator_advance(&nitem, new_iter);
|
||||
|
||||
git_buf_clear(&ignore_prefix);
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
goto fail;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* In core git, the next two "else if" clauses are effectively
|
||||
* reversed -- i.e. when an untracked file contained in an
|
||||
* ignored directory is individually ignored, it shows up as an
|
||||
* ignored file in the diff list, even though other untracked
|
||||
* files in the same directory are skipped completely.
|
||||
*
|
||||
* To me, this is odd. If the directory is ignored and the file
|
||||
* is untracked, we should skip it consistently, regardless of
|
||||
* whether it happens to match a pattern in the ignore file.
|
||||
*
|
||||
* To match the core git behavior, just reverse the following
|
||||
* two "else if" cases so that individual file ignores are
|
||||
* checked before container directory exclusions are used to
|
||||
* skip the file.
|
||||
*/
|
||||
else if (delta_type == GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) {
|
||||
if (git_iterator_advance(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
continue; /* ignored parent directory, so skip completely */
|
||||
}
|
||||
|
||||
else if (git_iterator_current_is_ignored(new_iter))
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
else if (new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
|
||||
delta_type = GIT_DELTA_ADDED;
|
||||
|
||||
if (diff_delta__from_one(diff, delta_type, nitem) < 0)
|
||||
goto fail;
|
||||
|
||||
/* if we are generating TYPECHANGE records then check for that
|
||||
* instead of just generating an ADDED/UNTRACKED record
|
||||
*/
|
||||
if (delta_type != GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
|
||||
contains_oitem)
|
||||
{
|
||||
/* this entry was prefixed with a tree - make TYPECHANGE */
|
||||
git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
|
||||
if (last) {
|
||||
last->status = GIT_DELTA_TYPECHANGE;
|
||||
last->old_file.mode = GIT_FILEMODE_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
if (git_iterator_advance(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
error = handle_unmatched_new_item(diff, &info);
|
||||
|
||||
/* otherwise item paths match, so create MODIFIED record
|
||||
* (or ADDED and DELETED pair if type changed)
|
||||
*/
|
||||
else {
|
||||
assert(oitem && nitem && cmp == 0);
|
||||
|
||||
if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
|
||||
git_iterator_advance(&oitem, old_iter) < 0 ||
|
||||
git_iterator_advance(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
error = handle_matched_item(diff, &info);
|
||||
}
|
||||
|
||||
*diff_ptr = diff;
|
||||
|
||||
fail:
|
||||
if (!*diff_ptr) {
|
||||
if (!error)
|
||||
*diff_ptr = diff;
|
||||
else
|
||||
git_diff_list_free(diff);
|
||||
error = -1;
|
||||
}
|
||||
|
||||
git_buf_free(&ignore_prefix);
|
||||
git_buf_free(&info.ignore_prefix);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -101,8 +101,8 @@ static bool diff_delta_is_binary_forced(
|
||||
|
||||
/* make sure files are conceivably mmap-able */
|
||||
if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size ||
|
||||
(git_off_t)((size_t)delta->new_file.size) != delta->new_file.size)
|
||||
{
|
||||
(git_off_t)((size_t)delta->new_file.size) != delta->new_file.size) {
|
||||
|
||||
delta->old_file.flags |= GIT_DIFF_FLAG_BINARY;
|
||||
delta->new_file.flags |= GIT_DIFF_FLAG_BINARY;
|
||||
delta->flags |= GIT_DIFF_FLAG_BINARY;
|
||||
@ -232,8 +232,7 @@ static int get_blob_content(
|
||||
if (git_oid_iszero(&file->oid))
|
||||
return 0;
|
||||
|
||||
if (file->mode == GIT_FILEMODE_COMMIT)
|
||||
{
|
||||
if (file->mode == GIT_FILEMODE_COMMIT) {
|
||||
char oidstr[GIT_OID_HEXSZ+1];
|
||||
git_buf content = GIT_BUF_INIT;
|
||||
|
||||
@ -299,8 +298,8 @@ static int get_workdir_sm_content(
|
||||
char oidstr[GIT_OID_HEXSZ+1];
|
||||
|
||||
if ((error = git_submodule_lookup(&sm, ctxt->repo, file->path)) < 0 ||
|
||||
(error = git_submodule_status(&sm_status, sm)) < 0)
|
||||
{
|
||||
(error = git_submodule_status(&sm_status, sm)) < 0) {
|
||||
|
||||
/* GIT_EEXISTS means a "submodule" that has not been git added */
|
||||
if (error == GIT_EEXISTS)
|
||||
error = 0;
|
||||
@ -312,8 +311,8 @@ static int get_workdir_sm_content(
|
||||
const git_oid* sm_head;
|
||||
|
||||
if ((sm_head = git_submodule_wd_id(sm)) != NULL ||
|
||||
(sm_head = git_submodule_head_id(sm)) != NULL)
|
||||
{
|
||||
(sm_head = git_submodule_head_id(sm)) != NULL) {
|
||||
|
||||
git_oid_cpy(&file->oid, sm_head);
|
||||
file->flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
}
|
||||
@ -660,8 +659,8 @@ static int diff_patch_load(
|
||||
*/
|
||||
if (check_if_unmodified &&
|
||||
delta->old_file.mode == delta->new_file.mode &&
|
||||
!git_oid__cmp(&delta->old_file.oid, &delta->new_file.oid))
|
||||
{
|
||||
!git_oid__cmp(&delta->old_file.oid, &delta->new_file.oid)) {
|
||||
|
||||
delta->status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
@ -1049,6 +1048,12 @@ char git_diff_status_char(git_delta_t status)
|
||||
return code;
|
||||
}
|
||||
|
||||
static int callback_error(void)
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
||||
static int print_compact(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
@ -1083,10 +1088,7 @@ static int print_compact(
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1200,10 +1202,7 @@ static int print_patch_file(
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
|
||||
return 0;
|
||||
@ -1217,10 +1216,7 @@ static int print_patch_file(
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1243,10 +1239,7 @@ static int print_patch_hunk(
|
||||
|
||||
if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1278,10 +1271,7 @@ static int print_patch_line(
|
||||
|
||||
if (pi->print_cb(delta, range, line_origin,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -277,15 +277,13 @@ void git_vector_swap(git_vector *a, git_vector *b)
|
||||
|
||||
int git_vector_resize_to(git_vector *v, size_t new_length)
|
||||
{
|
||||
if (new_length <= v->length)
|
||||
return 0;
|
||||
|
||||
if (new_length > v->_alloc_size &&
|
||||
resize_vector(v, new_length) < 0)
|
||||
return -1;
|
||||
|
||||
memset(&v->contents[v->length], 0,
|
||||
sizeof(void *) * (new_length - v->length));
|
||||
if (new_length > v->length)
|
||||
memset(&v->contents[v->length], 0,
|
||||
sizeof(void *) * (new_length - v->length));
|
||||
|
||||
v->length = new_length;
|
||||
|
||||
|
@ -28,7 +28,15 @@ int diff_file_cb(
|
||||
{
|
||||
diff_expects *e = payload;
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
if (e->debug)
|
||||
fprintf(stderr, "%c %s (%.3f)\n",
|
||||
git_diff_status_char(delta->status),
|
||||
delta->old_file.path, progress);
|
||||
|
||||
if (e->names)
|
||||
cl_assert_equal_s(e->names[e->files], delta->old_file.path);
|
||||
if (e->statuses)
|
||||
cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
|
||||
|
||||
e->files++;
|
||||
|
||||
|
@ -18,6 +18,13 @@ typedef struct {
|
||||
int line_ctxt;
|
||||
int line_adds;
|
||||
int line_dels;
|
||||
|
||||
/* optional arrays of expected specific values */
|
||||
const char **names;
|
||||
int *statuses;
|
||||
|
||||
int debug;
|
||||
|
||||
} diff_expects;
|
||||
|
||||
typedef struct {
|
||||
|
@ -1033,3 +1033,190 @@ void test_diff_workdir__to_tree_issue_1397(void)
|
||||
git_diff_list_free(diff);
|
||||
git_tree_free(a);
|
||||
}
|
||||
|
||||
void test_diff_workdir__untracked_directory_scenarios(void)
|
||||
{
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_list *diff = NULL;
|
||||
diff_expects exp;
|
||||
char *pathspec = NULL;
|
||||
static const char *files0[] = {
|
||||
"subdir/deleted_file",
|
||||
"subdir/modified_file",
|
||||
"subdir/new_file",
|
||||
NULL
|
||||
};
|
||||
static const char *files1[] = {
|
||||
"subdir/deleted_file",
|
||||
"subdir/directory/",
|
||||
"subdir/modified_file",
|
||||
"subdir/new_file",
|
||||
NULL
|
||||
};
|
||||
static const char *files2[] = {
|
||||
"subdir/deleted_file",
|
||||
"subdir/directory/more/notignored",
|
||||
"subdir/modified_file",
|
||||
"subdir/new_file",
|
||||
NULL
|
||||
};
|
||||
|
||||
g_repo = cl_git_sandbox_init("status");
|
||||
cl_git_mkfile("status/.gitignore", "ignored\n");
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
opts.pathspec.strings = &pathspec;
|
||||
opts.pathspec.count = 1;
|
||||
pathspec = "subdir";
|
||||
|
||||
/* baseline for "subdir" pathspec */
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
exp.names = files0;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
|
||||
|
||||
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
|
||||
|
||||
cl_assert_equal_i(3, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* empty directory */
|
||||
|
||||
cl_git_pass(p_mkdir("status/subdir/directory", 0777));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
exp.names = files1;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
|
||||
|
||||
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
|
||||
|
||||
cl_assert_equal_i(4, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* directory with only ignored files */
|
||||
|
||||
cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
|
||||
cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
|
||||
|
||||
cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
|
||||
cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
exp.names = files1;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
|
||||
|
||||
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
|
||||
|
||||
cl_assert_equal_i(4, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* directory with ignored directory (contents irrelevant) */
|
||||
|
||||
cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
|
||||
cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
|
||||
cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
|
||||
"inside ignored dir\n");
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
exp.names = files1;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
|
||||
|
||||
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
|
||||
|
||||
cl_assert_equal_i(4, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* quick version avoids directory scan */
|
||||
|
||||
opts.flags = opts.flags | GIT_DIFF_FAST_UNTRACKED_DIRS;
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
exp.names = files1;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
|
||||
|
||||
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
|
||||
|
||||
cl_assert_equal_i(4, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
|
||||
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* directory with nested non-ignored content */
|
||||
|
||||
opts.flags = opts.flags & ~GIT_DIFF_FAST_UNTRACKED_DIRS;
|
||||
|
||||
cl_git_mkfile("status/subdir/directory/more/notignored",
|
||||
"not ignored deep under untracked\n");
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
exp.names = files1;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
|
||||
|
||||
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
|
||||
|
||||
cl_assert_equal_i(4, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
|
||||
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
|
||||
|
||||
opts.flags = opts.flags & ~GIT_DIFF_INCLUDE_IGNORED;
|
||||
opts.flags = opts.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
exp.names = files2;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
|
||||
|
||||
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
|
||||
|
||||
cl_assert_equal_i(4, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
|
||||
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
@ -40,7 +40,8 @@ int cb_status__single(const char *p, unsigned int s, void *payload)
|
||||
{
|
||||
status_entry_single *data = (status_entry_single *)payload;
|
||||
|
||||
GIT_UNUSED(p);
|
||||
if (data->debug)
|
||||
fprintf(stderr, "%02d: %s (%04x)\n", data->count, p, s);
|
||||
|
||||
data->count++;
|
||||
data->status = s;
|
||||
|
@ -24,6 +24,7 @@ extern int cb_status__count(const char *p, unsigned int s, void *payload);
|
||||
typedef struct {
|
||||
int count;
|
||||
unsigned int status;
|
||||
bool debug;
|
||||
} status_entry_single;
|
||||
|
||||
/* cb_status__single takes payload of "status_entry_single *" */
|
||||
|
@ -258,9 +258,8 @@ void test_status_worktree__ignores(void)
|
||||
|
||||
static int cb_status__check_592(const char *p, unsigned int s, void *payload)
|
||||
{
|
||||
GIT_UNUSED(payload);
|
||||
|
||||
if (s != GIT_STATUS_WT_DELETED || (payload != NULL && strcmp(p, (const char *)payload) != 0))
|
||||
if (s != GIT_STATUS_WT_DELETED ||
|
||||
(payload != NULL && strcmp(p, (const char *)payload) != 0))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
@ -383,3 +383,30 @@ void test_submodule_status__iterator(void)
|
||||
|
||||
cl_git_pass(git_status_foreach_ext(g_repo, &opts, confirm_submodule_status, &exp));
|
||||
}
|
||||
|
||||
void test_submodule_status__untracked_dirs_containing_ignored_files(void)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
unsigned int status, expected;
|
||||
git_submodule *sm;
|
||||
|
||||
cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), "modules/sm_unchanged/info/exclude"));
|
||||
cl_git_append2file(git_buf_cstr(&path), "\n*.ignored\n");
|
||||
|
||||
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged/directory"));
|
||||
cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
|
||||
cl_git_pass(git_buf_joinpath(&path, git_buf_cstr(&path), "i_am.ignored"));
|
||||
cl_git_mkfile(git_buf_cstr(&path), "ignored this file, please\n");
|
||||
|
||||
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
|
||||
cl_git_pass(git_submodule_status(&status, sm));
|
||||
|
||||
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
|
||||
|
||||
expected = GIT_SUBMODULE_STATUS_IN_HEAD |
|
||||
GIT_SUBMODULE_STATUS_IN_INDEX |
|
||||
GIT_SUBMODULE_STATUS_IN_CONFIG |
|
||||
GIT_SUBMODULE_STATUS_IN_WD;
|
||||
|
||||
cl_assert(status == expected);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user