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
c3414d53
Commit
c3414d53
authored
May 04, 2015
by
Carlos Martín Nieto
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3070 from ethomson/checkout_icase
Case insensitive checkout improvements
parents
cfc2e56d
cd79d99a
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
253 additions
and
25 deletions
+253
-25
include/git2/diff.h
+6
-1
src/checkout.c
+29
-5
src/diff.c
+13
-0
tests/checkout/icase.c
+180
-9
tests/checkout/tree.c
+13
-2
tests/status/renames.c
+12
-8
No files found.
include/git2/diff.h
View file @
c3414d53
...
...
@@ -124,6 +124,11 @@ typedef enum {
/** Use case insensitive filename comparisons */
GIT_DIFF_IGNORE_CASE
=
(
1u
<<
10
),
/** May be combined with `GIT_DIFF_IGNORE_CASE` to specify that a file
* that has changed case will be returned as an add/delete pair.
*/
GIT_DIFF_INCLUDE_CASECHANGE
=
(
1u
<<
11
),
/** 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.
*/
...
...
@@ -220,7 +225,7 @@ typedef struct git_diff git_diff;
typedef
enum
{
GIT_DIFF_FLAG_BINARY
=
(
1u
<<
0
),
/**< file(s) treated as binary data */
GIT_DIFF_FLAG_NOT_BINARY
=
(
1u
<<
1
),
/**< file(s) treated as text data */
GIT_DIFF_FLAG_VALID_ID
=
(
1u
<<
2
),
/**< `id` value is known correct */
GIT_DIFF_FLAG_VALID_ID
=
(
1u
<<
2
),
/**< `id` value is known correct */
}
git_diff_flag_t
;
/**
...
...
src/checkout.c
View file @
c3414d53
...
...
@@ -409,6 +409,14 @@ static bool submodule_is_config_only(
return
rval
;
}
static
bool
checkout_is_empty_dir
(
checkout_data
*
data
,
const
char
*
path
)
{
git_buf_truncate
(
&
data
->
path
,
data
->
workdir_len
);
if
(
git_buf_puts
(
&
data
->
path
,
path
)
<
0
)
return
false
;
return
git_path_is_empty_dir
(
data
->
path
.
ptr
);
}
static
int
checkout_action_with_wd
(
int
*
action
,
checkout_data
*
data
,
...
...
@@ -526,6 +534,7 @@ static int checkout_action_with_wd_dir(
checkout_notify
(
data
,
GIT_CHECKOUT_NOTIFY_DIRTY
,
delta
,
NULL
));
GITERR_CHECK_ERROR
(
checkout_notify
(
data
,
GIT_CHECKOUT_NOTIFY_UNTRACKED
,
NULL
,
wd
));
*
action
=
CHECKOUT_ACTION_IF
(
FORCE
,
REMOVE_AND_UPDATE
,
NONE
);
break
;
case
GIT_DELTA_ADDED
:
/* case 4 (and 7 for dir) */
case
GIT_DELTA_MODIFIED
:
/* case 20 (or 37 but not really) */
...
...
@@ -550,8 +559,6 @@ static int checkout_action_with_wd_dir(
* dir and it will succeed if no children are left.
*/
*
action
=
CHECKOUT_ACTION_IF
(
SAFE
,
UPDATE_BLOB
,
NONE
);
if
(
*
action
!=
CHECKOUT_ACTION__NONE
)
*
action
|=
CHECKOUT_ACTION__DEFER_REMOVE
;
}
else
if
(
delta
->
new_file
.
mode
!=
GIT_FILEMODE_TREE
)
/* For typechange to dir, dir is already created so no action */
...
...
@@ -564,6 +571,20 @@ static int checkout_action_with_wd_dir(
return
checkout_action_common
(
action
,
data
,
delta
,
wd
);
}
static
int
checkout_action_with_wd_dir_empty
(
int
*
action
,
checkout_data
*
data
,
const
git_diff_delta
*
delta
)
{
int
error
=
checkout_action_no_wd
(
action
,
data
,
delta
);
/* We can always safely remove an empty directory. */
if
(
error
==
0
&&
*
action
!=
CHECKOUT_ACTION__NONE
)
*
action
|=
CHECKOUT_ACTION__REMOVE
;
return
error
;
}
static
int
checkout_action
(
int
*
action
,
checkout_data
*
data
,
...
...
@@ -653,7 +674,9 @@ static int checkout_action(
}
}
return
checkout_action_with_wd_dir
(
action
,
data
,
delta
,
workdir
,
wd
);
return
checkout_is_empty_dir
(
data
,
wd
->
path
)
?
checkout_action_with_wd_dir_empty
(
action
,
data
,
delta
)
:
checkout_action_with_wd_dir
(
action
,
data
,
delta
,
workdir
,
wd
);
}
/* case 6 - wd is after delta */
...
...
@@ -2462,7 +2485,8 @@ int git_checkout_iterator(
GIT_DIFF_INCLUDE_IGNORED
|
GIT_DIFF_INCLUDE_TYPECHANGE
|
GIT_DIFF_INCLUDE_TYPECHANGE_TREES
|
GIT_DIFF_SKIP_BINARY_CHECK
;
GIT_DIFF_SKIP_BINARY_CHECK
|
GIT_DIFF_INCLUDE_CASECHANGE
;
if
(
data
.
opts
.
checkout_strategy
&
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH
)
diff_opts
.
flags
|=
GIT_DIFF_DISABLE_PATHSPEC_MATCH
;
if
(
data
.
opts
.
paths
.
count
>
0
)
...
...
@@ -2643,7 +2667,7 @@ int git_checkout_tree(
if
((
error
=
git_repository_index
(
&
index
,
repo
))
<
0
)
return
error
;
if
(
!
(
error
=
git_iterator_for_tree
(
&
tree_i
,
tree
,
GIT_ITERATOR_DONT_IGNORE_CASE
,
NULL
,
NULL
)))
if
(
!
(
error
=
git_iterator_for_tree
(
&
tree_i
,
tree
,
0
,
NULL
,
NULL
)))
error
=
git_checkout_iterator
(
tree_i
,
index
,
opts
);
git_iterator_free
(
tree_i
);
...
...
src/diff.c
View file @
c3414d53
...
...
@@ -822,6 +822,19 @@ static int maybe_modified(
status
=
GIT_DELTA_UNMODIFIED
;
}
/* If we want case changes, then break this into a delete of the old
* and an add of the new so that consumers can act accordingly (eg,
* checkout will update the case on disk.)
*/
if
(
DIFF_FLAG_IS_SET
(
diff
,
GIT_DIFF_IGNORE_CASE
)
&&
DIFF_FLAG_IS_SET
(
diff
,
GIT_DIFF_INCLUDE_CASECHANGE
)
&&
strcmp
(
oitem
->
path
,
nitem
->
path
)
!=
0
)
{
if
(
!
(
error
=
diff_delta__from_one
(
diff
,
GIT_DELTA_DELETED
,
oitem
)))
error
=
diff_delta__from_one
(
diff
,
GIT_DELTA_ADDED
,
nitem
);
return
error
;
}
return
diff_delta__from_two
(
diff
,
status
,
oitem
,
omode
,
nitem
,
nmode
,
git_oid_iszero
(
&
noid
)
?
NULL
:
&
noid
,
matched_pathspec
);
...
...
tests/checkout/icase.c
View file @
c3414d53
#include "clar_libgit2.h"
#include "git2/checkout.h"
#include "refs.h"
#include "path.h"
#ifdef GIT_WIN32
# include <windows.h>
#else
# include <dirent.h>
#endif
static
git_repository
*
repo
;
...
...
@@ -14,14 +17,23 @@ static git_checkout_options checkout_opts;
void
test_checkout_icase__initialize
(
void
)
{
git_oid
id
;
git_config
*
cfg
;
int
icase
=
0
;
repo
=
cl_git_sandbox_init
(
"testrepo"
);
cl_git_pass
(
git_repository_config_snapshot
(
&
cfg
,
repo
));
git_config_get_bool
(
&
icase
,
cfg
,
"core.ignorecase"
);
git_config_free
(
cfg
);
if
(
!
icase
)
cl_skip
();
cl_git_pass
(
git_reference_name_to_id
(
&
id
,
repo
,
"refs/heads/dir"
));
cl_git_pass
(
git_object_lookup
(
&
obj
,
repo
,
&
id
,
GIT_OBJ_ANY
));
git_checkout_init_options
(
&
checkout_opts
,
GIT_CHECKOUT_OPTIONS_VERSION
);
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_
FORC
E
;
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_
NON
E
;
}
void
test_checkout_icase__cleanup
(
void
)
...
...
@@ -30,7 +42,7 @@ void test_checkout_icase__cleanup(void)
cl_git_sandbox_cleanup
();
}
static
char
*
test_realpath
(
const
char
*
in
)
static
char
*
get_filename
(
const
char
*
in
)
{
#ifdef GIT_WIN32
HANDLE
fh
;
...
...
@@ -55,7 +67,31 @@ static char *test_realpath(const char *in)
return
filename
;
#else
return
realpath
(
in
,
NULL
);
char
*
search_dirname
,
*
search_filename
,
*
filename
=
NULL
;
git_buf
out
=
GIT_BUF_INIT
;
DIR
*
dir
;
struct
dirent
*
de
;
cl_assert
(
search_dirname
=
git_path_dirname
(
in
));
cl_assert
(
search_filename
=
git_path_basename
(
in
));
cl_assert
(
dir
=
opendir
(
search_dirname
));
while
((
de
=
readdir
(
dir
)))
{
if
(
strcasecmp
(
de
->
d_name
,
search_filename
)
==
0
)
{
git_buf_join
(
&
out
,
'/'
,
search_dirname
,
de
->
d_name
);
filename
=
git_buf_detach
(
&
out
);
break
;
}
}
closedir
(
dir
);
git__free
(
search_dirname
);
git__free
(
search_filename
);
git_buf_free
(
&
out
);
return
filename
;
#endif
}
...
...
@@ -64,7 +100,7 @@ static void assert_name_is(const char *expected)
char
*
actual
;
size_t
actual_len
,
expected_len
,
start
;
cl_assert
(
actual
=
test_realpath
(
expected
));
cl_assert
(
actual
=
get_filename
(
expected
));
expected_len
=
strlen
(
expected
);
actual_len
=
strlen
(
actual
);
...
...
@@ -79,8 +115,21 @@ static void assert_name_is(const char *expected)
free
(
actual
);
}
void
test_checkout_icase__
overwrites
_files_for_files
(
void
)
void
test_checkout_icase__
refuses_to_overwrite
_files_for_files
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_git_write2file
(
"testrepo/BRANCH_FILE.txt"
,
"neue file
\n
"
,
10
,
\
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
0644
);
cl_git_fail
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
assert_name_is
(
"testrepo/BRANCH_FILE.txt"
);
}
void
test_checkout_icase__overwrites_files_for_files_when_forced
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
cl_git_write2file
(
"testrepo/NEW.txt"
,
"neue file
\n
"
,
10
,
\
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
0644
);
...
...
@@ -88,8 +137,22 @@ void test_checkout_icase__overwrites_files_for_files(void)
assert_name_is
(
"testrepo/new.txt"
);
}
void
test_checkout_icase__
overwrites
_links_for_files
(
void
)
void
test_checkout_icase__
refuses_to_overwrite
_links_for_files
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_must_pass
(
p_symlink
(
"../tmp"
,
"testrepo/BRANCH_FILE.txt"
));
cl_git_fail
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
cl_assert
(
!
git_path_exists
(
"tmp"
));
assert_name_is
(
"testrepo/BRANCH_FILE.txt"
);
}
void
test_checkout_icase__overwrites_links_for_files_when_forced
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
cl_must_pass
(
p_symlink
(
"../tmp"
,
"testrepo/NEW.txt"
));
cl_git_pass
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
...
...
@@ -98,8 +161,10 @@ void test_checkout_icase__overwrites_links_for_files(void)
assert_name_is
(
"testrepo/new.txt"
);
}
void
test_checkout_icase__overw
ites
_folders_for_files
(
void
)
void
test_checkout_icase__overw
rites_empty
_folders_for_files
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_must_pass
(
p_mkdir
(
"testrepo/NEW.txt"
,
0777
));
cl_git_pass
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
...
...
@@ -108,8 +173,50 @@ void test_checkout_icase__overwites_folders_for_files(void)
cl_assert
(
!
git_path_isdir
(
"testrepo/new.txt"
));
}
void
test_checkout_icase__
overwrites_files_for_folder
s
(
void
)
void
test_checkout_icase__
refuses_to_overwrite_populated_folders_for_file
s
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_must_pass
(
p_mkdir
(
"testrepo/BRANCH_FILE.txt"
,
0777
));
cl_git_write2file
(
"testrepo/BRANCH_FILE.txt/foobar"
,
"neue file
\n
"
,
10
,
\
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
0644
);
cl_git_fail
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
assert_name_is
(
"testrepo/BRANCH_FILE.txt"
);
cl_assert
(
git_path_isdir
(
"testrepo/BRANCH_FILE.txt"
));
}
void
test_checkout_icase__overwrites_folders_for_files_when_forced
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
cl_must_pass
(
p_mkdir
(
"testrepo/NEW.txt"
,
0777
));
cl_git_write2file
(
"testrepo/NEW.txt/foobar"
,
"neue file
\n
"
,
10
,
\
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
0644
);
cl_git_pass
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
assert_name_is
(
"testrepo/new.txt"
);
cl_assert
(
!
git_path_isdir
(
"testrepo/new.txt"
));
}
void
test_checkout_icase__refuses_to_overwrite_files_for_folders
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_git_write2file
(
"testrepo/A"
,
"neue file
\n
"
,
10
,
\
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
0644
);
cl_git_fail
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
assert_name_is
(
"testrepo/A"
);
cl_assert
(
!
git_path_isdir
(
"testrepo/A"
));
}
void
test_checkout_icase__overwrites_files_for_folders_when_forced
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
cl_git_write2file
(
"testrepo/A"
,
"neue file
\n
"
,
10
,
\
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
0644
);
...
...
@@ -118,8 +225,22 @@ void test_checkout_icase__overwrites_files_for_folders(void)
cl_assert
(
git_path_isdir
(
"testrepo/a"
));
}
void
test_checkout_icase__overwrites_links_for_folders
(
void
)
void
test_checkout_icase__refuses_to_overwrite_links_for_folders
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_must_pass
(
p_symlink
(
".."
,
"testrepo/A"
));
cl_git_fail
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
cl_assert
(
!
git_path_exists
(
"b.txt"
));
assert_name_is
(
"testrepo/A"
);
}
void
test_checkout_icase__overwrites_links_for_folders_when_forced
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
cl_must_pass
(
p_symlink
(
".."
,
"testrepo/A"
));
cl_git_pass
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
...
...
@@ -128,3 +249,53 @@ void test_checkout_icase__overwrites_links_for_folders(void)
assert_name_is
(
"testrepo/a"
);
}
void
test_checkout_icase__ignores_unstaged_casechange
(
void
)
{
git_reference
*
orig_ref
,
*
br2_ref
;
git_commit
*
orig
,
*
br2
;
git_checkout_options
checkout_opts
=
GIT_CHECKOUT_OPTIONS_INIT
;
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
;
cl_git_pass
(
git_reference_lookup_resolved
(
&
orig_ref
,
repo
,
"HEAD"
,
100
));
cl_git_pass
(
git_commit_lookup
(
&
orig
,
repo
,
git_reference_target
(
orig_ref
)));
cl_git_pass
(
git_reset
(
repo
,
(
git_object
*
)
orig
,
GIT_RESET_HARD
,
NULL
));
cl_rename
(
"testrepo/branch_file.txt"
,
"testrepo/Branch_File.txt"
);
cl_git_pass
(
git_reference_lookup_resolved
(
&
br2_ref
,
repo
,
"refs/heads/br2"
,
100
));
cl_git_pass
(
git_commit_lookup
(
&
br2
,
repo
,
git_reference_target
(
br2_ref
)));
cl_git_pass
(
git_checkout_tree
(
repo
,
(
const
git_object
*
)
br2
,
&
checkout_opts
));
git_commit_free
(
orig
);
git_reference_free
(
orig_ref
);
}
void
test_checkout_icase__conflicts_with_casechanged_subtrees
(
void
)
{
git_reference
*
orig_ref
;
git_object
*
orig
,
*
subtrees
;
git_oid
oid
;
git_checkout_options
checkout_opts
=
GIT_CHECKOUT_OPTIONS_INIT
;
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
;
cl_git_pass
(
git_reference_lookup_resolved
(
&
orig_ref
,
repo
,
"HEAD"
,
100
));
cl_git_pass
(
git_object_lookup
(
&
orig
,
repo
,
git_reference_target
(
orig_ref
),
GIT_OBJ_COMMIT
));
cl_git_pass
(
git_reset
(
repo
,
(
git_object
*
)
orig
,
GIT_RESET_HARD
,
NULL
));
cl_must_pass
(
p_mkdir
(
"testrepo/AB"
,
0777
));
cl_must_pass
(
p_mkdir
(
"testrepo/AB/C"
,
0777
));
cl_git_write2file
(
"testrepo/AB/C/3.txt"
,
"Foobar!
\n
"
,
8
,
O_RDWR
|
O_CREAT
,
0666
);
cl_git_pass
(
git_reference_name_to_id
(
&
oid
,
repo
,
"refs/heads/subtrees"
));
cl_git_pass
(
git_object_lookup
(
&
subtrees
,
repo
,
&
oid
,
GIT_OBJ_ANY
));
cl_git_fail
(
git_checkout_tree
(
repo
,
subtrees
,
&
checkout_opts
));
git_object_free
(
orig
);
git_object_free
(
subtrees
);
git_reference_free
(
orig_ref
);
}
tests/checkout/tree.c
View file @
c3414d53
...
...
@@ -646,7 +646,14 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
cl_git_fail_with
(
git_checkout_tree
(
g_repo
,
obj
,
&
opts
),
-
5555
);
cl_assert
(
!
git_path_exists
(
"testrepo/new.txt"
));
cl_assert_equal_i
(
4
,
ca
.
count
);
/* on case-insensitive FS = a/b.txt, branch_file.txt, new.txt */
/* on case-sensitive FS = README, then above */
if
(
git_path_exists
(
"testrepo/.git/CoNfIg"
))
/* case insensitive */
cl_assert_equal_i
(
3
,
ca
.
count
);
else
cl_assert_equal_i
(
4
,
ca
.
count
);
/* and again with a different stopping point and return code */
ca
.
filename
=
"README"
;
...
...
@@ -656,7 +663,11 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
cl_git_fail_with
(
git_checkout_tree
(
g_repo
,
obj
,
&
opts
),
123
);
cl_assert
(
!
git_path_exists
(
"testrepo/new.txt"
));
cl_assert_equal_i
(
1
,
ca
.
count
);
if
(
git_path_exists
(
"testrepo/.git/CoNfIg"
))
/* case insensitive */
cl_assert_equal_i
(
4
,
ca
.
count
);
else
cl_assert_equal_i
(
1
,
ca
.
count
);
git_object_free
(
obj
);
}
...
...
tests/status/renames.c
View file @
c3414d53
...
...
@@ -73,16 +73,20 @@ static void check_status(
cl_assert_equal_i_fmt
(
expected
->
status
,
actual
->
status
,
"%04x"
);
if
(
oldname
)
cl_assert
(
git__strcmp
(
oldname
,
expected
->
oldname
)
==
0
);
else
cl_assert
(
expected
->
oldname
==
NULL
);
if
(
expected
->
oldname
)
{
cl_assert
(
oldname
!=
NULL
);
cl_assert_equal_s
(
oldname
,
expected
->
oldname
);
}
else
{
cl_assert
(
oldname
==
NULL
);
}
if
(
actual
->
status
&
(
GIT_STATUS_INDEX_RENAMED
|
GIT_STATUS_WT_RENAMED
))
{
if
(
newname
)
cl_assert
(
git__strcmp
(
newname
,
expected
->
newname
)
==
0
);
else
cl_assert
(
expected
->
newname
==
NULL
);
if
(
expected
->
newname
)
{
cl_assert
(
newname
!=
NULL
);
cl_assert_equal_s
(
newname
,
expected
->
newname
);
}
else
{
cl_assert
(
newname
==
NULL
);
}
}
}
}
...
...
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