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
0eedacb0
Commit
0eedacb0
authored
Dec 11, 2013
by
Russell Belfer
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1985 from libgit2/diff-rename-config
Rename detection using diff.renames
parents
65e9dc65
5a52d6be
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
213 additions
and
11 deletions
+213
-11
include/git2/diff.h
+8
-1
src/diff_tform.c
+22
-10
tests/diff/rename.c
+183
-0
No files found.
include/git2/diff.h
View file @
0eedacb0
...
...
@@ -468,6 +468,9 @@ typedef int (*git_diff_line_cb)(
* Flags to control the behavior of diff rename/copy detection.
*/
typedef
enum
{
/** Obey `diff.renames`. This is overridden by any other GIT_DIFF_FIND_ALL flag. */
GIT_DIFF_FIND_BY_CONFIG
=
0
,
/** Look for renames? (`--find-renames`) */
GIT_DIFF_FIND_RENAMES
=
(
1u
<<
0
),
...
...
@@ -573,7 +576,11 @@ typedef struct {
typedef
struct
{
unsigned
int
version
;
/** Combination of git_diff_find_t values (default FIND_RENAMES) */
/**
* Combination of git_diff_find_t values (default FIND_BY_CONFIG).
* Note that if you don't explicitly set this, `diff.renames` could be set
* to false, resulting in `git_diff_find_similar` doing nothing.
*/
uint32_t
flags
;
/** Similarity to consider a file renamed (default 50) */
...
...
src/diff_tform.c
View file @
0eedacb0
...
...
@@ -275,28 +275,36 @@ static int normalize_find_opts(
{
git_config
*
cfg
=
NULL
;
GITERR_CHECK_VERSION
(
given
,
GIT_DIFF_FIND_OPTIONS_VERSION
,
"git_diff_find_options"
);
if
(
diff
->
repo
!=
NULL
&&
git_repository_config__weakptr
(
&
cfg
,
diff
->
repo
)
<
0
)
return
-
1
;
if
(
given
!=
NULL
)
if
(
given
)
{
memcpy
(
opts
,
given
,
sizeof
(
*
opts
));
else
{
const
char
*
val
=
NULL
;
}
else
{
GIT_INIT_STRUCTURE
(
opts
,
GIT_DIFF_FIND_OPTIONS_VERSION
);
}
opts
->
flags
=
GIT_DIFF_FIND_RENAMES
;
if
(
!
given
||
(
given
->
flags
&
GIT_DIFF_FIND_ALL
)
==
GIT_DIFF_FIND_BY_CONFIG
)
{
const
char
*
val
=
NULL
;
if
(
git_config_get_string
(
&
val
,
cfg
,
"diff.renames"
)
<
0
)
giterr_clear
();
else
if
(
val
&&
(
!
strcasecmp
(
val
,
"copies"
)
||
!
strcasecmp
(
val
,
"copy"
)))
opts
->
flags
=
GIT_DIFF_FIND_RENAMES
|
GIT_DIFF_FIND_COPIES
;
else
if
(
val
)
{
int
boolval
;
if
(
!
git__parse_bool
(
&
boolval
,
val
)
&&
!
boolval
)
{
/* do nothing */
}
else
if
(
!
strcasecmp
(
val
,
"copies"
)
||
!
strcasecmp
(
val
,
"copy"
))
opts
->
flags
|=
(
GIT_DIFF_FIND_RENAMES
|
GIT_DIFF_FIND_COPIES
);
else
opts
->
flags
|=
GIT_DIFF_FIND_RENAMES
;
}
}
GITERR_CHECK_VERSION
(
opts
,
GIT_DIFF_FIND_OPTIONS_VERSION
,
"git_diff_find_options"
);
/* some flags imply others */
if
(
opts
->
flags
&
GIT_DIFF_FIND_EXACT_MATCH_ONLY
)
{
...
...
@@ -830,6 +838,10 @@ int git_diff_find_similar(
if
((
error
=
normalize_find_opts
(
diff
,
&
opts
,
given_opts
))
<
0
)
return
error
;
/* No flags set; nothing to do */
if
((
opts
.
flags
&
GIT_DIFF_FIND_ALL
)
==
0
)
return
0
;
num_deltas
=
diff
->
deltas
.
length
;
/* TODO: maybe abort if deltas.length > rename_limit ??? */
...
...
tests/diff/rename.c
View file @
0eedacb0
...
...
@@ -919,6 +919,7 @@ void test_diff_rename__rejected_match_can_match_others(void)
char
*
ptr
;
opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
findopts
.
flags
=
GIT_DIFF_FIND_RENAMES
;
cl_git_pass
(
git_reference_lookup
(
&
head
,
g_repo
,
"HEAD"
));
cl_git_pass
(
git_reference_symbolic_set_target
(
...
...
@@ -1003,6 +1004,7 @@ void test_diff_rename__rejected_match_can_match_others_two(void)
struct
rename_expected
expect
=
{
2
,
status
,
sources
,
targets
};
opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
findopts
.
flags
=
GIT_DIFF_FIND_RENAMES
;
cl_git_pass
(
git_reference_lookup
(
&
head
,
g_repo
,
"HEAD"
));
cl_git_pass
(
git_reference_symbolic_set_target
(
...
...
@@ -1060,6 +1062,7 @@ void test_diff_rename__rejected_match_can_match_others_three(void)
struct
rename_expected
expect
=
{
2
,
status
,
sources
,
targets
};
opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
findopts
.
flags
=
GIT_DIFF_FIND_RENAMES
;
cl_git_pass
(
git_reference_lookup
(
&
head
,
g_repo
,
"HEAD"
));
cl_git_pass
(
git_reference_symbolic_set_target
(
...
...
@@ -1381,3 +1384,183 @@ void test_diff_rename__can_delete_unmodified_deltas(void)
git_buf_free
(
&
c1
);
}
void
test_diff_rename__matches_config_behavior
(
void
)
{
const
char
*
sha0
=
"31e47d8c1fa36d7f8d537b96158e3f024de0a9f2"
;
const
char
*
sha1
=
"2bc7f351d20b53f1c72c16c4b036e491c478c49a"
;
const
char
*
sha2
=
"1c068dee5790ef1580cfc4cd670915b48d790084"
;
git_tree
*
tree0
,
*
tree1
,
*
tree2
;
git_config
*
cfg
;
git_diff
*
diff
;
git_diff_options
diffopts
=
GIT_DIFF_OPTIONS_INIT
;
git_diff_find_options
opts
=
GIT_DIFF_FIND_OPTIONS_INIT
;
diff_expects
exp
;
opts
.
flags
=
GIT_DIFF_FIND_BY_CONFIG
;
tree0
=
resolve_commit_oid_to_tree
(
g_repo
,
sha0
);
tree1
=
resolve_commit_oid_to_tree
(
g_repo
,
sha1
);
tree2
=
resolve_commit_oid_to_tree
(
g_repo
,
sha2
);
diffopts
.
flags
|=
GIT_DIFF_INCLUDE_UNMODIFIED
;
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
/* diff.renames = false; no rename detection should happen */
cl_git_pass
(
git_config_set_bool
(
cfg
,
"diff.renames"
,
false
));
cl_git_pass
(
git_diff_tree_to_tree
(
&
diff
,
g_repo
,
tree0
,
tree1
,
&
diffopts
));
memset
(
&
exp
,
0
,
sizeof
(
exp
));
cl_git_pass
(
git_diff_find_similar
(
diff
,
&
opts
));
cl_git_pass
(
git_diff_foreach
(
diff
,
diff_file_cb
,
diff_hunk_cb
,
diff_line_cb
,
&
exp
));
cl_assert_equal_i
(
4
,
exp
.
files
);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_UNMODIFIED
]);
cl_assert_equal_i
(
2
,
exp
.
file_status
[
GIT_DELTA_ADDED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_DELETED
]);
git_diff_free
(
diff
);
/* diff.renames = true; should act like -M */
cl_git_pass
(
git_config_set_bool
(
cfg
,
"diff.renames"
,
true
));
cl_git_pass
(
git_diff_tree_to_tree
(
&
diff
,
g_repo
,
tree0
,
tree1
,
&
diffopts
));
memset
(
&
exp
,
0
,
sizeof
(
exp
));
cl_git_pass
(
git_diff_find_similar
(
diff
,
&
opts
));
cl_git_pass
(
git_diff_foreach
(
diff
,
diff_file_cb
,
diff_hunk_cb
,
diff_line_cb
,
&
exp
));
cl_assert_equal_i
(
3
,
exp
.
files
);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_UNMODIFIED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_ADDED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_RENAMED
]);
git_diff_free
(
diff
);
/* diff.renames = copies; should act like -M -C */
cl_git_pass
(
git_config_set_string
(
cfg
,
"diff.renames"
,
"copies"
));
cl_git_pass
(
git_diff_tree_to_tree
(
&
diff
,
g_repo
,
tree1
,
tree2
,
&
diffopts
));
memset
(
&
exp
,
0
,
sizeof
(
exp
));
cl_git_pass
(
git_diff_find_similar
(
diff
,
&
opts
));
cl_git_pass
(
git_diff_foreach
(
diff
,
diff_file_cb
,
diff_hunk_cb
,
diff_line_cb
,
&
exp
));
cl_assert_equal_i
(
4
,
exp
.
files
);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_UNMODIFIED
]);
cl_assert_equal_i
(
2
,
exp
.
file_status
[
GIT_DELTA_MODIFIED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_COPIED
]);
git_diff_free
(
diff
);
/* NULL find options is the same as GIT_DIFF_FIND_BY_CONFIG */
cl_git_pass
(
git_diff_tree_to_tree
(
&
diff
,
g_repo
,
tree1
,
tree2
,
&
diffopts
));
memset
(
&
exp
,
0
,
sizeof
(
exp
));
cl_git_pass
(
git_diff_find_similar
(
diff
,
NULL
));
cl_git_pass
(
git_diff_foreach
(
diff
,
diff_file_cb
,
diff_hunk_cb
,
diff_line_cb
,
&
exp
));
cl_assert_equal_i
(
4
,
exp
.
files
);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_UNMODIFIED
]);
cl_assert_equal_i
(
2
,
exp
.
file_status
[
GIT_DELTA_MODIFIED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_COPIED
]);
git_diff_free
(
diff
);
/* Cleanup */
git_tree_free
(
tree0
);
git_tree_free
(
tree1
);
git_tree_free
(
tree2
);
git_config_free
(
cfg
);
}
void
test_diff_rename__can_override_thresholds_when_obeying_config
(
void
)
{
const
char
*
sha1
=
"2bc7f351d20b53f1c72c16c4b036e491c478c49a"
;
const
char
*
sha2
=
"1c068dee5790ef1580cfc4cd670915b48d790084"
;
git_tree
*
tree1
,
*
tree2
;
git_config
*
cfg
;
git_diff
*
diff
;
git_diff_options
diffopts
=
GIT_DIFF_OPTIONS_INIT
;
git_diff_find_options
opts
=
GIT_DIFF_FIND_OPTIONS_INIT
;
diff_expects
exp
;
tree1
=
resolve_commit_oid_to_tree
(
g_repo
,
sha1
);
tree2
=
resolve_commit_oid_to_tree
(
g_repo
,
sha2
);
diffopts
.
flags
|=
GIT_DIFF_INCLUDE_UNMODIFIED
;
opts
.
flags
=
GIT_DIFF_FIND_BY_CONFIG
;
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_set_string
(
cfg
,
"diff.renames"
,
"copies"
));
git_config_free
(
cfg
);
/* copy threshold = 96%, should see creation of ikeepsix.txt */
opts
.
copy_threshold
=
96
;
cl_git_pass
(
git_diff_tree_to_tree
(
&
diff
,
g_repo
,
tree1
,
tree2
,
&
diffopts
));
memset
(
&
exp
,
0
,
sizeof
(
exp
));
cl_git_pass
(
git_diff_find_similar
(
diff
,
&
opts
));
cl_git_pass
(
git_diff_foreach
(
diff
,
diff_file_cb
,
diff_hunk_cb
,
diff_line_cb
,
&
exp
));
cl_assert_equal_i
(
4
,
exp
.
files
);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_UNMODIFIED
]);
cl_assert_equal_i
(
2
,
exp
.
file_status
[
GIT_DELTA_MODIFIED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_ADDED
]);
git_diff_free
(
diff
);
/* copy threshold = 20%, should see sixserving.txt => ikeepsix.txt */
opts
.
copy_threshold
=
20
;
cl_git_pass
(
git_diff_tree_to_tree
(
&
diff
,
g_repo
,
tree1
,
tree2
,
&
diffopts
));
memset
(
&
exp
,
0
,
sizeof
(
exp
));
cl_git_pass
(
git_diff_find_similar
(
diff
,
&
opts
));
cl_git_pass
(
git_diff_foreach
(
diff
,
diff_file_cb
,
diff_hunk_cb
,
diff_line_cb
,
&
exp
));
cl_assert_equal_i
(
4
,
exp
.
files
);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_UNMODIFIED
]);
cl_assert_equal_i
(
2
,
exp
.
file_status
[
GIT_DELTA_MODIFIED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_COPIED
]);
git_diff_free
(
diff
);
/* Cleanup */
git_tree_free
(
tree1
);
git_tree_free
(
tree2
);
}
void
test_diff_rename__by_config_doesnt_mess_with_whitespace_settings
(
void
)
{
const
char
*
sha1
=
"1c068dee5790ef1580cfc4cd670915b48d790084"
;
const
char
*
sha2
=
"19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"
;
git_tree
*
tree1
,
*
tree2
;
git_config
*
cfg
;
git_diff
*
diff
;
git_diff_options
diffopts
=
GIT_DIFF_OPTIONS_INIT
;
git_diff_find_options
opts
=
GIT_DIFF_FIND_OPTIONS_INIT
;
diff_expects
exp
;
tree1
=
resolve_commit_oid_to_tree
(
g_repo
,
sha1
);
tree2
=
resolve_commit_oid_to_tree
(
g_repo
,
sha2
);
diffopts
.
flags
|=
GIT_DIFF_INCLUDE_UNMODIFIED
;
opts
.
flags
=
GIT_DIFF_FIND_BY_CONFIG
;
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_set_string
(
cfg
,
"diff.renames"
,
"copies"
));
git_config_free
(
cfg
);
/* Don't ignore whitespace; this should find a change in sixserving.txt */
opts
.
flags
|=
0
|
GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE
;
cl_git_pass
(
git_diff_tree_to_tree
(
&
diff
,
g_repo
,
tree1
,
tree2
,
&
diffopts
));
memset
(
&
exp
,
0
,
sizeof
(
exp
));
cl_git_pass
(
git_diff_find_similar
(
diff
,
&
opts
));
cl_git_pass
(
git_diff_foreach
(
diff
,
diff_file_cb
,
diff_hunk_cb
,
diff_line_cb
,
&
exp
));
cl_assert_equal_i
(
5
,
exp
.
files
);
cl_assert_equal_i
(
2
,
exp
.
file_status
[
GIT_DELTA_MODIFIED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_RENAMED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_DELETED
]);
cl_assert_equal_i
(
1
,
exp
.
file_status
[
GIT_DELTA_ADDED
]);
git_diff_free
(
diff
);
/* Cleanup */
git_tree_free
(
tree1
);
git_tree_free
(
tree2
);
}
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