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
2ad51b81
Commit
2ad51b81
authored
Apr 25, 2014
by
Vicent Marti
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2241 from libgit2/rb/stash-skip-submodules
Improve stash and checkout for ignored + untracked items
parents
af9eeac9
a409acef
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
206 additions
and
102 deletions
+206
-102
src/checkout.c
+63
-21
src/diff.c
+8
-70
src/iterator.c
+74
-0
src/iterator.h
+19
-0
src/stash.c
+2
-1
tests/stash/save.c
+35
-1
tests/stash/stash_helpers.c
+5
-9
No files found.
src/checkout.c
View file @
2ad51b81
...
...
@@ -56,6 +56,7 @@ typedef struct {
git_vector
conflicts
;
git_buf
path
;
size_t
workdir_len
;
git_buf
tmp
;
unsigned
int
strategy
;
int
can_symlink
;
bool
reload_submodules
;
...
...
@@ -259,21 +260,41 @@ static int checkout_action_no_wd(
return
checkout_action_common
(
action
,
data
,
delta
,
NULL
);
}
static
bool
wd_item_is_removable
(
git_iterator
*
iter
,
const
git_index_entry
*
wd
)
{
git_buf
*
full
=
NULL
;
if
(
wd
->
mode
!=
GIT_FILEMODE_TREE
)
return
true
;
if
(
git_iterator_current_workdir_path
(
&
full
,
iter
)
<
0
)
return
true
;
return
!
full
||
!
git_path_contains
(
full
,
DOT_GIT
);
}
static
int
checkout_queue_remove
(
checkout_data
*
data
,
const
char
*
path
)
{
char
*
copy
=
git_pool_strdup
(
&
data
->
pool
,
path
);
GITERR_CHECK_ALLOC
(
copy
);
return
git_vector_insert
(
&
data
->
removes
,
copy
);
}
/* note that this advances the iterator over the wd item */
static
int
checkout_action_wd_only
(
checkout_data
*
data
,
git_iterator
*
workdir
,
const
git_index_entry
*
wd
,
const
git_index_entry
*
*
wditem
,
git_vector
*
pathspec
)
{
int
error
=
0
;
bool
remove
=
false
;
git_checkout_notify_t
notify
=
GIT_CHECKOUT_NOTIFY_NONE
;
const
git_index_entry
*
wd
=
*
wditem
;
if
(
!
git_pathspec__match
(
pathspec
,
wd
->
path
,
(
data
->
strategy
&
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH
)
!=
0
,
git_iterator_ignore_case
(
workdir
),
NULL
,
NULL
))
return
0
;
return
git_iterator_advance
(
wditem
,
workdir
)
;
/* check if item is tracked in the index but not in the checkout diff */
if
(
data
->
index
!=
NULL
)
{
...
...
@@ -303,24 +324,49 @@ static int checkout_action_wd_only(
}
}
if
(
notify
!=
GIT_CHECKOUT_NOTIFY_NONE
)
/* found in index */
;
else
if
(
git_iterator_current_is_ignored
(
workdir
))
{
if
(
notify
!=
GIT_CHECKOUT_NOTIFY_NONE
)
{
/* if we found something in the index, notify and advance */
if
((
error
=
checkout_notify
(
data
,
notify
,
NULL
,
wd
))
!=
0
)
return
error
;
if
(
remove
&&
wd_item_is_removable
(
workdir
,
wd
))
error
=
checkout_queue_remove
(
data
,
wd
->
path
);
if
(
!
error
)
error
=
git_iterator_advance
(
wditem
,
workdir
);
}
else
{
/* untracked or ignored - can't know which until we advance through */
bool
over
=
false
,
removable
=
wd_item_is_removable
(
workdir
,
wd
);
git_iterator_status_t
untracked_state
;
/* copy the entry for issuing notification callback later */
git_index_entry
saved_wd
=
*
wd
;
git_buf_sets
(
&
data
->
tmp
,
wd
->
path
);
saved_wd
.
path
=
data
->
tmp
.
ptr
;
error
=
git_iterator_advance_over_with_status
(
wditem
,
&
untracked_state
,
workdir
);
if
(
error
==
GIT_ITEROVER
)
over
=
true
;
else
if
(
error
<
0
)
return
error
;
if
(
untracked_state
==
GIT_ITERATOR_STATUS_IGNORED
)
{
notify
=
GIT_CHECKOUT_NOTIFY_IGNORED
;
remove
=
((
data
->
strategy
&
GIT_CHECKOUT_REMOVE_IGNORED
)
!=
0
);
}
else
{
}
else
{
notify
=
GIT_CHECKOUT_NOTIFY_UNTRACKED
;
remove
=
((
data
->
strategy
&
GIT_CHECKOUT_REMOVE_UNTRACKED
)
!=
0
);
}
error
=
checkout_notify
(
data
,
notify
,
NULL
,
wd
);
if
((
error
=
checkout_notify
(
data
,
notify
,
NULL
,
&
saved_wd
))
!=
0
)
return
error
;
if
(
!
error
&&
remove
)
{
char
*
path
=
git_pool_strdup
(
&
data
->
pool
,
wd
->
path
);
GITERR_CHECK_ALLOC
(
path
);
if
(
remove
&&
removable
)
error
=
checkout_queue_remove
(
data
,
saved_wd
.
path
);
error
=
git_vector_insert
(
&
data
->
removes
,
path
);
if
(
!
error
&&
over
)
/* restore ITEROVER if needed */
error
=
GIT_ITEROVER
;
}
return
error
;
...
...
@@ -554,11 +600,8 @@ static int checkout_action(
}
/* case 1 - handle wd item (if it matches pathspec) */
error
=
checkout_action_wd_only
(
data
,
workdir
,
wd
,
pathspec
);
if
(
error
)
goto
done
;
if
((
error
=
git_iterator_advance
(
wditem
,
workdir
))
<
0
&&
error
!=
GIT_ITEROVER
)
error
=
checkout_action_wd_only
(
data
,
workdir
,
wditem
,
pathspec
);
if
(
error
&&
error
!=
GIT_ITEROVER
)
goto
done
;
continue
;
}
...
...
@@ -619,10 +662,8 @@ static int checkout_remaining_wd_items(
{
int
error
=
0
;
while
(
wd
&&
!
error
)
{
if
(
!
(
error
=
checkout_action_wd_only
(
data
,
workdir
,
wd
,
spec
)))
error
=
git_iterator_advance
(
&
wd
,
workdir
);
}
while
(
wd
&&
!
error
)
error
=
checkout_action_wd_only
(
data
,
workdir
,
&
wd
,
spec
);
if
(
error
==
GIT_ITEROVER
)
error
=
0
;
...
...
@@ -1853,6 +1894,7 @@ static void checkout_data_clear(checkout_data *data)
data
->
pfx
=
NULL
;
git_buf_free
(
&
data
->
path
);
git_buf_free
(
&
data
->
tmp
);
git_index_free
(
data
->
index
);
data
->
index
=
NULL
;
...
...
src/diff.c
View file @
2ad51b81
...
...
@@ -784,72 +784,6 @@ static bool entry_is_prefixed(
item
->
path
[
pathlen
]
==
'/'
);
}
static
int
diff_scan_inside_untracked_dir
(
git_diff
*
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
);
}
goto
done
;
}
/* look for actual untracked file */
while
(
info
->
nitem
!=
NULL
&&
!
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
))
{
error
=
git_iterator_advance_into
(
&
info
->
nitem
,
info
->
new_iter
);
if
(
!
error
)
continue
;
else
if
(
error
==
GIT_ENOTFOUND
)
{
error
=
0
;
is_ignored
=
true
;
/* treat empty as ignored */
}
else
break
;
/* real error, must stop */
}
/* 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
(
info
->
nitem
!=
NULL
&&
!
diff
->
pfxcomp
(
info
->
nitem
->
path
,
git_buf_cstr
(
&
base
)))
{
if
((
error
=
git_iterator_advance
(
&
info
->
nitem
,
info
->
new_iter
))
<
0
)
break
;
}
done:
git_buf_free
(
&
base
);
if
(
error
==
GIT_ITEROVER
)
error
=
0
;
return
error
;
}
static
int
handle_unmatched_new_item
(
git_diff
*
diff
,
diff_in_progress
*
info
)
{
...
...
@@ -905,6 +839,7 @@ static int handle_unmatched_new_item(
DIFF_FLAG_ISNT_SET
(
diff
,
GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS
))
{
git_diff_delta
*
last
;
git_iterator_status_t
untracked_state
;
/* attempt to insert record for this directory */
if
((
error
=
diff_delta__from_one
(
diff
,
delta_type
,
nitem
))
!=
0
)
...
...
@@ -916,11 +851,14 @@ static int handle_unmatched_new_item(
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
;
if
((
error
=
git_iterator_advance_over_with_status
(
&
info
->
nitem
,
&
untracked_state
,
info
->
new_iter
))
<
0
&&
error
!=
GIT_ITEROVER
)
return
error
;
/* it iteration changed delta type, the update the record */
if
(
delta_type
==
GIT_DELTA_IGNORED
)
{
/* if we found nothing or just ignored items, update the record */
if
(
untracked_state
==
GIT_ITERATOR_STATUS_IGNORED
||
untracked_state
==
GIT_ITERATOR_STATUS_EMPTY
)
{
last
->
status
=
GIT_DELTA_IGNORED
;
/* remove the record if we don't want ignored records */
...
...
src/iterator.c
View file @
2ad51b81
...
...
@@ -1528,3 +1528,77 @@ int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
return
0
;
}
int
git_iterator_advance_over_with_status
(
const
git_index_entry
**
entryptr
,
git_iterator_status_t
*
status
,
git_iterator
*
iter
)
{
int
error
=
0
;
workdir_iterator
*
wi
=
(
workdir_iterator
*
)
iter
;
char
*
base
=
NULL
;
const
git_index_entry
*
entry
;
*
status
=
GIT_ITERATOR_STATUS_NORMAL
;
if
(
iter
->
type
!=
GIT_ITERATOR_TYPE_WORKDIR
)
return
git_iterator_advance
(
entryptr
,
iter
);
if
((
error
=
git_iterator_current
(
&
entry
,
iter
))
<
0
)
return
error
;
if
(
!
S_ISDIR
(
entry
->
mode
))
{
if
(
git_ignore__lookup
(
&
wi
->
ignores
,
wi
->
fi
.
entry
.
path
,
&
wi
->
is_ignored
)
<
0
)
wi
->
is_ignored
=
true
;
if
(
wi
->
is_ignored
)
*
status
=
GIT_ITERATOR_STATUS_IGNORED
;
return
git_iterator_advance
(
entryptr
,
iter
);
}
*
status
=
GIT_ITERATOR_STATUS_EMPTY
;
base
=
git__strdup
(
entry
->
path
);
GITERR_CHECK_ALLOC
(
base
);
/* scan inside directory looking for a non-ignored item */
while
(
entry
&&
!
iter
->
prefixcomp
(
entry
->
path
,
base
))
{
if
(
git_ignore__lookup
(
&
wi
->
ignores
,
wi
->
fi
.
entry
.
path
,
&
wi
->
is_ignored
)
<
0
)
wi
->
is_ignored
=
true
;
/* if we found an explicitly ignored item, then update from
* EMPTY to IGNORED
*/
if
(
wi
->
is_ignored
)
*
status
=
GIT_ITERATOR_STATUS_IGNORED
;
else
if
(
S_ISDIR
(
entry
->
mode
))
{
error
=
git_iterator_advance_into
(
&
entry
,
iter
);
if
(
!
error
)
continue
;
else
if
(
error
==
GIT_ENOTFOUND
)
{
error
=
0
;
wi
->
is_ignored
=
true
;
/* mark empty directories as ignored */
}
else
break
;
/* real error, stop here */
}
else
{
/* we found a non-ignored item, treat parent as untracked */
*
status
=
GIT_ITERATOR_STATUS_NORMAL
;
break
;
}
if
((
error
=
git_iterator_advance
(
&
entry
,
iter
))
<
0
)
break
;
}
/* wrap up scan back to base directory */
while
(
entry
&&
!
iter
->
prefixcomp
(
entry
->
path
,
base
))
if
((
error
=
git_iterator_advance
(
&
entry
,
iter
))
<
0
)
break
;
*
entryptr
=
entry
;
git__free
(
base
);
return
error
;
}
src/iterator.h
View file @
2ad51b81
...
...
@@ -258,4 +258,23 @@ extern int git_iterator_current_workdir_path(
/* Return index pointer if index iterator, else NULL */
extern
git_index
*
git_iterator_get_index
(
git_iterator
*
iter
);
typedef
enum
{
GIT_ITERATOR_STATUS_NORMAL
=
0
,
GIT_ITERATOR_STATUS_IGNORED
=
1
,
GIT_ITERATOR_STATUS_EMPTY
=
2
}
git_iterator_status_t
;
/* Advance over a directory and check if it contains no files or just
* ignored files.
*
* In a tree or the index, all directories will contain files, but in the
* working directory it is possible to have an empty directory tree or a
* tree that only contains ignored files. Many Git operations treat these
* cases specially. This advances over a directory (presumably an
* untracked directory) but checks during the scan if there are any files
* and any non-ignored files.
*/
extern
int
git_iterator_advance_over_with_status
(
const
git_index_entry
**
entry
,
git_iterator_status_t
*
status
,
git_iterator
*
iter
);
#endif
src/stash.c
View file @
2ad51b81
...
...
@@ -178,7 +178,8 @@ static int stash_update_index_from_diff(
break
;
case
GIT_DELTA_UNTRACKED
:
if
(
data
->
include_untracked
)
if
(
data
->
include_untracked
&&
delta
->
new_file
.
mode
!=
GIT_FILEMODE_TREE
)
add_path
=
delta
->
new_file
.
path
;
break
;
...
...
tests/stash/save.c
View file @
2ad51b81
...
...
@@ -148,6 +148,25 @@ void test_stash_save__can_include_untracked_files(void)
assert_blob_oid
(
"refs/stash^3:just.ignore"
,
NULL
);
}
void
test_stash_save__untracked_skips_ignored
(
void
)
{
cl_git_append2file
(
"stash/.gitignore"
,
"bundle/vendor/
\n
"
);
cl_must_pass
(
p_mkdir
(
"stash/bundle"
,
0777
));
cl_must_pass
(
p_mkdir
(
"stash/bundle/vendor"
,
0777
));
cl_git_mkfile
(
"stash/bundle/vendor/blah"
,
"contents
\n
"
);
cl_assert
(
git_path_exists
(
"stash/when"
));
/* untracked */
cl_assert
(
git_path_exists
(
"stash/just.ignore"
));
/* ignored */
cl_assert
(
git_path_exists
(
"stash/bundle/vendor/blah"
));
/* ignored */
cl_git_pass
(
git_stash_save
(
&
stash_tip_oid
,
repo
,
signature
,
NULL
,
GIT_STASH_INCLUDE_UNTRACKED
));
cl_assert
(
!
git_path_exists
(
"stash/when"
));
cl_assert
(
git_path_exists
(
"stash/bundle/vendor/blah"
));
cl_assert
(
git_path_exists
(
"stash/just.ignore"
));
}
void
test_stash_save__can_include_untracked_and_ignored_files
(
void
)
{
cl_git_pass
(
git_stash_save
(
&
stash_tip_oid
,
repo
,
signature
,
NULL
,
GIT_STASH_INCLUDE_UNTRACKED
|
GIT_STASH_INCLUDE_IGNORED
));
...
...
@@ -342,7 +361,7 @@ void test_stash_save__can_stage_normal_then_stage_untracked(void)
void
test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree
(
void
)
{
cl_
gi
t_pass
(
p_unlink
(
"stash/when"
));
cl_
mus
t_pass
(
p_unlink
(
"stash/when"
));
assert_status
(
repo
,
"what"
,
GIT_STATUS_WT_MODIFIED
|
GIT_STATUS_INDEX_MODIFIED
);
assert_status
(
repo
,
"how"
,
GIT_STATUS_INDEX_MODIFIED
);
...
...
@@ -354,3 +373,18 @@ void test_stash_save__including_untracked_without_any_untracked_file_creates_an_
assert_object_oid
(
"stash^3^{tree}"
,
EMPTY_TREE
,
GIT_OBJ_TREE
);
}
void
test_stash_save__skip_submodules
(
void
)
{
git_repository
*
untracked_repo
;
cl_git_pass
(
git_repository_init
(
&
untracked_repo
,
"stash/untracked_repo"
,
false
));
cl_git_mkfile
(
"stash/untracked_repo/content"
,
"stuff"
);
git_repository_free
(
untracked_repo
);
assert_status
(
repo
,
"untracked_repo/"
,
GIT_STATUS_WT_NEW
);
cl_git_pass
(
git_stash_save
(
&
stash_tip_oid
,
repo
,
signature
,
NULL
,
GIT_STASH_INCLUDE_UNTRACKED
));
assert_status
(
repo
,
"untracked_repo/"
,
GIT_STATUS_WT_NEW
);
}
tests/stash/stash_helpers.c
View file @
2ad51b81
...
...
@@ -42,15 +42,11 @@ void assert_status(
int
status_flags
)
{
unsigned
int
status
;
int
error
;
error
=
git_status_file
(
&
status
,
repo
,
path
);
if
(
status_flags
<
0
)
{
cl_assert_equal_i
(
status_flags
,
error
);
return
;
}
cl_assert_equal_i
(
0
,
error
);
if
(
status_flags
<
0
)
cl_assert_equal_i
(
status_flags
,
git_status_file
(
&
status
,
repo
,
path
));
else
{
cl_git_pass
(
git_status_file
(
&
status
,
repo
,
path
));
cl_assert_equal_i
((
unsigned
int
)
status_flags
,
status
);
}
}
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