Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
git2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
git2
Commits
f3e49210
Commit
f3e49210
authored
Feb 08, 2013
by
Russell Belfer
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1249 from yorah/topic/diff-notify-unmatched-pathspecs
diff: Add a callback to notify of diffed files
parents
5b62eb7d
0d64ba48
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
291 additions
and
53 deletions
+291
-53
include/git2/diff.h
+58
-34
src/checkout.c
+1
-1
src/diff.c
+38
-12
src/index.c
+2
-1
src/pathspec.c
+14
-3
src/pathspec.h
+10
-2
tests-clar/diff/diff_helpers.h
+5
-0
tests-clar/diff/workdir.c
+163
-0
No files found.
include/git2/diff.h
View file @
f3e49210
...
...
@@ -123,40 +123,6 @@ typedef enum {
}
git_diff_option_t
;
/**
* Structure describing options about how the diff should be executed.
*
* Setting all values of the structure to zero will yield the default
* values. Similarly, passing NULL for the options structure will
* give the defaults. The default values are marked below.
*
* - `flags` is a combination of the `git_diff_option_t` values above
* - `context_lines` is the number of unchanged lines that define the
* boundary of a hunk (and to display before and after)
* - `interhunk_lines` is the maximum number of unchanged lines between
* hunk boundaries before the hunks will be merged into a one.
* - `old_prefix` is the virtual "directory" to prefix to old file names
* in hunk headers (default "a")
* - `new_prefix` is the virtual "directory" to prefix to new file names
* in hunk headers (default "b")
* - `pathspec` is an array of paths / fnmatch patterns to constrain diff
* - `max_size` is a file size (in bytes) above which a blob will be marked
* as binary automatically; pass a negative value to disable.
*/
typedef
struct
{
unsigned
int
version
;
/**< version for the struct */
uint32_t
flags
;
/**< defaults to GIT_DIFF_NORMAL */
uint16_t
context_lines
;
/**< defaults to 3 */
uint16_t
interhunk_lines
;
/**< defaults to 0 */
const
char
*
old_prefix
;
/**< defaults to "a" */
const
char
*
new_prefix
;
/**< defaults to "b" */
git_strarray
pathspec
;
/**< defaults to include all paths */
git_off_t
max_size
;
/**< defaults to 512MB */
}
git_diff_options
;
#define GIT_DIFF_OPTIONS_VERSION 1
#define GIT_DIFF_OPTIONS_INIT {GIT_DIFF_OPTIONS_VERSION}
/**
* The diff list object that contains all individual file deltas.
*
* This is an opaque structure which will be allocated by one of the diff
...
...
@@ -266,6 +232,64 @@ typedef struct {
}
git_diff_delta
;
/**
* Diff notification callback function.
*
* The callback will be called for each file, just before the `git_delta_t`
* gets inserted into the diff list.
*
* When the callback:
* - returns < 0, the diff process will be aborted.
* - returns > 0, the delta will not be inserted into the diff list, but the
* diff process continues.
* - returns 0, the delta is inserted into the diff list, and the diff process
* continues.
*/
typedef
int
(
*
git_diff_notify_cb
)(
const
git_diff_list
*
diff_so_far
,
const
git_diff_delta
*
delta_to_add
,
const
char
*
matched_pathspec
,
void
*
payload
);
/**
* Structure describing options about how the diff should be executed.
*
* Setting all values of the structure to zero will yield the default
* values. Similarly, passing NULL for the options structure will
* give the defaults. The default values are marked below.
*
* - `flags` is a combination of the `git_diff_option_t` values above
* - `context_lines` is the number of unchanged lines that define the
* boundary of a hunk (and to display before and after)
* - `interhunk_lines` is the maximum number of unchanged lines between
* hunk boundaries before the hunks will be merged into a one.
* - `old_prefix` is the virtual "directory" to prefix to old file names
* in hunk headers (default "a")
* - `new_prefix` is the virtual "directory" to prefix to new file names
* in hunk headers (default "b")
* - `pathspec` is an array of paths / fnmatch patterns to constrain diff
* - `max_size` is a file size (in bytes) above which a blob will be marked
* as binary automatically; pass a negative value to disable.
* - `notify_cb` is an optional callback function, notifying the consumer of
* which files are being examined as the diff is generated
* - `notify_payload` is the payload data to pass to the `notify_cb` function
*/
typedef
struct
{
unsigned
int
version
;
/**< version for the struct */
uint32_t
flags
;
/**< defaults to GIT_DIFF_NORMAL */
uint16_t
context_lines
;
/**< defaults to 3 */
uint16_t
interhunk_lines
;
/**< defaults to 0 */
const
char
*
old_prefix
;
/**< defaults to "a" */
const
char
*
new_prefix
;
/**< defaults to "b" */
git_strarray
pathspec
;
/**< defaults to include all paths */
git_off_t
max_size
;
/**< defaults to 512MB */
git_diff_notify_cb
notify_cb
;
void
*
notify_payload
;
}
git_diff_options
;
#define GIT_DIFF_OPTIONS_VERSION 1
#define GIT_DIFF_OPTIONS_INIT {GIT_DIFF_OPTIONS_VERSION}
/**
* When iterating over a diff, callback that will be made per file.
*
* @param delta A pointer to the delta data for the file
...
...
src/checkout.c
View file @
f3e49210
...
...
@@ -224,7 +224,7 @@ static int checkout_action_wd_only(
if
(
!
git_pathspec_match_path
(
pathspec
,
wd
->
path
,
(
data
->
strategy
&
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH
)
!=
0
,
git_iterator_ignore_case
(
workdir
)))
git_iterator_ignore_case
(
workdir
)
,
NULL
))
return
0
;
/* check if item is tracked in the index but not in the checkout diff */
...
...
src/diff.c
View file @
f3e49210
...
...
@@ -41,12 +41,26 @@ static git_diff_delta *diff_delta__alloc(
return
delta
;
}
static
int
diff_notify
(
const
git_diff_list
*
diff
,
const
git_diff_delta
*
delta
,
const
char
*
matched_pathspec
)
{
if
(
!
diff
->
opts
.
notify_cb
)
return
0
;
return
diff
->
opts
.
notify_cb
(
diff
,
delta
,
matched_pathspec
,
diff
->
opts
.
notify_payload
);
}
static
int
diff_delta__from_one
(
git_diff_list
*
diff
,
git_delta_t
status
,
const
git_index_entry
*
entry
)
{
git_diff_delta
*
delta
;
const
char
*
matched_pathspec
;
int
notify_res
;
if
(
status
==
GIT_DELTA_IGNORED
&&
(
diff
->
opts
.
flags
&
GIT_DIFF_INCLUDE_IGNORED
)
==
0
)
...
...
@@ -59,7 +73,7 @@ static int diff_delta__from_one(
if
(
!
git_pathspec_match_path
(
&
diff
->
pathspec
,
entry
->
path
,
(
diff
->
opts
.
flags
&
GIT_DIFF_DISABLE_PATHSPEC_MATCH
)
!=
0
,
(
diff
->
opts
.
flags
&
GIT_DIFF_DELTAS_ARE_ICASE
)
!=
0
))
(
diff
->
opts
.
flags
&
GIT_DIFF_DELTAS_ARE_ICASE
)
!=
0
,
&
matched_pathspec
))
return
0
;
delta
=
diff_delta__alloc
(
diff
,
status
,
entry
->
path
);
...
...
@@ -84,12 +98,16 @@ static int diff_delta__from_one(
!
git_oid_iszero
(
&
delta
->
new_file
.
oid
))
delta
->
new_file
.
flags
|=
GIT_DIFF_FILE_VALID_OID
;
if
(
git_vector_insert
(
&
diff
->
deltas
,
delta
)
<
0
)
{
notify_res
=
diff_notify
(
diff
,
delta
,
matched_pathspec
);
if
(
notify_res
)
git__free
(
delta
);
else
if
(
git_vector_insert
(
&
diff
->
deltas
,
delta
)
<
0
)
{
git__free
(
delta
);
return
-
1
;
}
return
0
;
return
notify_res
<
0
?
GIT_EUSER
:
0
;
}
static
int
diff_delta__from_two
(
...
...
@@ -99,9 +117,11 @@ static int diff_delta__from_two(
uint32_t
old_mode
,
const
git_index_entry
*
new_entry
,
uint32_t
new_mode
,
git_oid
*
new_oid
)
git_oid
*
new_oid
,
const
char
*
matched_pathspec
)
{
git_diff_delta
*
delta
;
int
notify_res
;
if
(
status
==
GIT_DELTA_UNMODIFIED
&&
(
diff
->
opts
.
flags
&
GIT_DIFF_INCLUDE_UNMODIFIED
)
==
0
)
...
...
@@ -138,12 +158,16 @@ static int diff_delta__from_two(
if
(
new_oid
||
!
git_oid_iszero
(
&
new_entry
->
oid
))
delta
->
new_file
.
flags
|=
GIT_DIFF_FILE_VALID_OID
;
if
(
git_vector_insert
(
&
diff
->
deltas
,
delta
)
<
0
)
{
notify_res
=
diff_notify
(
diff
,
delta
,
matched_pathspec
);
if
(
notify_res
)
git__free
(
delta
);
else
if
(
git_vector_insert
(
&
diff
->
deltas
,
delta
)
<
0
)
{
git__free
(
delta
);
return
-
1
;
}
return
0
;
return
notify_res
<
0
?
GIT_EUSER
:
0
;
}
static
git_diff_delta
*
diff_delta__last_for_item
(
...
...
@@ -419,13 +443,14 @@ static int maybe_modified(
unsigned
int
omode
=
oitem
->
mode
;
unsigned
int
nmode
=
nitem
->
mode
;
bool
new_is_workdir
=
(
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
->
opts
.
flags
&
GIT_DIFF_DISABLE_PATHSPEC_MATCH
)
!=
0
,
(
diff
->
opts
.
flags
&
GIT_DIFF_DELTAS_ARE_ICASE
)
!=
0
))
(
diff
->
opts
.
flags
&
GIT_DIFF_DELTAS_ARE_ICASE
)
!=
0
,
&
matched_pathspec
))
return
0
;
/* on platforms with no symlinks, preserve mode of existing symlinks */
...
...
@@ -526,7 +551,7 @@ static int maybe_modified(
}
return
diff_delta__from_two
(
diff
,
status
,
oitem
,
omode
,
nitem
,
nmode
,
use_noid
);
diff
,
status
,
oitem
,
omode
,
nitem
,
nmode
,
use_noid
,
matched_pathspec
);
}
static
bool
entry_is_prefixed
(
...
...
@@ -747,10 +772,11 @@ int git_diff__from_iterators(
else
{
assert
(
oitem
&&
nitem
&&
cmp
==
0
);
if
(
maybe_modified
(
old_iter
,
oitem
,
new_iter
,
nitem
,
diff
)
<
0
||
git_iterator_advance
(
old_iter
,
&
oitem
)
<
0
||
git_iterator_advance
(
new_iter
,
&
nitem
)
<
0
)
goto
fail
;
if
(
maybe_modified
(
old_iter
,
oitem
,
new_iter
,
nitem
,
diff
)
<
0
||
git_iterator_advance
(
old_iter
,
&
oitem
)
<
0
||
git_iterator_advance
(
new_iter
,
&
nitem
)
<
0
)
goto
fail
;
}
}
...
...
src/index.c
View file @
f3e49210
...
...
@@ -1710,7 +1710,8 @@ int git_index_read_tree_match(
goto cleanup;
while (entry != NULL) {
if (git_pathspec_match_path(&pathspec, entry->path, false, false) &&
if (git_pathspec_match_path(
&pathspec, entry->path, false, false, NULL) &&
(error = git_index_add(index, entry)) < 0)
goto cleanup;
...
...
src/pathspec.c
View file @
f3e49210
...
...
@@ -106,14 +106,21 @@ void git_pathspec_free(git_vector *vspec)
/* match a path against the vectorized pathspec */
bool
git_pathspec_match_path
(
git_vector
*
vspec
,
const
char
*
path
,
bool
disable_fnmatch
,
bool
casefold
)
git_vector
*
vspec
,
const
char
*
path
,
bool
disable_fnmatch
,
bool
casefold
,
const
char
**
matched_pathspec
)
{
unsigned
in
t
i
;
size_
t
i
;
git_attr_fnmatch
*
match
;
int
fnmatch_flags
=
0
;
int
(
*
use_strcmp
)(
const
char
*
,
const
char
*
);
int
(
*
use_strncmp
)(
const
char
*
,
const
char
*
,
size_t
);
if
(
matched_pathspec
)
*
matched_pathspec
=
NULL
;
if
(
!
vspec
||
!
vspec
->
length
)
return
true
;
...
...
@@ -143,8 +150,12 @@ bool git_pathspec_match_path(
path
[
match
->
length
]
==
'/'
)
result
=
0
;
if
(
result
==
0
)
if
(
result
==
0
)
{
if
(
matched_pathspec
)
*
matched_pathspec
=
match
->
pattern
;
return
(
match
->
flags
&
GIT_ATTR_FNMATCH_NEGATIVE
)
?
false
:
true
;
}
}
return
false
;
...
...
src/pathspec.h
View file @
f3e49210
...
...
@@ -25,8 +25,16 @@ extern int git_pathspec_init(
/* free data from the pathspec vector */
extern
void
git_pathspec_free
(
git_vector
*
vspec
);
/* match a path against the vectorized pathspec */
/*
* Match a path against the vectorized pathspec.
* The matched pathspec is passed back into the `matched_pathspec` parameter,
* unless it is passed as NULL by the caller.
*/
extern
bool
git_pathspec_match_path
(
git_vector
*
vspec
,
const
char
*
path
,
bool
disable_fnmatch
,
bool
casefold
);
git_vector
*
vspec
,
const
char
*
path
,
bool
disable_fnmatch
,
bool
casefold
,
const
char
**
matched_pathspec
);
#endif
tests-clar/diff/diff_helpers.h
View file @
f3e49210
...
...
@@ -20,6 +20,11 @@ typedef struct {
int
line_dels
;
}
diff_expects
;
typedef
struct
{
const
char
*
path
;
const
char
*
matched_pathspec
;
}
notify_expected
;
extern
int
diff_file_cb
(
const
git_diff_delta
*
delta
,
float
progress
,
...
...
tests-clar/diff/workdir.c
View file @
f3e49210
...
...
@@ -307,6 +307,169 @@ void test_diff_workdir__to_index_with_pathspec(void)
git_diff_list_free
(
diff
);
}
static
int
assert_called_notifications
(
const
git_diff_list
*
diff_so_far
,
const
git_diff_delta
*
delta_to_add
,
const
char
*
matched_pathspec
,
void
*
payload
)
{
bool
found
=
false
;
notify_expected
*
exp
=
(
notify_expected
*
)
payload
;
notify_expected
*
e
;;
GIT_UNUSED
(
diff_so_far
);
for
(
e
=
exp
;
e
->
path
!=
NULL
;
e
++
)
{
if
(
strcmp
(
e
->
path
,
delta_to_add
->
new_file
.
path
))
continue
;
cl_assert_equal_s
(
e
->
matched_pathspec
,
matched_pathspec
);
found
=
true
;
break
;
}
cl_assert
(
found
);
return
0
;
}
void
test_diff_workdir__to_index_notify
(
void
)
{
git_diff_options
opts
=
GIT_DIFF_OPTIONS_INIT
;
git_diff_list
*
diff
=
NULL
;
diff_expects
exp
;
char
*
searched_pathspecs_solo
[]
=
{
"*_deleted"
,
};
notify_expected
expected_matched_pathspecs_solo
[]
=
{
{
"file_deleted"
,
"*_deleted"
},
{
"staged_changes_file_deleted"
,
"*_deleted"
},
{
NULL
,
NULL
}
};
char
*
searched_pathspecs_multiple
[]
=
{
"staged_changes_cant_find_me"
,
"subdir/modified_cant_find_me"
,
"subdir/*"
,
"staged*"
};
notify_expected
expected_matched_pathspecs_multiple
[]
=
{
{
"staged_changes_file_deleted"
,
"staged*"
},
{
"staged_changes_modified_file"
,
"staged*"
},
{
"staged_delete_modified_file"
,
"staged*"
},
{
"staged_new_file_deleted_file"
,
"staged*"
},
{
"staged_new_file_modified_file"
,
"staged*"
},
{
"subdir/deleted_file"
,
"subdir/*"
},
{
"subdir/modified_file"
,
"subdir/*"
},
{
"subdir/new_file"
,
"subdir/*"
},
{
NULL
,
NULL
}
};
g_repo
=
cl_git_sandbox_init
(
"status"
);
opts
.
flags
|=
GIT_DIFF_INCLUDE_IGNORED
|
GIT_DIFF_INCLUDE_UNTRACKED
;
opts
.
notify_cb
=
assert_called_notifications
;
opts
.
pathspec
.
strings
=
searched_pathspecs_solo
;
opts
.
pathspec
.
count
=
1
;
opts
.
notify_payload
=
&
expected_matched_pathspecs_solo
;
memset
(
&
exp
,
0
,
sizeof
(
exp
));
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
(
2
,
exp
.
files
);
git_diff_list_free
(
diff
);
opts
.
pathspec
.
strings
=
searched_pathspecs_multiple
;
opts
.
pathspec
.
count
=
4
;
opts
.
notify_payload
=
&
expected_matched_pathspecs_multiple
;
memset
(
&
exp
,
0
,
sizeof
(
exp
));
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
(
8
,
exp
.
files
);
git_diff_list_free
(
diff
);
}
static
int
abort_diff
(
const
git_diff_list
*
diff_so_far
,
const
git_diff_delta
*
delta_to_add
,
const
char
*
matched_pathspec
,
void
*
payload
)
{
GIT_UNUSED
(
diff_so_far
);
GIT_UNUSED
(
delta_to_add
);
GIT_UNUSED
(
matched_pathspec
);
GIT_UNUSED
(
payload
);
return
-
42
;
}
void
test_diff_workdir__to_index_notify_can_be_aborted_by_callback
(
void
)
{
git_diff_options
opts
=
GIT_DIFF_OPTIONS_INIT
;
git_diff_list
*
diff
=
NULL
;
char
*
pathspec
=
NULL
;
g_repo
=
cl_git_sandbox_init
(
"status"
);
opts
.
flags
|=
GIT_DIFF_INCLUDE_IGNORED
|
GIT_DIFF_INCLUDE_UNTRACKED
;
opts
.
notify_cb
=
abort_diff
;
opts
.
pathspec
.
strings
=
&
pathspec
;
opts
.
pathspec
.
count
=
1
;
pathspec
=
"file_deleted"
;
cl_git_fail
(
git_diff_index_to_workdir
(
&
diff
,
g_repo
,
NULL
,
&
opts
));
pathspec
=
"staged_changes_modified_file"
;
cl_git_fail
(
git_diff_index_to_workdir
(
&
diff
,
g_repo
,
NULL
,
&
opts
));
}
static
int
filter_all
(
const
git_diff_list
*
diff_so_far
,
const
git_diff_delta
*
delta_to_add
,
const
char
*
matched_pathspec
,
void
*
payload
)
{
GIT_UNUSED
(
diff_so_far
);
GIT_UNUSED
(
delta_to_add
);
GIT_UNUSED
(
matched_pathspec
);
GIT_UNUSED
(
payload
);
return
42
;
}
void
test_diff_workdir__to_index_notify_can_be_used_as_filtering_function
(
void
)
{
git_diff_options
opts
=
GIT_DIFF_OPTIONS_INIT
;
git_diff_list
*
diff
=
NULL
;
char
*
pathspec
=
NULL
;
diff_expects
exp
;
g_repo
=
cl_git_sandbox_init
(
"status"
);
opts
.
flags
|=
GIT_DIFF_INCLUDE_IGNORED
|
GIT_DIFF_INCLUDE_UNTRACKED
;
opts
.
notify_cb
=
filter_all
;
opts
.
pathspec
.
strings
=
&
pathspec
;
opts
.
pathspec
.
count
=
1
;
pathspec
=
"*_deleted"
;
memset
(
&
exp
,
0
,
sizeof
(
exp
));
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
(
0
,
exp
.
files
);
git_diff_list_free
(
diff
);
}
void
test_diff_workdir__filemode_changes
(
void
)
{
git_config
*
cfg
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment