mirror of
https://git.proxmox.com/git/libgit2
synced 2026-01-05 21:13:49 +00:00
Merge pull request #744 from arrbee/fix-filemodes
Fix filemode comparison in diffs
This commit is contained in:
commit
e0b110edb1
@ -185,9 +185,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* open repo */
|
||||
|
||||
check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
|
||||
"Could not discover repository");
|
||||
check(git_repository_open(&repo, path),
|
||||
check(git_repository_open_ext(&repo, dir, 0, NULL),
|
||||
"Could not open repository");
|
||||
|
||||
if (treeish1)
|
||||
|
||||
@ -29,6 +29,10 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Flags for diff options. A combination of these flags can be passed
|
||||
* in via the `flags` value in the `git_diff_options`.
|
||||
*/
|
||||
enum {
|
||||
GIT_DIFF_NORMAL = 0,
|
||||
GIT_DIFF_REVERSE = (1 << 0),
|
||||
@ -160,15 +164,16 @@ typedef int (*git_diff_hunk_fn)(
|
||||
* the file or hunk headers.
|
||||
*/
|
||||
enum {
|
||||
/* these values will be sent to `git_diff_data_fn` along with the line */
|
||||
/* These values will be sent to `git_diff_data_fn` along with the line */
|
||||
GIT_DIFF_LINE_CONTEXT = ' ',
|
||||
GIT_DIFF_LINE_ADDITION = '+',
|
||||
GIT_DIFF_LINE_DELETION = '-',
|
||||
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< LF was added at end of file */
|
||||
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< DEPRECATED */
|
||||
GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
|
||||
/* these values will only be sent to a `git_diff_data_fn` when the content
|
||||
* of a diff is being formatted (eg. through git_diff_print_patch() or
|
||||
* git_diff_print_compact(), for instance).
|
||||
|
||||
/* The following values will only be sent to a `git_diff_data_fn` when
|
||||
* the content of a diff is being formatted (eg. through
|
||||
* git_diff_print_patch() or git_diff_print_compact(), for instance).
|
||||
*/
|
||||
GIT_DIFF_LINE_FILE_HDR = 'F',
|
||||
GIT_DIFF_LINE_HUNK_HDR = 'H',
|
||||
@ -206,6 +211,8 @@ GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
|
||||
/**
|
||||
* Compute a difference between two tree objects.
|
||||
*
|
||||
* This is equivalent to `git diff <treeish> <treeish>`
|
||||
*
|
||||
* @param repo The repository containing the trees.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
* @param old_tree A git_tree object to diff from.
|
||||
@ -222,6 +229,9 @@ GIT_EXTERN(int) git_diff_tree_to_tree(
|
||||
/**
|
||||
* Compute a difference between a tree and the index.
|
||||
*
|
||||
* This is equivalent to `git diff --cached <treeish>` or if you pass
|
||||
* the HEAD tree, then like `git diff --cached`.
|
||||
*
|
||||
* @param repo The repository containing the tree and index.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
* @param old_tree A git_tree object to diff from.
|
||||
@ -236,6 +246,11 @@ GIT_EXTERN(int) git_diff_index_to_tree(
|
||||
/**
|
||||
* Compute a difference between the working directory and the index.
|
||||
*
|
||||
* This matches the `git diff` command. See the note below on
|
||||
* `git_diff_workdir_to_tree` for a discussion of the difference between
|
||||
* `git diff` and `git diff HEAD` and how to emulate a `git diff <treeish>`
|
||||
* using libgit2.
|
||||
*
|
||||
* @param repo The repository.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
* @param diff A pointer to a git_diff_list pointer that will be allocated.
|
||||
@ -248,14 +263,24 @@ GIT_EXTERN(int) git_diff_workdir_to_index(
|
||||
/**
|
||||
* Compute a difference between the working directory and a tree.
|
||||
*
|
||||
* This returns strictly the differences between the tree and the
|
||||
* files contained in the working directory, regardless of the state
|
||||
* of files in the index. There is no direct equivalent in C git.
|
||||
* This is *NOT* the same as `git diff <treeish>`. Running `git diff HEAD`
|
||||
* or the like actually uses information from the index, along with the tree
|
||||
* and workdir dir info.
|
||||
*
|
||||
* This is *NOT* the same as 'git diff HEAD' or 'git diff <SHA>'. Those
|
||||
* commands diff the tree, the index, and the workdir. To emulate those
|
||||
* functions, call `git_diff_index_to_tree` and `git_diff_workdir_to_index`,
|
||||
* then call `git_diff_merge` on the results.
|
||||
* This function returns strictly the differences between the tree and the
|
||||
* files contained in the working directory, regardless of the state of
|
||||
* files in the index. It may come as a surprise, but there is no direct
|
||||
* equivalent in core git.
|
||||
*
|
||||
* To emulate `git diff <treeish>`, you should call both
|
||||
* `git_diff_index_to_tree` and `git_diff_workdir_to_index`, then call
|
||||
* `git_diff_merge` on the results. That will yield a `git_diff_list` that
|
||||
* matches the git output.
|
||||
*
|
||||
* If this seems confusing, take the case of a file with a staged deletion
|
||||
* where the file has then been put back into the working dir and modified.
|
||||
* The tree-to-workdir diff for that file is 'modified', but core git would
|
||||
* show status 'deleted' since there is a pending deletion in the index.
|
||||
*
|
||||
* @param repo The repository containing the tree.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
@ -298,10 +323,23 @@ GIT_EXTERN(int) git_diff_merge(
|
||||
/**
|
||||
* Iterate over a diff list issuing callbacks.
|
||||
*
|
||||
* If the hunk and/or line callbacks are not NULL, then this will calculate
|
||||
* text diffs for all files it thinks are not binary. If those are both
|
||||
* NULL, then this will not bother with the text diffs, so it can be
|
||||
* efficient.
|
||||
* This will iterate through all of the files described in a diff. You
|
||||
* should provide a file callback to learn about each file.
|
||||
*
|
||||
* The "hunk" and "line" callbacks are optional, and the text diff of the
|
||||
* files will only be calculated if they are not NULL. Of course, these
|
||||
* callbacks will not be invoked for binary files on the diff list or for
|
||||
* files whose only changed is a file mode change.
|
||||
*
|
||||
* @param diff A git_diff_list generated by one of the above functions.
|
||||
* @param cb_data Reference pointer that will be passed to your callbacks.
|
||||
* @param file_cb Callback function to make per file in the diff.
|
||||
* @param hunk_cb Optional callback to make per hunk of text diff. This
|
||||
* callback is called to describe a range of lines in the
|
||||
* diff. It will not be issued for binary files.
|
||||
* @param line_cb Optional callback to make per line of diff text. This
|
||||
* same callback will be made for context lines, added, and
|
||||
* removed lines, and even for a deleted trailing newline.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_foreach(
|
||||
git_diff_list *diff,
|
||||
@ -322,6 +360,14 @@ GIT_EXTERN(int) git_diff_print_compact(
|
||||
* Iterate over a diff generating text output like "git diff".
|
||||
*
|
||||
* This is a super easy way to generate a patch from a diff.
|
||||
*
|
||||
* @param diff A git_diff_list generated by one of the above functions.
|
||||
* @param cb_data Reference pointer that will be passed to your callbacks.
|
||||
* @param print_cb Callback function to output lines of the diff. This
|
||||
* same function will be called for file headers, hunk
|
||||
* headers, and diff lines. Fortunately, you can probably
|
||||
* use various GIT_DIFF_LINE constants to determine what
|
||||
* text you are given.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_print_patch(
|
||||
git_diff_list *diff,
|
||||
@ -338,13 +384,14 @@ GIT_EXTERN(int) git_diff_print_patch(
|
||||
/**
|
||||
* Directly run a text diff on two blobs.
|
||||
*
|
||||
* Compared to a file, a blob lacks some contextual information. As such, the
|
||||
* `git_diff_file` parameters of the callbacks will be filled accordingly to the following:
|
||||
* `mode` will be set to 0, `path` will be set to NULL. When dealing with a NULL blob, `oid`
|
||||
* will be set to 0.
|
||||
* Compared to a file, a blob lacks some contextual information. As such,
|
||||
* the `git_diff_file` parameters of the callbacks will be filled
|
||||
* accordingly to the following: `mode` will be set to 0, `path` will be set
|
||||
* to NULL. When dealing with a NULL blob, `oid` will be set to 0.
|
||||
*
|
||||
* When at least one of the blobs being dealt with is binary, the `git_diff_delta` binary
|
||||
* attribute will be set to 1 and no call to the hunk_cb nor line_cb will be made.
|
||||
* When at least one of the blobs being dealt with is binary, the
|
||||
* `git_diff_delta` binary attribute will be set to 1 and no call to the
|
||||
* hunk_cb nor line_cb will be made.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_blobs(
|
||||
git_blob *old_blob,
|
||||
|
||||
@ -102,7 +102,7 @@ enum {
|
||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1 << 0),
|
||||
GIT_STATUS_OPT_INCLUDE_IGNORED = (1 << 1),
|
||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1 << 2),
|
||||
GIT_STATUS_OPT_EXCLUDE_SUBMODULED = (1 << 3),
|
||||
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1 << 3),
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1 << 4),
|
||||
};
|
||||
|
||||
|
||||
104
src/diff.c
104
src/diff.c
@ -130,37 +130,50 @@ fail:
|
||||
static git_diff_delta *diff_delta__merge_like_cgit(
|
||||
const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
|
||||
{
|
||||
git_diff_delta *dup = diff_delta__dup(a, pool);
|
||||
if (!dup)
|
||||
return NULL;
|
||||
|
||||
if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
|
||||
return dup;
|
||||
|
||||
git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
|
||||
|
||||
dup->new_file.mode = b->new_file.mode;
|
||||
dup->new_file.size = b->new_file.size;
|
||||
dup->new_file.flags = b->new_file.flags;
|
||||
git_diff_delta *dup;
|
||||
|
||||
/* Emulate C git for merging two diffs (a la 'git diff <sha>').
|
||||
*
|
||||
* When C git does a diff between the work dir and a tree, it actually
|
||||
* diffs with the index but uses the workdir contents. This emulates
|
||||
* those choices so we can emulate the type of diff.
|
||||
*
|
||||
* We have three file descriptions here, let's call them:
|
||||
* f1 = a->old_file
|
||||
* f2 = a->new_file AND b->old_file
|
||||
* f3 = b->new_file
|
||||
*/
|
||||
if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
|
||||
if (dup->status == GIT_DELTA_DELETED)
|
||||
/* preserve pending delete info */;
|
||||
else if (b->status == GIT_DELTA_UNTRACKED ||
|
||||
b->status == GIT_DELTA_IGNORED)
|
||||
dup->status = b->status;
|
||||
else
|
||||
|
||||
/* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
|
||||
if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
|
||||
return diff_delta__dup(a, pool);
|
||||
|
||||
/* otherwise, base this diff on the 'b' diff */
|
||||
if ((dup = diff_delta__dup(b, pool)) == NULL)
|
||||
return NULL;
|
||||
|
||||
/* If 'a' status is uninteresting, then we're done */
|
||||
if (a->status == GIT_DELTA_UNMODIFIED)
|
||||
return dup;
|
||||
|
||||
assert(a->status != GIT_DELTA_UNMODIFIED);
|
||||
assert(b->status != GIT_DELTA_UNMODIFIED);
|
||||
|
||||
/* A cgit exception is that the diff of a file that is only in the
|
||||
* index (i.e. not in HEAD nor workdir) is given as empty.
|
||||
*/
|
||||
if (dup->status == GIT_DELTA_DELETED) {
|
||||
if (a->status == GIT_DELTA_ADDED)
|
||||
dup->status = GIT_DELTA_UNMODIFIED;
|
||||
/* else don't overwrite DELETE status */
|
||||
} else {
|
||||
dup->status = a->status;
|
||||
}
|
||||
else if (dup->status == GIT_DELTA_UNMODIFIED ||
|
||||
b->status == GIT_DELTA_DELETED)
|
||||
dup->status = b->status;
|
||||
|
||||
git_oid_cpy(&dup->old_file.oid, &a->old_file.oid);
|
||||
dup->old_file.mode = a->old_file.mode;
|
||||
dup->old_file.size = a->old_file.size;
|
||||
dup->old_file.flags = a->old_file.flags;
|
||||
|
||||
return dup;
|
||||
}
|
||||
@ -214,7 +227,9 @@ static int diff_delta__from_two(
|
||||
git_diff_list *diff,
|
||||
git_delta_t status,
|
||||
const git_index_entry *old_entry,
|
||||
uint32_t old_mode,
|
||||
const git_index_entry *new_entry,
|
||||
uint32_t new_mode,
|
||||
git_oid *new_oid)
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
@ -224,19 +239,22 @@ static int diff_delta__from_two(
|
||||
return 0;
|
||||
|
||||
if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
|
||||
const git_index_entry *temp = old_entry;
|
||||
uint32_t temp_mode = old_mode;
|
||||
const git_index_entry *temp_entry = old_entry;
|
||||
old_entry = new_entry;
|
||||
new_entry = temp;
|
||||
new_entry = temp_entry;
|
||||
old_mode = new_mode;
|
||||
new_mode = temp_mode;
|
||||
}
|
||||
|
||||
delta = diff_delta__alloc(diff, status, old_entry->path);
|
||||
GITERR_CHECK_ALLOC(delta);
|
||||
|
||||
delta->old_file.mode = old_entry->mode;
|
||||
delta->old_file.mode = old_mode;
|
||||
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
delta->new_file.mode = new_entry->mode;
|
||||
delta->new_file.mode = new_mode;
|
||||
git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
|
||||
if (new_oid || !git_oid_iszero(&new_entry->oid))
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
@ -300,7 +318,7 @@ static git_diff_list *git_diff_list_alloc(
|
||||
if (config_bool(cfg, "core.ignorestat", 0))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
|
||||
if (config_bool(cfg, "core.filemode", 1))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS;
|
||||
if (config_bool(cfg, "core.trustctime", 1))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
|
||||
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
|
||||
@ -419,7 +437,7 @@ static int oid_for_workdir_item(
|
||||
return result;
|
||||
}
|
||||
|
||||
#define EXEC_BIT_MASK 0000111
|
||||
#define MODE_BITS_MASK 0000777
|
||||
|
||||
static int maybe_modified(
|
||||
git_iterator *old_iter,
|
||||
@ -443,13 +461,13 @@ static int maybe_modified(
|
||||
!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
|
||||
nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
|
||||
|
||||
/* on platforms with no execmode, clear exec bit from comparisons */
|
||||
if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
|
||||
omode = omode & ~EXEC_BIT_MASK;
|
||||
nmode = nmode & ~EXEC_BIT_MASK;
|
||||
}
|
||||
/* on platforms with no execmode, just preserve old mode */
|
||||
if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
|
||||
(nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) &&
|
||||
new_iter->type == GIT_ITERATOR_WORKDIR)
|
||||
nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
|
||||
|
||||
/* support "assume unchanged" (badly, b/c we still stat everything) */
|
||||
/* support "assume unchanged" (poorly, b/c we still stat everything) */
|
||||
if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
|
||||
status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
|
||||
GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
|
||||
@ -471,8 +489,13 @@ static int maybe_modified(
|
||||
omode == nmode)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* if we have a workdir item with an unknown oid, check deeper */
|
||||
else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
|
||||
/* if modes match and we have an unknown OID and a workdir iterator,
|
||||
* then check deeper for matching
|
||||
*/
|
||||
else if (omode == nmode &&
|
||||
git_oid_iszero(&nitem->oid) &&
|
||||
new_iter->type == GIT_ITERATOR_WORKDIR)
|
||||
{
|
||||
/* TODO: add check against index file st_mtime to avoid racy-git */
|
||||
|
||||
/* if they files look exactly alike, then we'll assume the same */
|
||||
@ -517,7 +540,8 @@ static int maybe_modified(
|
||||
use_noid = &noid;
|
||||
}
|
||||
|
||||
return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
|
||||
return diff_delta__from_two(
|
||||
diff, status, oitem, omode, nitem, nmode, use_noid);
|
||||
}
|
||||
|
||||
static int diff_from_iterators(
|
||||
@ -772,6 +796,12 @@ int git_diff_merge(
|
||||
git_vector_swap(&onto->deltas, &onto_new);
|
||||
git_pool_swap(&onto->pool, &onto_pool);
|
||||
onto->new_src = from->new_src;
|
||||
|
||||
/* prefix strings also come from old pool, so recreate those.*/
|
||||
onto->opts.old_prefix =
|
||||
git_pool_strdup(&onto->pool, onto->opts.old_prefix);
|
||||
onto->opts.new_prefix =
|
||||
git_pool_strdup(&onto->pool, onto->opts.new_prefix);
|
||||
}
|
||||
|
||||
git_vector_foreach(&onto_new, i, delta)
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
enum {
|
||||
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
|
||||
GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
|
||||
GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */
|
||||
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
|
||||
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
|
||||
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
|
||||
};
|
||||
@ -36,5 +36,8 @@ struct git_diff_list {
|
||||
uint32_t diffcaps;
|
||||
};
|
||||
|
||||
extern void git_diff__cleanup_modes(
|
||||
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@ -83,12 +83,13 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
|
||||
info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0)
|
||||
return -1;
|
||||
|
||||
/* deal with adding and removing newline at EOF */
|
||||
/* This should only happen if we are adding a line that does not
|
||||
* have a newline at the end and the old code did. In that case,
|
||||
* we have a ADD with a DEL_EOFNL as a pair.
|
||||
*/
|
||||
if (len == 3) {
|
||||
if (origin == GIT_DIFF_LINE_ADDITION)
|
||||
origin = GIT_DIFF_LINE_ADD_EOFNL;
|
||||
else
|
||||
origin = GIT_DIFF_LINE_DEL_EOFNL;
|
||||
origin = (origin == GIT_DIFF_LINE_ADDITION) ?
|
||||
GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL;
|
||||
|
||||
return info->line_cb(
|
||||
info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size);
|
||||
@ -359,7 +360,7 @@ int git_diff_foreach(
|
||||
|
||||
/* map files */
|
||||
if (delta->binary != 1 &&
|
||||
(hunk_cb || line_cb) &&
|
||||
(hunk_cb || line_cb || git_oid_iszero(&delta->old_file.oid)) &&
|
||||
(delta->status == GIT_DELTA_DELETED ||
|
||||
delta->status == GIT_DELTA_MODIFIED))
|
||||
{
|
||||
@ -397,7 +398,9 @@ int git_diff_foreach(
|
||||
/* since we did not have the definitive oid, we may have
|
||||
* incorrect status and need to skip this item.
|
||||
*/
|
||||
if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) {
|
||||
if (delta->old_file.mode == delta->new_file.mode &&
|
||||
!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
|
||||
{
|
||||
delta->status = GIT_DELTA_UNMODIFIED;
|
||||
if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
goto cleanup;
|
||||
@ -420,7 +423,8 @@ int git_diff_foreach(
|
||||
*/
|
||||
|
||||
if (file_cb != NULL) {
|
||||
error = file_cb(data, delta, (float)info.index / diff->deltas.length);
|
||||
error = file_cb(
|
||||
data, delta, (float)info.index / diff->deltas.length);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
@ -433,6 +437,10 @@ int git_diff_foreach(
|
||||
if (!old_data.len && !new_data.len)
|
||||
goto cleanup;
|
||||
|
||||
/* nothing to do if only diff was a mode change */
|
||||
if (!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
|
||||
goto cleanup;
|
||||
|
||||
assert(hunk_cb || line_cb);
|
||||
|
||||
info.delta = delta;
|
||||
|
||||
10
src/path.c
10
src/path.c
@ -17,6 +17,10 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
#define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':')
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Based on the Android implementation, BSD licensed.
|
||||
* Check http://android.git.kernel.org/
|
||||
@ -105,7 +109,7 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
|
||||
/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
|
||||
'C:/' here */
|
||||
|
||||
if (len == 2 && isalpha(path[0]) && path[1] == ':') {
|
||||
if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) {
|
||||
len = 3;
|
||||
goto Exit;
|
||||
}
|
||||
@ -170,7 +174,7 @@ int git_path_root(const char *path)
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
/* Does the root of the path look like a windows drive ? */
|
||||
if (isalpha(path[0]) && (path[1] == ':'))
|
||||
if (LOOKS_LIKE_DRIVE_PREFIX(path))
|
||||
offset += 2;
|
||||
|
||||
/* Are we dealing with a windows network path? */
|
||||
@ -210,7 +214,7 @@ int git_path_prettify(git_buf *path_out, const char *path, const char *base)
|
||||
giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);
|
||||
|
||||
git_buf_clear(path_out);
|
||||
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@ -155,3 +155,27 @@ void cl_git_sandbox_cleanup(void)
|
||||
}
|
||||
}
|
||||
|
||||
bool cl_toggle_filemode(const char *filename)
|
||||
{
|
||||
struct stat st1, st2;
|
||||
|
||||
cl_must_pass(p_stat(filename, &st1));
|
||||
cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100));
|
||||
cl_must_pass(p_stat(filename, &st2));
|
||||
|
||||
return (st1.st_mode != st2.st_mode);
|
||||
}
|
||||
|
||||
bool cl_is_chmod_supported(void)
|
||||
{
|
||||
static int _is_supported = -1;
|
||||
|
||||
if (_is_supported < 0) {
|
||||
cl_git_mkfile("filemode.t", "Test if filemode can be modified");
|
||||
_is_supported = cl_toggle_filemode("filemode.t");
|
||||
cl_must_pass(p_unlink("filemode.t"));
|
||||
}
|
||||
|
||||
return _is_supported;
|
||||
}
|
||||
|
||||
|
||||
@ -40,6 +40,9 @@ void cl_git_append2file(const char *filename, const char *new_content);
|
||||
void cl_git_rewritefile(const char *filename, const char *new_content);
|
||||
void cl_git_write2file(const char *filename, const char *new_content, int mode);
|
||||
|
||||
bool cl_toggle_filemode(const char *filename);
|
||||
bool cl_is_chmod_supported(void);
|
||||
|
||||
/* Environment wrappers */
|
||||
char *cl_getenv(const char *name);
|
||||
int cl_setenv(const char *name, const char *value);
|
||||
|
||||
@ -85,11 +85,16 @@ int diff_line_fn(
|
||||
e->line_ctxt++;
|
||||
break;
|
||||
case GIT_DIFF_LINE_ADDITION:
|
||||
case GIT_DIFF_LINE_ADD_EOFNL:
|
||||
e->line_adds++;
|
||||
break;
|
||||
case GIT_DIFF_LINE_ADD_EOFNL:
|
||||
assert(0);
|
||||
break;
|
||||
case GIT_DIFF_LINE_DELETION:
|
||||
e->line_dels++;
|
||||
break;
|
||||
case GIT_DIFF_LINE_DEL_EOFNL:
|
||||
/* technically not a line delete, but we'll count it as such */
|
||||
e->line_dels++;
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -116,7 +116,7 @@ void test_diff_tree__options(void)
|
||||
{ 5, 3, 0, 2, 0, 0, 0, 4, 0, 0, 51, 2, 46, 3 },
|
||||
{ 5, 3, 0, 2, 0, 0, 0, 4, 0, 0, 53, 4, 46, 3 },
|
||||
{ 5, 0, 3, 2, 0, 0, 0, 4, 0, 0, 52, 3, 3, 46 },
|
||||
{ 5, 3, 0, 2, 0, 0, 0, 5, 0, 0, 54, 3, 48, 3 },
|
||||
{ 5, 3, 0, 2, 0, 0, 0, 5, 0, 0, 54, 3, 47, 4 },
|
||||
/* c vs d tests */
|
||||
{ 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 22, 9, 10, 3 },
|
||||
{ 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 19, 12, 7, 0 },
|
||||
|
||||
@ -5,7 +5,6 @@ static git_repository *g_repo = NULL;
|
||||
|
||||
void test_diff_workdir__initialize(void)
|
||||
{
|
||||
g_repo = cl_git_sandbox_init("status");
|
||||
}
|
||||
|
||||
void test_diff_workdir__cleanup(void)
|
||||
@ -19,6 +18,8 @@ void test_diff_workdir__to_index(void)
|
||||
git_diff_list *diff = NULL;
|
||||
diff_expects exp;
|
||||
|
||||
g_repo = cl_git_sandbox_init("status");
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
@ -59,13 +60,17 @@ void test_diff_workdir__to_tree(void)
|
||||
/* grabbed a couple of commit oids from the history of the attr repo */
|
||||
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
|
||||
const char *b_commit = "0017bd4ab1ec3"; /* the start */
|
||||
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
|
||||
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
|
||||
git_tree *a, *b;
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff = NULL;
|
||||
git_diff_list *diff2 = NULL;
|
||||
diff_expects exp;
|
||||
|
||||
g_repo = cl_git_sandbox_init("status");
|
||||
|
||||
a = resolve_commit_oid_to_tree(g_repo, a_commit);
|
||||
b = resolve_commit_oid_to_tree(g_repo, b_commit);
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
@ -87,12 +92,12 @@ void test_diff_workdir__to_tree(void)
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
|
||||
cl_assert(exp.files == 14);
|
||||
cl_assert(exp.file_adds == 0);
|
||||
cl_assert(exp.file_dels == 4);
|
||||
cl_assert(exp.file_mods == 4);
|
||||
cl_assert(exp.file_ignored == 1);
|
||||
cl_assert(exp.file_untracked == 5);
|
||||
cl_assert_equal_i(14, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_adds);
|
||||
cl_assert_equal_i(4, exp.file_dels);
|
||||
cl_assert_equal_i(4, exp.file_mods);
|
||||
cl_assert_equal_i(1, exp.file_ignored);
|
||||
cl_assert_equal_i(5, exp.file_untracked);
|
||||
|
||||
/* Since there is no git diff equivalent, let's just assume that the
|
||||
* text diffs produced by git_diff_foreach are accurate here. We will
|
||||
@ -115,19 +120,19 @@ void test_diff_workdir__to_tree(void)
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
|
||||
cl_assert(exp.files == 15);
|
||||
cl_assert(exp.file_adds == 2);
|
||||
cl_assert(exp.file_dels == 5);
|
||||
cl_assert(exp.file_mods == 4);
|
||||
cl_assert(exp.file_ignored == 1);
|
||||
cl_assert(exp.file_untracked == 3);
|
||||
cl_assert_equal_i(15, exp.files);
|
||||
cl_assert_equal_i(2, exp.file_adds);
|
||||
cl_assert_equal_i(5, exp.file_dels);
|
||||
cl_assert_equal_i(4, exp.file_mods);
|
||||
cl_assert_equal_i(1, exp.file_ignored);
|
||||
cl_assert_equal_i(3, exp.file_untracked);
|
||||
|
||||
cl_assert(exp.hunks == 11);
|
||||
cl_assert_equal_i(11, exp.hunks);
|
||||
|
||||
cl_assert(exp.lines == 17);
|
||||
cl_assert(exp.line_ctxt == 4);
|
||||
cl_assert(exp.line_adds == 8);
|
||||
cl_assert(exp.line_dels == 5);
|
||||
cl_assert_equal_i(17, exp.lines);
|
||||
cl_assert_equal_i(4, exp.line_ctxt);
|
||||
cl_assert_equal_i(8, exp.line_adds);
|
||||
cl_assert_equal_i(5, exp.line_dels);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
diff = NULL;
|
||||
@ -144,19 +149,19 @@ void test_diff_workdir__to_tree(void)
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
|
||||
cl_assert(exp.files == 16);
|
||||
cl_assert(exp.file_adds == 5);
|
||||
cl_assert(exp.file_dels == 4);
|
||||
cl_assert(exp.file_mods == 3);
|
||||
cl_assert(exp.file_ignored == 1);
|
||||
cl_assert(exp.file_untracked == 3);
|
||||
cl_assert_equal_i(16, exp.files);
|
||||
cl_assert_equal_i(5, exp.file_adds);
|
||||
cl_assert_equal_i(4, exp.file_dels);
|
||||
cl_assert_equal_i(3, exp.file_mods);
|
||||
cl_assert_equal_i(1, exp.file_ignored);
|
||||
cl_assert_equal_i(3, exp.file_untracked);
|
||||
|
||||
cl_assert(exp.hunks == 12);
|
||||
cl_assert_equal_i(12, exp.hunks);
|
||||
|
||||
cl_assert(exp.lines == 19);
|
||||
cl_assert(exp.line_ctxt == 3);
|
||||
cl_assert(exp.line_adds == 12);
|
||||
cl_assert(exp.line_dels == 4);
|
||||
cl_assert_equal_i(19, exp.lines);
|
||||
cl_assert_equal_i(3, exp.line_ctxt);
|
||||
cl_assert_equal_i(12, exp.line_adds);
|
||||
cl_assert_equal_i(4, exp.line_dels);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
@ -171,6 +176,8 @@ void test_diff_workdir__to_index_with_pathspec(void)
|
||||
diff_expects exp;
|
||||
char *pathspec = NULL;
|
||||
|
||||
g_repo = cl_git_sandbox_init("status");
|
||||
|
||||
opts.context_lines = 3;
|
||||
opts.interhunk_lines = 1;
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
@ -237,6 +244,241 @@ void test_diff_workdir__to_index_with_pathspec(void)
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
void test_diff_workdir__filemode_changes(void)
|
||||
{
|
||||
git_config *cfg;
|
||||
git_diff_list *diff = NULL;
|
||||
diff_expects exp;
|
||||
|
||||
if (!cl_is_chmod_supported())
|
||||
return;
|
||||
|
||||
g_repo = cl_git_sandbox_init("issue_592");
|
||||
|
||||
cl_git_pass(git_repository_config(&cfg, g_repo));
|
||||
cl_git_pass(git_config_set_bool(cfg, "core.filemode", true));
|
||||
|
||||
/* test once with no mods */
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
|
||||
cl_assert_equal_i(0, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_mods);
|
||||
cl_assert_equal_i(0, exp.hunks);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* chmod file and test again */
|
||||
|
||||
cl_assert(cl_toggle_filemode("issue_592/a.txt"));
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
|
||||
cl_assert_equal_i(1, exp.files);
|
||||
cl_assert_equal_i(1, exp.file_mods);
|
||||
cl_assert_equal_i(0, exp.hunks);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
cl_assert(cl_toggle_filemode("issue_592/a.txt"));
|
||||
git_config_free(cfg);
|
||||
}
|
||||
|
||||
void test_diff_workdir__filemode_changes_with_filemode_false(void)
|
||||
{
|
||||
git_config *cfg;
|
||||
git_diff_list *diff = NULL;
|
||||
diff_expects exp;
|
||||
|
||||
if (!cl_is_chmod_supported())
|
||||
return;
|
||||
|
||||
g_repo = cl_git_sandbox_init("issue_592");
|
||||
|
||||
cl_git_pass(git_repository_config(&cfg, g_repo));
|
||||
cl_git_pass(git_config_set_bool(cfg, "core.filemode", false));
|
||||
|
||||
/* test once with no mods */
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
|
||||
cl_assert_equal_i(0, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_mods);
|
||||
cl_assert_equal_i(0, exp.hunks);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* chmod file and test again */
|
||||
|
||||
cl_assert(cl_toggle_filemode("issue_592/a.txt"));
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
|
||||
cl_assert_equal_i(0, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_mods);
|
||||
cl_assert_equal_i(0, exp.hunks);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
cl_assert(cl_toggle_filemode("issue_592/a.txt"));
|
||||
git_config_free(cfg);
|
||||
}
|
||||
|
||||
void test_diff_workdir__head_index_and_workdir_all_differ(void)
|
||||
{
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff_i2t = NULL, *diff_w2i = NULL;
|
||||
diff_expects exp;
|
||||
char *pathspec = "staged_changes_modified_file";
|
||||
git_tree *tree;
|
||||
|
||||
/* For this file,
|
||||
* - head->index diff has 1 line of context, 1 line of diff
|
||||
* - index->workdir diff has 2 lines of context, 1 line of diff
|
||||
* but
|
||||
* - head->workdir diff has 1 line of context, 2 lines of diff
|
||||
* Let's make sure the right one is returned from each fn.
|
||||
*/
|
||||
|
||||
g_repo = cl_git_sandbox_init("status");
|
||||
|
||||
tree = resolve_commit_oid_to_tree(g_repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
|
||||
|
||||
opts.pathspec.strings = &pathspec;
|
||||
opts.pathspec.count = 1;
|
||||
|
||||
cl_git_pass(git_diff_index_to_tree(g_repo, &opts, tree, &diff_i2t));
|
||||
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff_w2i));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
cl_assert_equal_i(1, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_adds);
|
||||
cl_assert_equal_i(0, exp.file_dels);
|
||||
cl_assert_equal_i(1, exp.file_mods);
|
||||
cl_assert_equal_i(1, exp.hunks);
|
||||
cl_assert_equal_i(2, exp.lines);
|
||||
cl_assert_equal_i(1, exp.line_ctxt);
|
||||
cl_assert_equal_i(1, exp.line_adds);
|
||||
cl_assert_equal_i(0, exp.line_dels);
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff_w2i, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
cl_assert_equal_i(1, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_adds);
|
||||
cl_assert_equal_i(0, exp.file_dels);
|
||||
cl_assert_equal_i(1, exp.file_mods);
|
||||
cl_assert_equal_i(1, exp.hunks);
|
||||
cl_assert_equal_i(3, exp.lines);
|
||||
cl_assert_equal_i(2, exp.line_ctxt);
|
||||
cl_assert_equal_i(1, exp.line_adds);
|
||||
cl_assert_equal_i(0, exp.line_dels);
|
||||
|
||||
cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
cl_assert_equal_i(1, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_adds);
|
||||
cl_assert_equal_i(0, exp.file_dels);
|
||||
cl_assert_equal_i(1, exp.file_mods);
|
||||
cl_assert_equal_i(1, exp.hunks);
|
||||
cl_assert_equal_i(3, exp.lines);
|
||||
cl_assert_equal_i(1, exp.line_ctxt);
|
||||
cl_assert_equal_i(2, exp.line_adds);
|
||||
cl_assert_equal_i(0, exp.line_dels);
|
||||
|
||||
git_diff_list_free(diff_i2t);
|
||||
git_diff_list_free(diff_w2i);
|
||||
}
|
||||
|
||||
void test_diff_workdir__eof_newline_changes(void)
|
||||
{
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff = NULL;
|
||||
diff_expects exp;
|
||||
char *pathspec = "current_file";
|
||||
|
||||
g_repo = cl_git_sandbox_init("status");
|
||||
|
||||
opts.pathspec.strings = &pathspec;
|
||||
opts.pathspec.count = 1;
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
cl_assert_equal_i(0, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_adds);
|
||||
cl_assert_equal_i(0, exp.file_dels);
|
||||
cl_assert_equal_i(0, exp.file_mods);
|
||||
cl_assert_equal_i(0, exp.hunks);
|
||||
cl_assert_equal_i(0, exp.lines);
|
||||
cl_assert_equal_i(0, exp.line_ctxt);
|
||||
cl_assert_equal_i(0, exp.line_adds);
|
||||
cl_assert_equal_i(0, exp.line_dels);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
cl_git_append2file("status/current_file", "\n");
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
cl_assert_equal_i(1, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_adds);
|
||||
cl_assert_equal_i(0, exp.file_dels);
|
||||
cl_assert_equal_i(1, exp.file_mods);
|
||||
cl_assert_equal_i(1, exp.hunks);
|
||||
cl_assert_equal_i(2, exp.lines);
|
||||
cl_assert_equal_i(1, exp.line_ctxt);
|
||||
cl_assert_equal_i(1, exp.line_adds);
|
||||
cl_assert_equal_i(0, exp.line_dels);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
cl_git_rewritefile("status/current_file", "current_file");
|
||||
|
||||
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
cl_git_pass(git_diff_foreach(
|
||||
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
|
||||
cl_assert_equal_i(1, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_adds);
|
||||
cl_assert_equal_i(0, exp.file_dels);
|
||||
cl_assert_equal_i(1, exp.file_mods);
|
||||
cl_assert_equal_i(1, exp.hunks);
|
||||
cl_assert_equal_i(3, exp.lines);
|
||||
cl_assert_equal_i(0, exp.line_ctxt);
|
||||
cl_assert_equal_i(1, exp.line_adds);
|
||||
cl_assert_equal_i(2, exp.line_dels);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
}
|
||||
|
||||
/* PREPARATION OF TEST DATA
|
||||
*
|
||||
* Since there is no command line equivalent of git_diff_workdir_to_tree,
|
||||
|
||||
1
tests-clar/resources/filemodes/.gitted/HEAD
Normal file
1
tests-clar/resources/filemodes/.gitted/HEAD
Normal file
@ -0,0 +1 @@
|
||||
ref: refs/heads/master
|
||||
6
tests-clar/resources/filemodes/.gitted/config
Normal file
6
tests-clar/resources/filemodes/.gitted/config
Normal file
@ -0,0 +1,6 @@
|
||||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = false
|
||||
logallrefupdates = true
|
||||
ignorecase = true
|
||||
1
tests-clar/resources/filemodes/.gitted/description
Normal file
1
tests-clar/resources/filemodes/.gitted/description
Normal file
@ -0,0 +1 @@
|
||||
Unnamed repository; edit this file 'description' to name the repository.
|
||||
24
tests-clar/resources/filemodes/.gitted/hooks/commit-msg.sample
Executable file
24
tests-clar/resources/filemodes/.gitted/hooks/commit-msg.sample
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to check the commit log message.
|
||||
# Called by "git commit" with one argument, the name of the file
|
||||
# that has the commit message. The hook should exit with non-zero
|
||||
# status after issuing an appropriate message if it wants to stop the
|
||||
# commit. The hook is allowed to edit the commit message file.
|
||||
#
|
||||
# To enable this hook, rename this file to "commit-msg".
|
||||
|
||||
# Uncomment the below to add a Signed-off-by line to the message.
|
||||
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
||||
# hook is more suited to it.
|
||||
#
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
||||
|
||||
# This example catches duplicate Signed-off-by lines.
|
||||
|
||||
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
||||
echo >&2 Duplicate Signed-off-by lines.
|
||||
exit 1
|
||||
}
|
||||
BIN
tests-clar/resources/filemodes/.gitted/index
Normal file
BIN
tests-clar/resources/filemodes/.gitted/index
Normal file
Binary file not shown.
6
tests-clar/resources/filemodes/.gitted/info/exclude
Normal file
6
tests-clar/resources/filemodes/.gitted/info/exclude
Normal file
@ -0,0 +1,6 @@
|
||||
# git ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
||||
1
tests-clar/resources/filemodes/.gitted/logs/HEAD
Normal file
1
tests-clar/resources/filemodes/.gitted/logs/HEAD
Normal file
@ -0,0 +1 @@
|
||||
0000000000000000000000000000000000000000 9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a Russell Belfer <rb@github.com> 1338847682 -0700 commit (initial): Initial commit of test data
|
||||
@ -0,0 +1 @@
|
||||
0000000000000000000000000000000000000000 9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a Russell Belfer <rb@github.com> 1338847682 -0700 commit (initial): Initial commit of test data
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
tests-clar/resources/filemodes/.gitted/refs/heads/master
Normal file
1
tests-clar/resources/filemodes/.gitted/refs/heads/master
Normal file
@ -0,0 +1 @@
|
||||
9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
|
||||
1
tests-clar/resources/filemodes/exec_off
Normal file
1
tests-clar/resources/filemodes/exec_off
Normal file
@ -0,0 +1 @@
|
||||
Howdy
|
||||
1
tests-clar/resources/filemodes/exec_off2on_staged
Executable file
1
tests-clar/resources/filemodes/exec_off2on_staged
Executable file
@ -0,0 +1 @@
|
||||
Howdy
|
||||
1
tests-clar/resources/filemodes/exec_off2on_workdir
Executable file
1
tests-clar/resources/filemodes/exec_off2on_workdir
Executable file
@ -0,0 +1 @@
|
||||
Howdy
|
||||
1
tests-clar/resources/filemodes/exec_off_untracked
Normal file
1
tests-clar/resources/filemodes/exec_off_untracked
Normal file
@ -0,0 +1 @@
|
||||
Howdy
|
||||
1
tests-clar/resources/filemodes/exec_on
Executable file
1
tests-clar/resources/filemodes/exec_on
Executable file
@ -0,0 +1 @@
|
||||
Howdy
|
||||
1
tests-clar/resources/filemodes/exec_on2off_staged
Normal file
1
tests-clar/resources/filemodes/exec_on2off_staged
Normal file
@ -0,0 +1 @@
|
||||
Howdy
|
||||
1
tests-clar/resources/filemodes/exec_on2off_workdir
Normal file
1
tests-clar/resources/filemodes/exec_on2off_workdir
Normal file
@ -0,0 +1 @@
|
||||
Howdy
|
||||
1
tests-clar/resources/filemodes/exec_on_untracked
Executable file
1
tests-clar/resources/filemodes/exec_on_untracked
Executable file
@ -0,0 +1 @@
|
||||
Howdy
|
||||
@ -581,3 +581,69 @@ void test_status_worktree__space_in_filename(void)
|
||||
git_index_free(index);
|
||||
git_repository_free(repo);
|
||||
}
|
||||
|
||||
static const char *filemode_paths[] = {
|
||||
"exec_off",
|
||||
"exec_off2on_staged",
|
||||
"exec_off2on_workdir",
|
||||
"exec_off_untracked",
|
||||
"exec_on",
|
||||
"exec_on2off_staged",
|
||||
"exec_on2off_workdir",
|
||||
"exec_on_untracked",
|
||||
};
|
||||
|
||||
static unsigned int filemode_statuses[] = {
|
||||
GIT_STATUS_CURRENT,
|
||||
GIT_STATUS_INDEX_MODIFIED,
|
||||
GIT_STATUS_WT_MODIFIED,
|
||||
GIT_STATUS_WT_NEW,
|
||||
GIT_STATUS_CURRENT,
|
||||
GIT_STATUS_INDEX_MODIFIED,
|
||||
GIT_STATUS_WT_MODIFIED,
|
||||
GIT_STATUS_WT_NEW
|
||||
};
|
||||
|
||||
static const size_t filemode_count = 8;
|
||||
|
||||
void test_status_worktree__filemode_changes(void)
|
||||
{
|
||||
git_repository *repo = cl_git_sandbox_init("filemodes");
|
||||
status_entry_counts counts;
|
||||
git_status_options opts;
|
||||
git_config *cfg;
|
||||
|
||||
/* overwrite stored filemode with platform appropriate value */
|
||||
cl_git_pass(git_repository_config(&cfg, repo));
|
||||
if (cl_is_chmod_supported())
|
||||
cl_git_pass(git_config_set_bool(cfg, "core.filemode", true));
|
||||
else {
|
||||
unsigned int i;
|
||||
cl_git_pass(git_config_set_bool(cfg, "core.filemode", false));
|
||||
|
||||
/* won't trust filesystem mode diffs, so these will appear unchanged */
|
||||
for (i = 0; i < filemode_count; ++i)
|
||||
if (filemode_statuses[i] == GIT_STATUS_WT_MODIFIED)
|
||||
filemode_statuses[i] = GIT_STATUS_CURRENT;
|
||||
}
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||
GIT_STATUS_OPT_INCLUDE_IGNORED |
|
||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
|
||||
|
||||
memset(&counts, 0, sizeof(counts));
|
||||
counts.expected_entry_count = filemode_count;
|
||||
counts.expected_paths = filemode_paths;
|
||||
counts.expected_statuses = filemode_statuses;
|
||||
|
||||
cl_git_pass(
|
||||
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
|
||||
);
|
||||
|
||||
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
|
||||
cl_assert_equal_i(0, counts.wrong_status_flags_count);
|
||||
cl_assert_equal_i(0, counts.wrong_sorted_path);
|
||||
|
||||
git_config_free(cfg);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user