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
923c8400
Commit
923c8400
authored
Apr 04, 2014
by
Vicent Marti
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2215 from libgit2/rb/submodule-cache-fixes
Improve submodule cache management
parents
f34408a7
eedeeb9e
Hide whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
1203 additions
and
517 deletions
+1203
-517
examples/status.c
+50
-9
src/buffer.c
+53
-0
src/buffer.h
+4
-0
src/config.c
+30
-0
src/config.h
+8
-0
src/config_file.h
+2
-1
src/index.c
+12
-0
src/index.h
+7
-0
src/path.h
+8
-0
src/refdb_fs.c
+1
-1
src/remote.c
+35
-51
src/repository.c
+1
-1
src/repository.h
+2
-7
src/submodule.c
+541
-343
src/submodule.h
+28
-5
src/vector.c
+1
-1
tests/core/buffer.c
+32
-0
tests/diff/submodules.c
+0
-5
tests/submodule/add.c
+117
-0
tests/submodule/lookup.c
+121
-26
tests/submodule/modify.c
+26
-57
tests/submodule/nosubs.c
+82
-1
tests/submodule/submodule_helpers.c
+29
-7
tests/submodule/submodule_helpers.h
+13
-2
No files found.
examples/status.c
View file @
923c8400
...
...
@@ -13,6 +13,7 @@
*/
#include "common.h"
#include <unistd.h>
/**
* This example demonstrates the use of the libgit2 status APIs,
...
...
@@ -44,19 +45,22 @@ enum {
#define MAX_PATHSPEC 8
struct
opts
{
git_status_options
statusopt
;
char
*
repodir
;
char
*
pathspec
[
MAX_PATHSPEC
];
int
npaths
;
int
format
;
int
zterm
;
int
showbranch
;
git_status_options
statusopt
;
char
*
repodir
;
char
*
pathspec
[
MAX_PATHSPEC
];
int
npaths
;
int
format
;
int
zterm
;
int
showbranch
;
int
showsubmod
;
int
repeat
;
};
static
void
parse_opts
(
struct
opts
*
o
,
int
argc
,
char
*
argv
[]);
static
void
show_branch
(
git_repository
*
repo
,
int
format
);
static
void
print_long
(
git_status_list
*
status
);
static
void
print_short
(
git_repository
*
repo
,
git_status_list
*
status
);
static
int
print_submod
(
git_submodule
*
sm
,
const
char
*
name
,
void
*
payload
);
int
main
(
int
argc
,
char
*
argv
[])
{
...
...
@@ -84,6 +88,10 @@ int main(int argc, char *argv[])
fatal
(
"Cannot report status on bare repository"
,
git_repository_path
(
repo
));
show_status:
if
(
o
.
repeat
)
printf
(
"
\033
[H
\033
[2J"
);
/**
* Run status on the repository
*
...
...
@@ -98,17 +106,29 @@ int main(int argc, char *argv[])
* about what results are presented.
*/
check_lg2
(
git_status_list_new
(
&
status
,
repo
,
&
o
.
statusopt
),
"Could not get status"
,
NULL
);
"Could not get status"
,
NULL
);
if
(
o
.
showbranch
)
show_branch
(
repo
,
o
.
format
);
if
(
o
.
showsubmod
)
{
int
submod_count
=
0
;
check_lg2
(
git_submodule_foreach
(
repo
,
print_submod
,
&
submod_count
),
"Cannot iterate submodules"
,
o
.
repodir
);
}
if
(
o
.
format
==
FORMAT_LONG
)
print_long
(
status
);
else
print_short
(
repo
,
status
);
git_status_list_free
(
status
);
if
(
o
.
repeat
)
{
sleep
(
o
.
repeat
);
goto
show_status
;
}
git_repository_free
(
repo
);
git_threads_shutdown
();
...
...
@@ -381,7 +401,7 @@ static void print_short(git_repository *repo, git_status_list *status)
}
/**
* Now that we have all the information,
it's time to
format the output.
* Now that we have all the information, format the output.
*/
if
(
s
->
head_to_index
)
{
...
...
@@ -417,6 +437,21 @@ static void print_short(git_repository *repo, git_status_list *status)
}
}
static
int
print_submod
(
git_submodule
*
sm
,
const
char
*
name
,
void
*
payload
)
{
int
*
count
=
payload
;
(
void
)
name
;
if
(
*
count
==
0
)
printf
(
"# Submodules
\n
"
);
(
*
count
)
++
;
printf
(
"# - submodule '%s' at %s
\n
"
,
git_submodule_name
(
sm
),
git_submodule_path
(
sm
));
return
0
;
}
/**
* Parse options that git's status command supports.
*/
...
...
@@ -462,6 +497,12 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
o
->
statusopt
.
flags
|=
GIT_STATUS_OPT_EXCLUDE_SUBMODULES
;
else
if
(
!
strncmp
(
a
,
"--git-dir="
,
strlen
(
"--git-dir="
)))
o
->
repodir
=
a
+
strlen
(
"--git-dir="
);
else
if
(
!
strcmp
(
a
,
"--repeat"
))
o
->
repeat
=
10
;
else
if
(
match_int_arg
(
&
o
->
repeat
,
&
args
,
"--repeat"
,
0
))
/* okay */
;
else
if
(
!
strcmp
(
a
,
"--list-submodules"
))
o
->
showsubmod
=
1
;
else
check_lg2
(
-
1
,
"Unsupported option"
,
a
);
}
...
...
src/buffer.c
View file @
923c8400
...
...
@@ -467,6 +467,59 @@ int git_buf_join(
return
0
;
}
int
git_buf_join3
(
git_buf
*
buf
,
char
separator
,
const
char
*
str_a
,
const
char
*
str_b
,
const
char
*
str_c
)
{
size_t
len_a
=
strlen
(
str_a
),
len_b
=
strlen
(
str_b
),
len_c
=
strlen
(
str_c
);
int
sep_a
=
0
,
sep_b
=
0
;
char
*
tgt
;
/* for this function, disallow pointers into the existing buffer */
assert
(
str_a
<
buf
->
ptr
||
str_a
>=
buf
->
ptr
+
buf
->
size
);
assert
(
str_b
<
buf
->
ptr
||
str_b
>=
buf
->
ptr
+
buf
->
size
);
assert
(
str_c
<
buf
->
ptr
||
str_c
>=
buf
->
ptr
+
buf
->
size
);
if
(
separator
)
{
if
(
len_a
>
0
)
{
while
(
*
str_b
==
separator
)
{
str_b
++
;
len_b
--
;
}
sep_a
=
(
str_a
[
len_a
-
1
]
!=
separator
);
}
if
(
len_a
>
0
||
len_b
>
0
)
while
(
*
str_c
==
separator
)
{
str_c
++
;
len_c
--
;
}
if
(
len_b
>
0
)
sep_b
=
(
str_b
[
len_b
-
1
]
!=
separator
);
}
if
(
git_buf_grow
(
buf
,
len_a
+
sep_a
+
len_b
+
sep_b
+
len_c
+
1
)
<
0
)
return
-
1
;
tgt
=
buf
->
ptr
;
if
(
len_a
)
{
memcpy
(
tgt
,
str_a
,
len_a
);
tgt
+=
len_a
;
}
if
(
sep_a
)
*
tgt
++
=
separator
;
if
(
len_b
)
{
memcpy
(
tgt
,
str_b
,
len_b
);
tgt
+=
len_b
;
}
if
(
sep_b
)
*
tgt
++
=
separator
;
if
(
len_c
)
memcpy
(
tgt
,
str_c
,
len_c
);
buf
->
size
=
len_a
+
sep_a
+
len_b
+
sep_b
+
len_c
;
buf
->
ptr
[
buf
->
size
]
=
'\0'
;
return
0
;
}
void
git_buf_rtrim
(
git_buf
*
buf
)
{
while
(
buf
->
size
>
0
)
{
...
...
src/buffer.h
View file @
923c8400
...
...
@@ -98,8 +98,12 @@ void git_buf_truncate(git_buf *buf, size_t len);
void
git_buf_shorten
(
git_buf
*
buf
,
size_t
amount
);
void
git_buf_rtruncate_at_char
(
git_buf
*
path
,
char
separator
);
/** General join with separator */
int
git_buf_join_n
(
git_buf
*
buf
,
char
separator
,
int
nbuf
,
...);
/** Fast join of two strings - first may legally point into `buf` data */
int
git_buf_join
(
git_buf
*
buf
,
char
separator
,
const
char
*
str_a
,
const
char
*
str_b
);
/** Fast join of three strings - cannot reference `buf` data */
int
git_buf_join3
(
git_buf
*
buf
,
char
separator
,
const
char
*
str_a
,
const
char
*
str_b
,
const
char
*
str_c
);
/**
* Join two strings as paths, inserting a slash between as needed.
...
...
src/config.c
View file @
923c8400
...
...
@@ -615,6 +615,36 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return
error
;
}
int
git_config__update_entry
(
git_config
*
config
,
const
char
*
key
,
const
char
*
value
,
bool
overwrite_existing
,
bool
only_if_existing
)
{
int
error
=
0
;
const
git_config_entry
*
ce
=
NULL
;
if
((
error
=
git_config__lookup_entry
(
&
ce
,
config
,
key
,
false
))
<
0
)
return
error
;
if
(
!
ce
&&
only_if_existing
)
/* entry doesn't exist */
return
0
;
if
(
ce
&&
!
overwrite_existing
)
/* entry would be overwritten */
return
0
;
if
(
value
&&
ce
&&
ce
->
value
&&
!
strcmp
(
ce
->
value
,
value
))
/* no change */
return
0
;
if
(
!
value
&&
(
!
ce
||
!
ce
->
value
))
/* asked to delete absent entry */
return
0
;
if
(
!
value
)
error
=
git_config_delete_entry
(
config
,
key
);
else
error
=
git_config_set_string
(
config
,
key
,
value
);
return
error
;
}
/***********
* Getters
***********/
...
...
src/config.h
View file @
923c8400
...
...
@@ -53,6 +53,14 @@ extern int git_config__lookup_entry(
const
char
*
key
,
bool
no_errors
);
/* internal only: update and/or delete entry string with constraints */
extern
int
git_config__update_entry
(
git_config
*
cfg
,
const
char
*
key
,
const
char
*
value
,
bool
overwrite_existing
,
bool
only_if_existing
);
/*
* Lookup functions that cannot fail. These functions look up a config
* value and return a fallback value if the value is missing or if any
...
...
src/config_file.h
View file @
923c8400
...
...
@@ -16,7 +16,8 @@ GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level
GIT_INLINE
(
void
)
git_config_file_free
(
git_config_backend
*
cfg
)
{
cfg
->
free
(
cfg
);
if
(
cfg
)
cfg
->
free
(
cfg
);
}
GIT_INLINE
(
int
)
git_config_file_get_string
(
...
...
src/index.c
View file @
923c8400
...
...
@@ -517,6 +517,18 @@ int git_index_read(git_index *index, int force)
return
error
;
}
int
git_index__changed_relative_to
(
git_index
*
index
,
const
git_futils_filestamp
*
fs
)
{
/* attempt to update index (ignoring errors) */
if
(
git_index_read
(
index
,
false
)
<
0
)
giterr_clear
();
return
(
index
->
stamp
.
mtime
!=
fs
->
mtime
||
index
->
stamp
.
size
!=
fs
->
size
||
index
->
stamp
.
ino
!=
fs
->
ino
);
}
int
git_index_write
(
git_index
*
index
)
{
git_filebuf
file
=
GIT_FILEBUF_INIT
;
...
...
src/index.h
View file @
923c8400
...
...
@@ -62,4 +62,11 @@ extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
extern
unsigned
int
git_index__create_mode
(
unsigned
int
mode
);
GIT_INLINE
(
const
git_futils_filestamp
*
)
git_index__filestamp
(
git_index
*
index
)
{
return
&
index
->
stamp
;
}
extern
int
git_index__changed_relative_to
(
git_index
*
index
,
const
git_futils_filestamp
*
fs
);
#endif
src/path.h
View file @
923c8400
...
...
@@ -119,6 +119,14 @@ GIT_INLINE(void) git_path_mkposix(char *path)
# define git_path_mkposix(p)
/* blank */
#endif
/**
* Check if string is a relative path (i.e. starts with "./" or "../")
*/
GIT_INLINE
(
int
)
git_path_is_relative
(
const
char
*
p
)
{
return
(
p
[
0
]
==
'.'
&&
(
p
[
1
]
==
'/'
||
(
p
[
1
]
==
'.'
&&
p
[
2
]
==
'/'
)));
}
extern
int
git__percent_decode
(
git_buf
*
decoded_out
,
const
char
*
input
);
/**
...
...
src/refdb_fs.c
View file @
923c8400
...
...
@@ -1432,7 +1432,7 @@ static int create_new_reflog_file(const char *filepath)
GIT_INLINE
(
int
)
retrieve_reflog_path
(
git_buf
*
path
,
git_repository
*
repo
,
const
char
*
name
)
{
return
git_buf_join
_n
(
path
,
'/'
,
3
,
repo
->
path_repository
,
GIT_REFLOG_DIR
,
name
);
return
git_buf_join
3
(
path
,
'/'
,
repo
->
path_repository
,
GIT_REFLOG_DIR
,
name
);
}
static
int
refdb_reflog_fs__ensure_log
(
git_refdb_backend
*
_backend
,
const
char
*
name
)
...
...
src/remote.c
View file @
923c8400
...
...
@@ -495,9 +495,10 @@ cleanup:
int
git_remote_save
(
const
git_remote
*
remote
)
{
int
error
;
git_config
*
c
onfi
g
;
git_config
*
c
f
g
;
const
char
*
tagopt
=
NULL
;
git_buf
buf
=
GIT_BUF_INIT
;
const
git_config_entry
*
existing
;
assert
(
remote
);
...
...
@@ -509,43 +510,31 @@ int git_remote_save(const git_remote *remote)
if
((
error
=
ensure_remote_name_is_valid
(
remote
->
name
))
<
0
)
return
error
;
if
(
git_repository_config__weakptr
(
&
config
,
remote
->
repo
)
<
0
)
return
-
1
;
if
(
(
error
=
git_repository_config__weakptr
(
&
cfg
,
remote
->
repo
)
)
<
0
)
return
error
;
if
(
git_buf_printf
(
&
buf
,
"remote.%s.url"
,
remote
->
name
)
<
0
)
return
-
1
;
if
(
(
error
=
git_buf_printf
(
&
buf
,
"remote.%s.url"
,
remote
->
name
)
)
<
0
)
return
error
;
if
(
git_config_set_string
(
config
,
git_buf_cstr
(
&
buf
),
remote
->
url
)
<
0
)
{
git_buf_free
(
&
buf
);
return
-
1
;
}
/* after this point, buffer is allocated so end with cleanup */
if
((
error
=
git_config_set_string
(
cfg
,
git_buf_cstr
(
&
buf
),
remote
->
url
))
<
0
)
goto
cleanup
;
git_buf_clear
(
&
buf
);
if
(
git_buf_printf
(
&
buf
,
"remote.%s.pushurl"
,
remote
->
name
)
<
0
)
return
-
1
;
if
(
(
error
=
git_buf_printf
(
&
buf
,
"remote.%s.pushurl"
,
remote
->
name
)
)
<
0
)
goto
cleanup
;
if
(
remote
->
pushurl
)
{
if
(
git_config_set_string
(
config
,
git_buf_cstr
(
&
buf
),
remote
->
pushurl
)
<
0
)
{
git_buf_free
(
&
buf
);
return
-
1
;
}
}
else
{
int
error
=
git_config_delete_entry
(
config
,
git_buf_cstr
(
&
buf
));
if
(
error
==
GIT_ENOTFOUND
)
{
error
=
0
;
giterr_clear
();
}
if
(
error
<
0
)
{
git_buf_free
(
&
buf
);
return
error
;
}
}
if
((
error
=
git_config__update_entry
(
cfg
,
git_buf_cstr
(
&
buf
),
remote
->
pushurl
,
true
,
false
))
<
0
)
goto
cleanup
;
if
(
update_config_refspec
(
remote
,
config
,
GIT_DIRECTION_FETCH
)
<
0
)
goto
on_error
;
if
(
(
error
=
update_config_refspec
(
remote
,
cfg
,
GIT_DIRECTION_FETCH
)
)
<
0
)
goto
cleanup
;
if
(
update_config_refspec
(
remote
,
config
,
GIT_DIRECTION_PUSH
)
<
0
)
goto
on_error
;
if
(
(
error
=
update_config_refspec
(
remote
,
cfg
,
GIT_DIRECTION_PUSH
)
)
<
0
)
goto
cleanup
;
/*
* What action to take depends on the old and new values. This
...
...
@@ -561,31 +550,26 @@ int git_remote_save(const git_remote *remote)
*/
git_buf_clear
(
&
buf
);
if
(
git_buf_printf
(
&
buf
,
"remote.%s.tagopt"
,
remote
->
name
)
<
0
)
goto
on_error
;
error
=
git_config_get_string
(
&
tagopt
,
config
,
git_buf_cstr
(
&
buf
));
if
(
error
<
0
&&
error
!=
GIT_ENOTFOUND
)
goto
on_error
;
if
((
error
=
git_buf_printf
(
&
buf
,
"remote.%s.tagopt"
,
remote
->
name
))
<
0
)
goto
cleanup
;
if
(
remote
->
download_tags
==
GIT_REMOTE_DOWNLOAD_TAGS_ALL
)
{
if
(
git_config_set_string
(
config
,
git_buf_cstr
(
&
buf
),
"--tags"
)
<
0
)
goto
on_error
;
}
else
if
(
remote
->
download_tags
==
GIT_REMOTE_DOWNLOAD_TAGS_NONE
)
{
if
(
git_config_set_string
(
config
,
git_buf_cstr
(
&
buf
),
"--no-tags"
)
<
0
)
goto
on_error
;
}
else
if
(
tagopt
)
{
if
(
git_config_delete_entry
(
config
,
git_buf_cstr
(
&
buf
))
<
0
)
goto
on_error
;
}
if
((
error
=
git_config__lookup_entry
(
&
existing
,
cfg
,
git_buf_cstr
(
&
buf
),
false
))
<
0
)
goto
cleanup
;
git_buf_free
(
&
buf
);
if
(
remote
->
download_tags
==
GIT_REMOTE_DOWNLOAD_TAGS_ALL
)
tagopt
=
"--tags"
;
else
if
(
remote
->
download_tags
==
GIT_REMOTE_DOWNLOAD_TAGS_NONE
)
tagopt
=
"--no-tags"
;
else
if
(
existing
!=
NULL
)
tagopt
=
NULL
;
return
0
;
error
=
git_config__update_entry
(
cfg
,
git_buf_cstr
(
&
buf
),
tagopt
,
true
,
false
);
on_error
:
cleanup
:
git_buf_free
(
&
buf
);
return
-
1
;
return
error
;
}
const
char
*
git_remote_name
(
const
git_remote
*
remote
)
...
...
src/repository.c
View file @
923c8400
...
...
@@ -93,6 +93,7 @@ void git_repository__cleanup(git_repository *repo)
git_cache_clear
(
&
repo
->
objects
);
git_attr_cache_flush
(
repo
);
git_submodule_cache_free
(
repo
);
set_config
(
repo
,
NULL
);
set_index
(
repo
,
NULL
);
...
...
@@ -108,7 +109,6 @@ void git_repository_free(git_repository *repo)
git_repository__cleanup
(
repo
);
git_cache_free
(
&
repo
->
objects
);
git_submodule_config_free
(
repo
);
git_diff_driver_registry_free
(
repo
->
diff_drivers
);
repo
->
diff_drivers
=
NULL
;
...
...
src/repository.h
View file @
923c8400
...
...
@@ -19,7 +19,7 @@
#include "buffer.h"
#include "object.h"
#include "attrcache.h"
#include "s
trmap
.h"
#include "s
ubmodule
.h"
#include "diff_driver.h"
#define DOT_GIT ".git"
...
...
@@ -105,10 +105,10 @@ struct git_repository {
git_refdb
*
_refdb
;
git_config
*
_config
;
git_index
*
_index
;
git_submodule_cache
*
_submodules
;
git_cache
objects
;
git_attr_cache
attrcache
;
git_strmap
*
submodules
;
git_diff_driver_registry
*
diff_drivers
;
char
*
path_repository
;
...
...
@@ -149,11 +149,6 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo);
int
git_repository__cvar
(
int
*
out
,
git_repository
*
repo
,
git_cvar_cached
cvar
);
void
git_repository__cvar_cache_clear
(
git_repository
*
repo
);
/*
* Submodule cache
*/
extern
void
git_submodule_config_free
(
git_repository
*
repo
);
GIT_INLINE
(
int
)
git_repository__ensure_not_bare
(
git_repository
*
repo
,
const
char
*
operation_name
)
...
...
src/submodule.c
View file @
923c8400
...
...
@@ -49,6 +49,16 @@ static git_cvar_map _sm_recurse_map[] = {
{
GIT_CVAR_TRUE
,
NULL
,
GIT_SUBMODULE_RECURSE_YES
},
};
enum
{
CACHE_OK
=
0
,
CACHE_REFRESH
=
1
,
CACHE_FLUSH
=
2
};
enum
{
GITMODULES_EXISTING
=
0
,
GITMODULES_CREATE
=
1
,
};
static
kh_inline
khint_t
str_hash_no_trailing_slash
(
const
char
*
s
)
{
khint_t
h
;
...
...
@@ -77,13 +87,15 @@ __KHASH_IMPL(
str
,
static
kh_inline
,
const
char
*
,
void
*
,
1
,
str_hash_no_trailing_slash
,
str_equal_no_trailing_slash
);
static
int
load_submodule_config
(
git_repository
*
repo
,
bool
reload
);
static
git_config_backend
*
open_gitmodules
(
git_repository
*
,
bool
,
const
git_oid
*
);
static
int
lookup_head_remote
(
git_buf
*
url
,
git_repository
*
repo
);
static
int
submodule_get
(
git_submodule
**
,
git_repository
*
,
const
char
*
,
const
char
*
);
static
int
submodule_cache_init
(
git_repository
*
repo
,
int
refresh
);
static
void
submodule_cache_free
(
git_submodule_cache
*
cache
);
static
git_config_backend
*
open_gitmodules
(
git_submodule_cache
*
,
int
gitmod
);
static
int
get_url_base
(
git_buf
*
url
,
git_repository
*
repo
);
static
int
lookup_head_remote_key
(
git_buf
*
remote_key
,
git_repository
*
repo
);
static
int
submodule_get
(
git_submodule
**
,
git_submodule_cache
*
,
const
char
*
,
const
char
*
);
static
int
submodule_load_from_config
(
const
git_config_entry
*
,
void
*
);
static
int
submodule_load_from_wd_lite
(
git_submodule
*
);
static
int
submodule_update_config
(
git_submodule
*
,
const
char
*
,
const
char
*
,
bool
,
bool
);
static
void
submodule_get_index_status
(
unsigned
int
*
,
git_submodule
*
);
static
void
submodule_get_wd_status
(
unsigned
int
*
,
git_submodule
*
,
git_repository
*
,
git_submodule_ignore_t
);
...
...
@@ -102,7 +114,7 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
/* lookup submodule or return ENOTFOUND if it doesn't exist */
static
int
submodule_lookup
(
git_submodule
**
out
,
git_s
trmap
*
cache
,
git_s
ubmodule_cache
*
cache
,
const
char
*
name
,
const
char
*
alternate
)
{
...
...
@@ -110,18 +122,18 @@ static int submodule_lookup(
/* lock cache */
pos
=
git_strmap_lookup_index
(
cache
,
name
);
pos
=
git_strmap_lookup_index
(
cache
->
submodules
,
name
);
if
(
!
git_strmap_valid_index
(
cache
,
pos
)
&&
alternate
)
pos
=
git_strmap_lookup_index
(
cache
,
alternate
);
if
(
!
git_strmap_valid_index
(
cache
->
submodules
,
pos
)
&&
alternate
)
pos
=
git_strmap_lookup_index
(
cache
->
submodules
,
alternate
);
if
(
!
git_strmap_valid_index
(
cache
,
pos
))
{
if
(
!
git_strmap_valid_index
(
cache
->
submodules
,
pos
))
{
/* unlock cache */
return
GIT_ENOTFOUND
;
/* don't set error - caller will cope */
}
if
(
out
!=
NULL
)
{
git_submodule
*
sm
=
git_strmap_value_at
(
cache
,
pos
);
git_submodule
*
sm
=
git_strmap_value_at
(
cache
->
submodules
,
pos
);
GIT_REFCOUNT_INC
(
sm
);
*
out
=
sm
;
}
...
...
@@ -131,6 +143,18 @@ static int submodule_lookup(
return
0
;
}
/* clear a set of flags on all submodules */
static
void
submodule_cache_clear_flags
(
git_submodule_cache
*
cache
,
uint32_t
mask
)
{
git_submodule
*
sm
;
uint32_t
inverted_mask
=
~
mask
;
git_strmap_foreach_value
(
cache
->
submodules
,
sm
,
{
sm
->
flags
&=
inverted_mask
;
});
}
/*
* PUBLIC APIS
*/
...
...
@@ -139,17 +163,45 @@ bool git_submodule__is_submodule(git_repository *repo, const char *name)
{
git_strmap
*
map
;
if
(
load_submodule_config
(
repo
,
false
)
<
0
)
{
if
(
submodule_cache_init
(
repo
,
CACHE_OK
)
<
0
)
{
giterr_clear
();
return
false
;
}
if
(
!
(
map
=
repo
->
submodules
))
if
(
!
repo
->
_submodules
||
!
(
map
=
repo
->
_submodules
->
submodules
))
return
false
;
return
git_strmap_valid_index
(
map
,
git_strmap_lookup_index
(
map
,
name
));
}
static
void
submodule_set_lookup_error
(
int
error
,
const
char
*
name
)
{
if
(
!
error
)
return
;
giterr_set
(
GITERR_SUBMODULE
,
(
error
==
GIT_ENOTFOUND
)
?
"No submodule named '%s'"
:
"Submodule '%s' has not been added yet"
,
name
);
}
int
git_submodule__lookup
(
git_submodule
**
out
,
/* NULL if user only wants to test existence */
git_repository
*
repo
,
const
char
*
name
)
/* trailing slash is allowed */
{
int
error
;
assert
(
repo
&&
name
);
if
((
error
=
submodule_cache_init
(
repo
,
CACHE_OK
))
<
0
)
return
error
;
if
((
error
=
submodule_lookup
(
out
,
repo
->
_submodules
,
name
,
NULL
))
<
0
)
submodule_set_lookup_error
(
error
,
name
);
return
error
;
}
int
git_submodule_lookup
(
git_submodule
**
out
,
/* NULL if user only wants to test existence */
git_repository
*
repo
,
...
...
@@ -159,88 +211,99 @@ int git_submodule_lookup(
assert
(
repo
&&
name
);
if
((
error
=
load_submodule_config
(
repo
,
false
))
<
0
)
if
((
error
=
submodule_cache_init
(
repo
,
CACHE_REFRESH
))
<
0
)
return
error
;
if
((
error
=
submodule_lookup
(
out
,
repo
->
submodules
,
name
,
NULL
))
<
0
)
{
if
((
error
=
submodule_lookup
(
out
,
repo
->
_
submodules
,
name
,
NULL
))
<
0
)
{
/* check if a plausible submodule exists at path */
if
(
git_repository_workdir
(
repo
))
{
git_buf
path
=
GIT_BUF_INIT
;
if
(
git_buf_joinpath
(
&
path
,
git_repository_workdir
(
repo
),
name
)
<
0
)
if
(
git_buf_join3
(
&
path
,
'/'
,
git_repository_workdir
(
repo
),
name
,
DOT_GIT
)
<
0
)
return
-
1
;
if
(
git_path_
contains
(
&
path
,
DOT_GIT
))
if
(
git_path_
exists
(
path
.
ptr
))
error
=
GIT_EEXISTS
;
git_buf_free
(
&
path
);
}
giterr_set
(
GITERR_SUBMODULE
,
(
error
==
GIT_ENOTFOUND
)
?
"No submodule named '%s'"
:
"Submodule '%s' has not been added yet"
,
name
);
submodule_set_lookup_error
(
error
,
name
);
}
return
error
;
}
static
void
submodule_free_dup
(
void
*
sm
)
{
git_submodule_free
(
sm
);
}
int
git_submodule_foreach
(
git_repository
*
repo
,
int
(
*
callback
)(
git_submodule
*
sm
,
const
char
*
name
,
void
*
payload
),
void
*
payload
)
{
int
error
;
size_t
i
;
git_submodule
*
sm
;
git_
vector
seen
=
GIT_VECTOR_INIT
;
git_vector
_set_cmp
(
&
seen
,
submodule_cmp
)
;
git_
submodule_cache
*
cache
;
git_vector
snapshot
=
GIT_VECTOR_INIT
;
assert
(
repo
&&
callback
);
if
((
error
=
load_submodule_config
(
repo
,
true
))
<
0
)
if
((
error
=
submodule_cache_init
(
repo
,
CACHE_REFRESH
))
<
0
)
return
error
;
git_strmap_foreach_value
(
repo
->
submodules
,
sm
,
{
/* Usually the following will not come into play - it just prevents
* us from issuing a callback twice for a submodule where the name
* and path are not the same.
*/
if
(
GIT_REFCOUNT_VAL
(
sm
)
>
1
)
{
if
(
git_vector_bsearch
(
NULL
,
&
seen
,
sm
)
!=
GIT_ENOTFOUND
)
continue
;
if
((
error
=
git_vector_insert
(
&
seen
,
sm
))
<
0
)
cache
=
repo
->
_submodules
;
if
(
git_mutex_lock
(
&
cache
->
lock
)
<
0
)
{
giterr_set
(
GITERR_OS
,
"Unable to acquire lock on submodule cache"
);
return
-
1
;
}
if
(
!
(
error
=
git_vector_init
(
&
snapshot
,
kh_size
(
cache
->
submodules
),
submodule_cmp
)))
{
git_strmap_foreach_value
(
cache
->
submodules
,
sm
,
{
if
((
error
=
git_vector_insert
(
&
snapshot
,
sm
))
<
0
)
break
;
}
GIT_REFCOUNT_INC
(
sm
);
});
}
git_mutex_unlock
(
&
cache
->
lock
);
if
(
error
<
0
)
goto
done
;
git_vector_uniq
(
&
snapshot
,
submodule_free_dup
);
git_vector_foreach
(
&
snapshot
,
i
,
sm
)
{
if
((
error
=
callback
(
sm
,
sm
->
name
,
payload
))
!=
0
)
{
giterr_set_after_callback
(
error
);
break
;
}
}
);
}
git_vector_free
(
&
seen
);
done:
git_vector_foreach
(
&
snapshot
,
i
,
sm
)
git_submodule_free
(
sm
);
git_vector_free
(
&
snapshot
);
return
error
;
}
void
git_submodule_c
onfig
_free
(
git_repository
*
repo
)
void
git_submodule_c
ache
_free
(
git_repository
*
repo
)
{
git_strmap
*
smcfg
;
git_submodule
*
sm
;
git_submodule_cache
*
cache
;
assert
(
repo
);
smcfg
=
repo
->
submodules
;
repo
->
submodules
=
NULL
;
if
(
smcfg
==
NULL
)
return
;
git_strmap_foreach_value
(
smcfg
,
sm
,
{
sm
->
repo
=
NULL
;
/* disconnect from repo */
;
git_submodule_free
(
sm
);
});
git_strmap_free
(
smcfg
);
if
((
cache
=
git__swap
(
repo
->
_submodules
,
NULL
))
!=
NULL
)
submodule_cache_free
(
cache
);
}
int
git_submodule_add_setup
(
...
...
@@ -261,18 +324,16 @@ int git_submodule_add_setup(
/* see if there is already an entry for this submodule */
if
(
git_submodule_lookup
(
&
sm
,
repo
,
path
)
<
0
)
if
(
git_submodule_lookup
(
NULL
,
repo
,
path
)
<
0
)
giterr_clear
();
else
{
git_submodule_free
(
sm
);
giterr_set
(
GITERR_SUBMODULE
,
"Attempt to add
a submodule that already exists"
);
"Attempt to add
submodule '%s' that already exists"
,
path
);
return
GIT_EEXISTS
;
}
/* resolve parameters */
error
=
git_submodule_resolve_url
(
&
real_url
,
repo
,
url
);
if
(
error
)
if
((
error
=
git_submodule_resolve_url
(
&
real_url
,
repo
,
url
))
<
0
)
goto
cleanup
;
/* validate and normalize path */
...
...
@@ -288,9 +349,9 @@ int git_submodule_add_setup(
/* update .gitmodules */
if
(
(
mods
=
open_gitmodules
(
repo
,
true
,
NULL
))
==
NULL
)
{
if
(
!
(
mods
=
open_gitmodules
(
repo
->
_submodules
,
GITMODULES_CREATE
))
)
{
giterr_set
(
GITERR_SUBMODULE
,
"Adding submodules to a bare repository is not supported
(for now)
"
);
"Adding submodules to a bare repository is not supported"
);
return
-
1
;
}
...
...
@@ -328,8 +389,8 @@ int git_submodule_add_setup(
else
if
(
use_gitlink
)
{
git_buf
repodir
=
GIT_BUF_INIT
;
error
=
git_buf_join
_n
(
&
repodir
,
'/'
,
3
,
git_repository_path
(
repo
),
"modules"
,
path
);
error
=
git_buf_join
3
(
&
repodir
,
'/'
,
git_repository_path
(
repo
),
"modules"
,
path
);
if
(
error
<
0
)
goto
cleanup
;
...
...
@@ -348,10 +409,18 @@ int git_submodule_add_setup(
/* add submodule to hash and "reload" it */
if
(
!
(
error
=
submodule_get
(
&
sm
,
repo
,
path
,
NULL
))
&&
if
(
git_mutex_lock
(
&
repo
->
_submodules
->
lock
)
<
0
)
{
giterr_set
(
GITERR_OS
,
"Unable to acquire lock on submodule cache"
);
error
=
-
1
;
goto
cleanup
;
}
if
(
!
(
error
=
submodule_get
(
&
sm
,
repo
->
_submodules
,
path
,
NULL
))
&&
!
(
error
=
git_submodule_reload
(
sm
,
false
)))
error
=
git_submodule_init
(
sm
,
false
);
git_mutex_unlock
(
&
repo
->
_submodules
->
lock
);
cleanup:
if
(
error
&&
sm
)
{
git_submodule_free
(
sm
);
...
...
@@ -360,8 +429,7 @@ cleanup:
if
(
out
!=
NULL
)
*
out
=
sm
;
if
(
mods
!=
NULL
)
git_config_file_free
(
mods
);
git_config_file_free
(
mods
);
git_repository_free
(
subrepo
);
git_buf_free
(
&
real_url
);
git_buf_free
(
&
name
);
...
...
@@ -489,10 +557,10 @@ int git_submodule_save(git_submodule *submodule)
assert
(
submodule
);
mods
=
open_gitmodules
(
submodule
->
repo
,
true
,
NULL
);
mods
=
open_gitmodules
(
submodule
->
repo
->
_submodules
,
GITMODULES_CREATE
);
if
(
!
mods
)
{
giterr_set
(
GITERR_SUBMODULE
,
"Adding submodules to a bare repository is not supported
(for now)
"
);
"Adding submodules to a bare repository is not supported"
);
return
-
1
;
}
...
...
@@ -539,8 +607,7 @@ int git_submodule_save(git_submodule *submodule)
submodule
->
flags
|=
GIT_SUBMODULE_STATUS_IN_CONFIG
;
cleanup:
if
(
mods
!=
NULL
)
git_config_file_free
(
mods
);
git_config_file_free
(
mods
);
git_buf_free
(
&
key
);
return
error
;
...
...
@@ -576,8 +643,8 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur
assert
(
url
);
if
(
url
[
0
]
==
'.'
&&
(
url
[
1
]
==
'/'
||
(
url
[
1
]
==
'.'
&&
url
[
2
]
==
'/'
)
))
{
if
(
!
(
error
=
lookup_head_remot
e
(
out
,
repo
)))
if
(
git_path_is_relative
(
url
))
{
if
(
!
(
error
=
get_url_bas
e
(
out
,
repo
)))
error
=
git_path_apply_relative
(
out
,
url
);
}
else
if
(
strchr
(
url
,
':'
)
!=
NULL
||
url
[
0
]
==
'/'
)
{
error
=
git_buf_sets
(
out
,
url
);
...
...
@@ -715,46 +782,95 @@ git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules(
return
old
;
}
int
git_submodule_init
(
git_submodule
*
s
ubmodule
,
int
overwrite
)
int
git_submodule_init
(
git_submodule
*
s
m
,
int
overwrite
)
{
int
error
;
const
char
*
val
;
git_buf
key
=
GIT_BUF_INIT
;
git_config
*
cfg
=
NULL
;
/* write "submodule.NAME.url" */
if
(
!
submodule
->
url
)
{
if
(
!
sm
->
url
)
{
giterr_set
(
GITERR_SUBMODULE
,
"No URL configured for submodule '%s'"
,
s
ubmodule
->
name
);
"No URL configured for submodule '%s'"
,
s
m
->
name
);
return
-
1
;
}
error
=
submodule_update_config
(
submodule
,
"url"
,
submodule
->
url
,
overwrite
!=
0
,
false
);
if
(
error
<
0
)
if
((
error
=
git_repository_config
(
&
cfg
,
sm
->
repo
))
<
0
)
return
error
;
/* write "submodule.NAME.url" */
if
((
error
=
git_buf_printf
(
&
key
,
"submodule.%s.url"
,
sm
->
name
))
<
0
||
(
error
=
git_config__update_entry
(
cfg
,
key
.
ptr
,
sm
->
url
,
overwrite
!=
0
,
false
))
<
0
)
goto
cleanup
;
/* write "submodule.NAME.update" if not default */
val
=
(
submodule
->
update
==
GIT_SUBMODULE_UPDATE_CHECKOUT
)
?
NULL
:
git_submodule_update_to_str
(
submodule
->
update
);
error
=
submodule_update_config
(
submodule
,
"update"
,
val
,
(
overwrite
!=
0
),
false
);
val
=
(
sm
->
update
==
GIT_SUBMODULE_UPDATE_CHECKOUT
)
?
NULL
:
git_submodule_update_to_str
(
sm
->
update
);
if
((
error
=
git_buf_printf
(
&
key
,
"submodule.%s.update"
,
sm
->
name
))
<
0
||
(
error
=
git_config__update_entry
(
cfg
,
key
.
ptr
,
val
,
overwrite
!=
0
,
false
))
<
0
)
goto
cleanup
;
/* success */
cleanup:
git_config_free
(
cfg
);
git_buf_free
(
&
key
);
return
error
;
}
int
git_submodule_sync
(
git_submodule
*
s
ubmodule
)
int
git_submodule_sync
(
git_submodule
*
s
m
)
{
if
(
!
submodule
->
url
)
{
int
error
=
0
;
git_config
*
cfg
=
NULL
;
git_buf
key
=
GIT_BUF_INIT
;
git_repository
*
smrepo
=
NULL
;
if
(
!
sm
->
url
)
{
giterr_set
(
GITERR_SUBMODULE
,
"No URL configured for submodule '%s'"
,
s
ubmodule
->
name
);
"No URL configured for submodule '%s'"
,
s
m
->
name
);
return
-
1
;
}
/* copy URL over to config only if it already exists */
return
submodule_update_config
(
submodule
,
"url"
,
submodule
->
url
,
true
,
true
);
if
(
!
(
error
=
git_repository_config__weakptr
(
&
cfg
,
sm
->
repo
))
&&
!
(
error
=
git_buf_printf
(
&
key
,
"submodule.%s.url"
,
sm
->
name
)))
error
=
git_config__update_entry
(
cfg
,
key
.
ptr
,
sm
->
url
,
true
,
true
);
/* if submodule exists in the working directory, update remote url */
if
(
!
error
&&
(
sm
->
flags
&
GIT_SUBMODULE_STATUS_IN_WD
)
!=
0
&&
!
(
error
=
git_submodule_open
(
&
smrepo
,
sm
)))
{
git_buf
remote_name
=
GIT_BUF_INIT
;
if
((
error
=
git_repository_config__weakptr
(
&
cfg
,
smrepo
))
<
0
)
/* return error from reading submodule config */
;
else
if
((
error
=
lookup_head_remote_key
(
&
remote_name
,
smrepo
))
<
0
)
{
giterr_clear
();
error
=
git_buf_sets
(
&
key
,
"branch.origin.remote"
);
}
else
{
error
=
git_buf_join3
(
&
key
,
'.'
,
"branch"
,
remote_name
.
ptr
,
"remote"
);
git_buf_free
(
&
remote_name
);
}
if
(
!
error
)
error
=
git_config__update_entry
(
cfg
,
key
.
ptr
,
sm
->
url
,
true
,
false
);
git_repository_free
(
smrepo
);
}
git_buf_free
(
&
key
);
return
error
;
}
static
int
git_submodule__open
(
...
...
@@ -821,64 +937,9 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm)
return
git_submodule__open
(
subrepo
,
sm
,
false
);
}
static
void
submodule_cache_remove_item
(
git_strmap
*
cache
,
const
char
*
name
,
git_submodule
*
expected
,
bool
free_after_remove
)
{
khiter_t
pos
;
git_submodule
*
found
;
if
(
!
cache
)
return
;
pos
=
git_strmap_lookup_index
(
cache
,
name
);
if
(
!
git_strmap_valid_index
(
cache
,
pos
))
return
;
found
=
git_strmap_value_at
(
cache
,
pos
);
if
(
expected
&&
found
!=
expected
)
return
;
git_strmap_set_value_at
(
cache
,
pos
,
NULL
);
git_strmap_delete_at
(
cache
,
pos
);
if
(
free_after_remove
)
git_submodule_free
(
found
);
}
int
git_submodule_reload_all
(
git_repository
*
repo
,
int
force
)
{
int
error
=
0
;
git_submodule
*
sm
;
GIT_UNUSED
(
force
);
assert
(
repo
);
if
(
repo
->
submodules
)
git_strmap_foreach_value
(
repo
->
submodules
,
sm
,
{
sm
->
flags
=
0
;
});
if
((
error
=
load_submodule_config
(
repo
,
true
))
<
0
)
return
error
;
git_strmap_foreach_value
(
repo
->
submodules
,
sm
,
{
git_strmap
*
cache
=
repo
->
submodules
;
if
(
sm
&&
(
sm
->
flags
&
GIT_SUBMODULE_STATUS__IN_FLAGS
)
==
0
)
{
/* we must check path != name before first remove, in case
* that call frees the submodule */
bool
free_as_path
=
(
sm
->
path
!=
sm
->
name
);
submodule_cache_remove_item
(
cache
,
sm
->
name
,
sm
,
true
);
if
(
free_as_path
)
submodule_cache_remove_item
(
cache
,
sm
->
path
,
sm
,
true
);
}
});
return
error
;
return
submodule_cache_init
(
repo
,
force
?
CACHE_FLUSH
:
CACHE_REFRESH
);
}
static
void
submodule_update_from_index_entry
(
...
...
@@ -955,41 +1016,44 @@ static int submodule_update_head(git_submodule *submodule)
}
int
git_submodule_reload
(
git_submodule
*
s
ubmodule
,
int
force
)
int
git_submodule_reload
(
git_submodule
*
s
m
,
int
force
)
{
int
error
=
0
;
git_config_backend
*
mods
;
git_submodule_cache
*
cache
;
GIT_UNUSED
(
force
);
assert
(
submodule
);
assert
(
sm
);
cache
=
sm
->
repo
->
_submodules
;
/* refresh index data */
if
((
error
=
submodule_update_index
(
s
ubmodule
))
<
0
)
if
((
error
=
submodule_update_index
(
s
m
))
<
0
)
return
error
;
/* refresh HEAD tree data */
if
((
error
=
submodule_update_head
(
s
ubmodule
))
<
0
)
if
((
error
=
submodule_update_head
(
s
m
))
<
0
)
return
error
;
/* done if bare */
if
(
git_repository_is_bare
(
s
ubmodule
->
repo
))
if
(
git_repository_is_bare
(
s
m
->
repo
))
return
error
;
/* refresh config data */
mods
=
open_gitmodules
(
submodule
->
repo
,
false
,
NULL
);
mods
=
open_gitmodules
(
cache
,
GITMODULES_EXISTING
);
if
(
mods
!=
NULL
)
{
git_buf
path
=
GIT_BUF_INIT
;
git_buf_sets
(
&
path
,
"submodule
\\
."
);
git_buf_text_puts_escape_regex
(
&
path
,
s
ubmodule
->
name
);
git_buf_text_puts_escape_regex
(
&
path
,
s
m
->
name
);
git_buf_puts
(
&
path
,
".*"
);
if
(
git_buf_oom
(
&
path
))
error
=
-
1
;
else
error
=
git_config_file_foreach_match
(
mods
,
path
.
ptr
,
submodule_load_from_config
,
submodule
->
repo
);
mods
,
path
.
ptr
,
submodule_load_from_config
,
cache
);
git_buf_free
(
&
path
);
git_config_file_free
(
mods
);
...
...
@@ -999,9 +1063,11 @@ int git_submodule_reload(git_submodule *submodule, int force)
}
/* refresh wd data */
submodule
->
flags
&=
~
GIT_SUBMODULE_STATUS__ALL_WD_FLAGS
;
sm
->
flags
&=
~
(
GIT_SUBMODULE_STATUS_IN_WD
|
GIT_SUBMODULE_STATUS__WD_OID_VALID
|
GIT_SUBMODULE_STATUS__WD_FLAGS
);
return
submodule_load_from_wd_lite
(
s
ubmodule
);
return
submodule_load_from_wd_lite
(
s
m
);
}
static
void
submodule_copy_oid_maybe
(
...
...
@@ -1095,34 +1161,69 @@ int git_submodule_location(unsigned int *location, git_submodule *sm)
* INTERNAL FUNCTIONS
*/
static
git_submodule
*
submodule_alloc
(
git_repository
*
repo
,
const
char
*
name
)
static
int
submodule_alloc
(
git_submodule
**
out
,
git_submodule_cache
*
cache
,
const
char
*
name
)
{
size_t
namelen
;
git_submodule
*
sm
;
if
(
!
name
||
!
(
namelen
=
strlen
(
name
)))
{
giterr_set
(
GITERR_SUBMODULE
,
"Invalid submodule name"
);
return
NULL
;
return
-
1
;
}
sm
=
git__calloc
(
1
,
sizeof
(
git_submodule
));
if
(
sm
==
NULL
)
return
NULL
;
GITERR_CHECK_ALLOC
(
sm
);
sm
->
name
=
sm
->
path
=
git__strdup
(
name
);
if
(
!
sm
->
name
)
{
git__free
(
sm
);
return
NULL
;
return
-
1
;
}
GIT_REFCOUNT_INC
(
sm
);
sm
->
ignore
=
sm
->
ignore_default
=
GIT_SUBMODULE_IGNORE_NONE
;
sm
->
update
=
sm
->
update_default
=
GIT_SUBMODULE_UPDATE_CHECKOUT
;
sm
->
fetch_recurse
=
sm
->
fetch_recurse_default
=
GIT_SUBMODULE_RECURSE_NO
;
sm
->
repo
=
repo
;
sm
->
repo
=
cache
->
repo
;
sm
->
branch
=
NULL
;
return
sm
;
*
out
=
sm
;
return
0
;
}
static
void
submodule_cache_remove_item
(
git_submodule_cache
*
cache
,
git_submodule
*
item
,
bool
free_after_remove
)
{
git_strmap
*
map
;
const
char
*
name
,
*
alt
;
if
(
!
cache
||
!
(
map
=
cache
->
submodules
)
||
!
item
)
return
;
name
=
item
->
name
;
alt
=
(
item
->
path
!=
item
->
name
)
?
item
->
path
:
NULL
;
for
(;
name
;
name
=
alt
,
alt
=
NULL
)
{
khiter_t
pos
=
git_strmap_lookup_index
(
map
,
name
);
git_submodule
*
found
;
if
(
!
git_strmap_valid_index
(
map
,
pos
))
continue
;
found
=
git_strmap_value_at
(
map
,
pos
);
if
(
found
!=
item
)
continue
;
git_strmap_set_value_at
(
map
,
pos
,
NULL
);
git_strmap_delete_at
(
map
,
pos
);
if
(
free_after_remove
)
git_submodule_free
(
found
);
}
}
static
void
submodule_release
(
git_submodule
*
sm
)
...
...
@@ -1131,10 +1232,9 @@ static void submodule_release(git_submodule *sm)
return
;
if
(
sm
->
repo
)
{
git_strmap
*
cache
=
sm
->
repo
->
submodules
;
submodule_cache_remove_item
(
cache
,
sm
->
name
,
sm
,
false
);
if
(
sm
->
path
!=
sm
->
name
)
submodule_cache_remove_item
(
cache
,
sm
->
path
,
sm
,
false
);
git_submodule_cache
*
cache
=
sm
->
repo
->
_submodules
;
sm
->
repo
=
NULL
;
submodule_cache_remove_item
(
cache
,
sm
,
false
);
}
if
(
sm
->
path
!=
sm
->
name
)
...
...
@@ -1155,40 +1255,39 @@ void git_submodule_free(git_submodule *sm)
static
int
submodule_get
(
git_submodule
**
out
,
git_
repository
*
repo
,
git_
submodule_cache
*
cache
,
const
char
*
name
,
const
char
*
alternate
)
{
int
error
=
0
;
git_strmap
*
smcfg
=
repo
->
submodules
;
khiter_t
pos
;
git_submodule
*
sm
;
pos
=
git_strmap_lookup_index
(
smcfg
,
name
);
pos
=
git_strmap_lookup_index
(
cache
->
submodules
,
name
);
if
(
!
git_strmap_valid_index
(
smcfg
,
pos
)
&&
alternate
)
pos
=
git_strmap_lookup_index
(
smcfg
,
alternate
);
if
(
!
git_strmap_valid_index
(
cache
->
submodules
,
pos
)
&&
alternate
)
pos
=
git_strmap_lookup_index
(
cache
->
submodules
,
alternate
);
if
(
!
git_strmap_valid_index
(
smcfg
,
pos
))
{
sm
=
submodule_alloc
(
repo
,
name
);
GITERR_CHECK_ALLOC
(
sm
)
;
if
(
!
git_strmap_valid_index
(
cache
->
submodules
,
pos
))
{
if
((
error
=
submodule_alloc
(
&
sm
,
cache
,
name
))
<
0
)
return
error
;
/* insert value at name - if another thread beats us to it, then use
* their record and release our own.
*/
pos
=
kh_put
(
str
,
smcfg
,
sm
->
name
,
&
error
);
pos
=
kh_put
(
str
,
cache
->
submodules
,
sm
->
name
,
&
error
);
if
(
error
<
0
)
goto
done
;
else
if
(
error
==
0
)
{
git_submodule_free
(
sm
);
sm
=
git_strmap_value_at
(
smcfg
,
pos
);
sm
=
git_strmap_value_at
(
cache
->
submodules
,
pos
);
}
else
{
error
=
0
;
git_strmap_set_value_at
(
smcfg
,
pos
,
sm
);
git_strmap_set_value_at
(
cache
->
submodules
,
pos
,
sm
);
}
}
else
{
sm
=
git_strmap_value_at
(
smcfg
,
pos
);
sm
=
git_strmap_value_at
(
cache
->
submodules
,
pos
);
}
done:
...
...
@@ -1254,10 +1353,10 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
static
int
submodule_load_from_config
(
const
git_config_entry
*
entry
,
void
*
payload
)
{
git_repository
*
repo
=
payload
;
git_strmap
*
smcfg
=
repo
->
submodules
;
const
char
*
namestart
,
*
property
,
*
alternate
=
NULL
;
git_submodule_cache
*
cache
=
payload
;
const
char
*
namestart
,
*
property
;
const
char
*
key
=
entry
->
name
,
*
value
=
entry
->
value
,
*
path
;
char
*
alternate
=
NULL
,
*
replaced
=
NULL
;
git_buf
name
=
GIT_BUF_INIT
;
git_submodule
*
sm
=
NULL
;
int
error
=
0
;
...
...
@@ -1275,7 +1374,7 @@ static int submodule_load_from_config(
path
=
!
strcasecmp
(
property
,
"path"
)
?
value
:
NULL
;
if
((
error
=
git_buf_set
(
&
name
,
namestart
,
property
-
namestart
-
1
))
<
0
||
(
error
=
submodule_get
(
&
sm
,
repo
,
name
.
ptr
,
path
))
<
0
)
(
error
=
submodule_get
(
&
sm
,
cache
,
name
.
ptr
,
path
))
<
0
)
goto
done
;
sm
->
flags
|=
GIT_SUBMODULE_STATUS_IN_CONFIG
;
...
...
@@ -1288,20 +1387,42 @@ static int submodule_load_from_config(
* should be strcasecmp
*/
if
(
strcmp
(
sm
->
name
,
name
.
ptr
)
!=
0
)
{
alternate
=
sm
->
name
=
git_buf_detach
(
&
name
);
}
else
if
(
path
&&
strcmp
(
path
,
sm
->
path
)
!=
0
)
{
alternate
=
sm
->
path
=
git__strdup
(
value
);
if
(
!
sm
->
path
)
{
error
=
-
1
;
goto
done
;
if
(
strcmp
(
sm
->
name
,
name
.
ptr
)
!=
0
)
{
/* name changed */
if
(
!
strcmp
(
sm
->
path
,
name
.
ptr
))
{
/* already set as path */
replaced
=
sm
->
name
;
sm
->
name
=
sm
->
path
;
}
else
{
if
(
sm
->
name
!=
sm
->
path
)
replaced
=
sm
->
name
;
alternate
=
sm
->
name
=
git_buf_detach
(
&
name
);
}
}
else
if
(
path
&&
strcmp
(
path
,
sm
->
path
)
!=
0
)
{
/* path changed */
if
(
!
strcmp
(
sm
->
name
,
value
))
{
/* already set as name */
replaced
=
sm
->
path
;
sm
->
path
=
sm
->
name
;
}
else
{
if
(
sm
->
path
!=
sm
->
name
)
replaced
=
sm
->
path
;
if
((
alternate
=
git__strdup
(
value
))
==
NULL
)
{
error
=
-
1
;
goto
done
;
}
sm
->
path
=
alternate
;
}
}
/* Found a alternate key for the submodule */
/* Deregister under name being replaced */
if
(
replaced
)
{
git_strmap_delete
(
cache
->
submodules
,
replaced
);
git_submodule_free
(
sm
);
git__free
(
replaced
);
}
/* Insert under alternate key */
if
(
alternate
)
{
void
*
old_sm
=
NULL
;
git_strmap_insert2
(
smcfg
,
alternate
,
sm
,
old_sm
,
error
);
git_strmap_insert2
(
cache
->
submodules
,
alternate
,
sm
,
old_sm
,
error
);
if
(
error
<
0
)
goto
done
;
...
...
@@ -1383,36 +1504,33 @@ static int submodule_load_from_wd_lite(git_submodule *sm)
return
0
;
}
static
int
load_submodule_config
_from_index
(
git_
repository
*
repo
,
git_oid
*
gitmodules_oid
)
static
int
submodule_cache_refresh
_from_index
(
git_
submodule_cache
*
cache
,
git_index
*
idx
)
{
int
error
;
git_index
*
index
;
git_iterator
*
i
;
const
git_index_entry
*
entry
;
if
((
error
=
git_repository_index__weakptr
(
&
index
,
repo
))
<
0
||
(
error
=
git_iterator_for_index
(
&
i
,
index
,
0
,
NULL
,
NULL
))
<
0
)
if
((
error
=
git_iterator_for_index
(
&
i
,
idx
,
0
,
NULL
,
NULL
))
<
0
)
return
error
;
while
(
!
(
error
=
git_iterator_advance
(
&
entry
,
i
)))
{
khiter_t
pos
=
git_strmap_lookup_index
(
repo
->
submodules
,
entry
->
path
);
khiter_t
pos
=
git_strmap_lookup_index
(
cache
->
submodules
,
entry
->
path
);
git_submodule
*
sm
;
if
(
git_strmap_valid_index
(
repo
->
submodules
,
pos
))
{
sm
=
git_strmap_value_at
(
repo
->
submodules
,
pos
);
if
(
git_strmap_valid_index
(
cache
->
submodules
,
pos
))
{
sm
=
git_strmap_value_at
(
cache
->
submodules
,
pos
);
if
(
S_ISGITLINK
(
entry
->
mode
))
submodule_update_from_index_entry
(
sm
,
entry
);
else
sm
->
flags
|=
GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE
;
}
else
if
(
S_ISGITLINK
(
entry
->
mode
))
{
if
(
!
submodule_get
(
&
sm
,
repo
,
entry
->
path
,
NULL
))
{
if
(
!
submodule_get
(
&
sm
,
cache
,
entry
->
path
,
NULL
))
{
submodule_update_from_index_entry
(
sm
,
entry
);
git_submodule_free
(
sm
);
}
}
else
if
(
strcmp
(
entry
->
path
,
GIT_MODULES_FILE
)
==
0
)
git_oid_cpy
(
gitmodules_oid
,
&
entry
->
id
);
}
}
if
(
error
==
GIT_ITEROVER
)
...
...
@@ -1423,46 +1541,33 @@ static int load_submodule_config_from_index(
return
error
;
}
static
int
load_submodule_config
_from_head
(
git_
repository
*
repo
,
git_oid
*
gitmodules_oi
d
)
static
int
submodule_cache_refresh
_from_head
(
git_
submodule_cache
*
cache
,
git_tree
*
hea
d
)
{
int
error
;
git_tree
*
head
;
git_iterator
*
i
;
const
git_index_entry
*
entry
;
/* if we can't look up current head, then there's no submodule in it */
if
(
git_repository_head_tree
(
&
head
,
repo
)
<
0
)
{
giterr_clear
();
return
0
;
}
if
((
error
=
git_iterator_for_tree
(
&
i
,
head
,
0
,
NULL
,
NULL
))
<
0
)
{
git_tree_free
(
head
);
if
((
error
=
git_iterator_for_tree
(
&
i
,
head
,
0
,
NULL
,
NULL
))
<
0
)
return
error
;
}
while
(
!
(
error
=
git_iterator_advance
(
&
entry
,
i
)))
{
khiter_t
pos
=
git_strmap_lookup_index
(
repo
->
submodules
,
entry
->
path
);
khiter_t
pos
=
git_strmap_lookup_index
(
cache
->
submodules
,
entry
->
path
);
git_submodule
*
sm
;
if
(
git_strmap_valid_index
(
repo
->
submodules
,
pos
))
{
sm
=
git_strmap_value_at
(
repo
->
submodules
,
pos
);
if
(
git_strmap_valid_index
(
cache
->
submodules
,
pos
))
{
sm
=
git_strmap_value_at
(
cache
->
submodules
,
pos
);
if
(
S_ISGITLINK
(
entry
->
mode
))
submodule_update_from_head_data
(
sm
,
entry
->
mode
,
&
entry
->
id
);
submodule_update_from_head_data
(
sm
,
entry
->
mode
,
&
entry
->
id
);
else
sm
->
flags
|=
GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE
;
}
else
if
(
S_ISGITLINK
(
entry
->
mode
))
{
if
(
!
submodule_get
(
&
sm
,
repo
,
entry
->
path
,
NULL
))
{
if
(
!
submodule_get
(
&
sm
,
cache
,
entry
->
path
,
NULL
))
{
submodule_update_from_head_data
(
sm
,
entry
->
mode
,
&
entry
->
id
);
git_submodule_free
(
sm
);
}
}
else
if
(
strcmp
(
entry
->
path
,
GIT_MODULES_FILE
)
==
0
&&
git_oid_iszero
(
gitmodules_oid
))
{
git_oid_cpy
(
gitmodules_oid
,
&
entry
->
id
);
}
}
...
...
@@ -1470,17 +1575,15 @@ static int load_submodule_config_from_head(
error
=
0
;
git_iterator_free
(
i
);
git_tree_free
(
head
);
return
error
;
}
static
git_config_backend
*
open_gitmodules
(
git_repository
*
repo
,
bool
okay_to_create
,
const
git_oid
*
gitmodules_oid
)
git_submodule_cache
*
cache
,
int
okay_to_create
)
{
const
char
*
workdir
=
git_repository_workdir
(
repo
);
const
char
*
workdir
=
git_repository_workdir
(
cache
->
repo
);
git_buf
path
=
GIT_BUF_INIT
;
git_config_backend
*
mods
=
NULL
;
...
...
@@ -1500,186 +1603,281 @@ static git_config_backend *open_gitmodules(
}
}
if
(
!
mods
&&
gitmodules_oid
&&
!
git_oid_iszero
(
gitmodules_oid
))
{
/* TODO: Retrieve .gitmodules content from ODB */
git_buf_free
(
&
path
);
/* Should we actually do this? Core git does not, but it means you
* can't really get much information about submodules on bare repos.
*/
return
mods
;
}
static
void
submodule_cache_free
(
git_submodule_cache
*
cache
)
{
git_submodule
*
sm
;
if
(
!
cache
)
return
;
git_strmap_foreach_value
(
cache
->
submodules
,
sm
,
{
sm
->
repo
=
NULL
;
/* disconnect from repo */
git_submodule_free
(
sm
);
});
git_strmap_free
(
cache
->
submodules
);
git_buf_free
(
&
cache
->
gitmodules_path
);
git_mutex_free
(
&
cache
->
lock
);
git__free
(
cache
);
}
static
int
submodule_cache_alloc
(
git_submodule_cache
**
out
,
git_repository
*
repo
)
{
git_submodule_cache
*
cache
=
git__calloc
(
1
,
sizeof
(
git_submodule_cache
));
GITERR_CHECK_ALLOC
(
cache
);
if
(
git_mutex_init
(
&
cache
->
lock
)
<
0
)
{
giterr_set
(
GITERR_OS
,
"Unable to initialize submodule cache lock"
);
git__free
(
cache
);
return
-
1
;
}
git_buf_free
(
&
path
);
cache
->
submodules
=
git_strmap_alloc
();
if
(
!
cache
->
submodules
)
{
submodule_cache_free
(
cache
);
return
-
1
;
}
return
mods
;
cache
->
repo
=
repo
;
git_buf_init
(
&
cache
->
gitmodules_path
,
0
);
*
out
=
cache
;
return
0
;
}
static
int
load_submodule_config
(
git_repository
*
repo
,
bool
reload
)
static
int
submodule_cache_refresh
(
git_submodule_cache
*
cache
,
int
refresh
)
{
int
error
;
git_oid
gitmodules_oid
;
int
error
=
0
,
update_index
,
update_head
,
update_gitmod
;
git_index
*
idx
=
NULL
;
git_tree
*
head
=
NULL
;
const
char
*
wd
=
NULL
;
git_buf
path
=
GIT_BUF_INIT
;
git_submodule
*
sm
;
git_config_backend
*
mods
=
NULL
;
uint32_t
mask
;
if
(
!
reload
&&
repo
->
submodules
)
if
(
!
cache
||
!
cache
->
repo
||
!
refresh
)
return
0
;
memset
(
&
gitmodules_oid
,
0
,
sizeof
(
gitmodules_oid
));
/* Submodule data is kept in a hashtable keyed by both name and path.
* These are usually the same, but that is not guaranteed.
*/
if
(
!
repo
->
submodules
)
{
repo
->
submodules
=
git_strmap_alloc
();
GITERR_CHECK_ALLOC
(
repo
->
submodules
);
if
(
git_mutex_lock
(
&
cache
->
lock
)
<
0
)
{
giterr_set
(
GITERR_OS
,
"Unable to acquire lock on submodule cache"
);
return
-
1
;
}
/*
TODO: only do the following if the sources appear modified
*/
/*
get sources that we will need to check
*/
/* add submodule information from index */
if
(
git_repository_index
(
&
idx
,
cache
->
repo
)
<
0
)
giterr_clear
();
if
(
git_repository_head_tree
(
&
head
,
cache
->
repo
)
<
0
)
giterr_clear
();
if
((
error
=
load_submodule_config_from_index
(
repo
,
&
gitmodules_oid
))
<
0
)
wd
=
git_repository_workdir
(
cache
->
repo
);
if
(
wd
&&
(
error
=
git_buf_joinpath
(
&
path
,
wd
,
GIT_MODULES_FILE
))
<
0
)
goto
cleanup
;
/* check for invalidation */
if
(
refresh
==
CACHE_FLUSH
)
update_index
=
update_head
=
update_gitmod
=
true
;
else
{
update_index
=
!
idx
||
git_index__changed_relative_to
(
idx
,
&
cache
->
index_stamp
);
update_head
=
!
head
||
!
git_oid_equal
(
&
cache
->
head_id
,
git_tree_id
(
head
));
update_gitmod
=
(
wd
!=
NULL
)
?
git_futils_filestamp_check
(
&
cache
->
gitmodules_stamp
,
path
.
ptr
)
:
(
cache
->
gitmodules_stamp
.
mtime
!=
0
);
if
(
update_gitmod
<
0
)
giterr_clear
();
}
/* clear submodule flags that are to be refreshed */
mask
=
0
;
if
(
!
idx
||
update_index
)
mask
|=
GIT_SUBMODULE_STATUS_IN_INDEX
|
GIT_SUBMODULE_STATUS__INDEX_FLAGS
|
GIT_SUBMODULE_STATUS__INDEX_OID_VALID
|
GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES
;
if
(
!
head
||
update_head
)
mask
|=
GIT_SUBMODULE_STATUS_IN_HEAD
|
GIT_SUBMODULE_STATUS__HEAD_OID_VALID
;
if
(
update_gitmod
)
mask
|=
GIT_SUBMODULE_STATUS_IN_CONFIG
;
if
(
mask
!=
0
)
mask
|=
GIT_SUBMODULE_STATUS_IN_WD
|
GIT_SUBMODULE_STATUS__WD_SCANNED
|
GIT_SUBMODULE_STATUS__WD_FLAGS
|
GIT_SUBMODULE_STATUS__WD_OID_VALID
;
else
goto
cleanup
;
/* nothing to do */
submodule_cache_clear_flags
(
cache
,
mask
);
/* add back submodule information from index */
if
(
idx
&&
update_index
)
{
if
((
error
=
submodule_cache_refresh_from_index
(
cache
,
idx
))
<
0
)
goto
cleanup
;
git_futils_filestamp_set
(
&
cache
->
index_stamp
,
git_index__filestamp
(
idx
));
}
/* add submodule information from HEAD */
if
((
error
=
load_submodule_config_from_head
(
repo
,
&
gitmodules_oid
))
<
0
)
goto
cleanup
;
if
(
head
&&
update_head
)
{
if
((
error
=
submodule_cache_refresh_from_head
(
cache
,
head
))
<
0
)
goto
cleanup
;
git_oid_cpy
(
&
cache
->
head_id
,
git_tree_id
(
head
));
}
/* add submodule information from .gitmodules */
if
((
mods
=
open_gitmodules
(
repo
,
false
,
&
gitmodules_oid
))
!=
NULL
&&
(
error
=
git_config_file_foreach
(
mods
,
submodule_load_from_config
,
repo
))
<
0
)
goto
cleanup
;
if
(
wd
&&
update_gitmod
>
0
)
{
if
((
mods
=
open_gitmodules
(
cache
,
false
))
!=
NULL
&&
(
error
=
git_config_file_foreach
(
mods
,
submodule_load_from_config
,
cache
))
<
0
)
goto
cleanup
;
}
/* shallow scan submodules in work tree */
/* shallow scan submodules in work tree
as needed
*/
if
(
!
git_repository_is_bare
(
repo
))
{
git_submodule
*
sm
;
git_strmap_foreach_value
(
repo
->
submodules
,
sm
,
{
sm
->
flags
&=
~
GIT_SUBMODULE_STATUS__ALL_WD_FLAGS
;
});
git_strmap_foreach_value
(
repo
->
submodules
,
sm
,
{
if
(
wd
&&
mask
!=
0
)
{
git_strmap_foreach_value
(
cache
->
submodules
,
sm
,
{
submodule_load_from_wd_lite
(
sm
);
});
}
/* remove submodules that no longer exist */
git_strmap_foreach_value
(
cache
->
submodules
,
sm
,
{
/* purge unless in HEAD, index, or .gitmodules; no sm for wd only */
if
(
sm
!=
NULL
&&
!
(
sm
->
flags
&
(
GIT_SUBMODULE_STATUS_IN_HEAD
|
GIT_SUBMODULE_STATUS_IN_INDEX
|
GIT_SUBMODULE_STATUS_IN_CONFIG
)))
submodule_cache_remove_item
(
cache
,
sm
,
true
);
});
cleanup:
if
(
mods
!=
NULL
)
git_config_file_free
(
mods
);
git_config_file_free
(
mods
);
/* TODO: if we got an error, mark submodule config as invalid? */
if
(
error
)
git_submodule_config_free
(
repo
);
git_mutex_unlock
(
&
cache
->
lock
);
git_index_free
(
idx
);
git_tree_free
(
head
);
git_buf_free
(
&
path
);
return
error
;
}
static
int
lookup_head_remote
(
git_buf
*
url
,
git_repository
*
repo
)
static
int
submodule_cache_init
(
git_repository
*
repo
,
int
cache_refresh
)
{
int
error
;
git_config
*
cfg
;
git_reference
*
head
=
NULL
,
*
remote
=
NULL
;
const
char
*
tgt
,
*
scan
;
git_buf
key
=
GIT_BUF_INIT
;
int
error
=
0
;
git_submodule_cache
*
cache
=
NULL
;
/* 1. resolve HEAD -> refs/heads/BRANCH
* 2. lookup config branch.BRANCH.remote -> ORIGIN
* 3. lookup remote.ORIGIN.url
*/
/* if submodules already exist, just refresh as requested */
if
(
repo
->
_submodules
)
return
submodule_cache_refresh
(
repo
->
_submodules
,
cache_refresh
);
if
((
error
=
git_repository_config__weakptr
(
&
cfg
,
repo
))
<
0
)
return
error
;
/* otherwise create a new cache, load it, and atomically swap it in */
if
(
!
(
error
=
submodule_cache_alloc
(
&
cache
,
repo
))
&&
!
(
error
=
submodule_cache_refresh
(
cache
,
CACHE_FLUSH
)))
cache
=
git__compare_and_swap
(
&
repo
->
_submodules
,
NULL
,
cache
);
if
(
git_reference_lookup
(
&
head
,
repo
,
GIT_HEAD_FILE
)
<
0
)
{
giterr_set
(
GITERR_SUBMODULE
,
"Cannot resolve relative URL when HEAD cannot be resolved"
);
error
=
GIT_ENOTFOUND
;
goto
cleanup
;
}
/* might have raced with another thread to set cache, so free if needed */
if
(
cache
)
submodule_cache_free
(
cache
);
if
(
git_reference_type
(
head
)
!=
GIT_REF_SYMBOLIC
)
{
giterr_set
(
GITERR_SUBMODULE
,
"Cannot resolve relative URL when HEAD is not symbolic"
);
error
=
GIT_ENOTFOUND
;
goto
cleanup
;
}
return
error
;
}
if
((
error
=
git_branch_upstream
(
&
remote
,
head
))
<
0
)
goto
cleanup
;
/* Lookup name of remote of the local tracking branch HEAD points to */
static
int
lookup_head_remote_key
(
git_buf
*
remote_name
,
git_repository
*
repo
)
{
int
error
;
git_reference
*
head
=
NULL
;
git_buf
upstream_name
=
GIT_BUF_INIT
;
/* remote should refer to something like refs/remotes/ORIGIN/BRANCH */
/* lookup and dereference HEAD */
if
((
error
=
git_repository_head
(
&
head
,
repo
))
<
0
)
return
error
;
if
(
git_reference_type
(
remote
)
!=
GIT_REF_SYMBOLIC
||
git__prefixcmp
(
git_reference_symbolic_target
(
remote
),
GIT_REFS_REMOTES_DIR
)
!=
0
)
/* lookup remote tracking branch of HEAD */
if
(
!
(
error
=
git_branch_upstream_name
(
&
upstream_name
,
repo
,
git_reference_name
(
head
))))
{
giterr_set
(
GITERR_SUBMODULE
,
"Cannot resolve relative URL when HEAD is not symbolic"
);
error
=
GIT_ENOTFOUND
;
g
oto
cleanup
;
/* lookup remote of remote tracking branch */
error
=
git_branch_remote_name
(
remote_name
,
repo
,
upstream_name
.
ptr
);
g
it_buf_free
(
&
upstream_name
)
;
}
scan
=
tgt
=
git_reference_symbolic_target
(
remote
)
+
strlen
(
GIT_REFS_REMOTES_DIR
);
while
(
*
scan
&&
(
*
scan
!=
'/'
||
(
scan
>
tgt
&&
scan
[
-
1
]
!=
'\\'
)))
scan
++
;
/* find non-escaped slash to end ORIGIN name */
git_reference_free
(
head
);
error
=
git_buf_printf
(
&
key
,
"remote.%.*s.url"
,
(
int
)(
scan
-
tgt
),
tgt
);
if
(
error
<
0
)
goto
cleanup
;
return
error
;
}
if
((
error
=
git_config_get_string
(
&
tgt
,
cfg
,
key
.
ptr
))
<
0
)
goto
cleanup
;
/* Lookup the remote of the local tracking branch HEAD points to */
static
int
lookup_head_remote
(
git_remote
**
remote
,
git_repository
*
repo
)
{
int
error
;
git_buf
remote_name
=
GIT_BUF_INIT
;
error
=
git_buf_sets
(
url
,
tgt
);
/* lookup remote of remote tracking branch name */
if
(
!
(
error
=
lookup_head_remote_key
(
&
remote_name
,
repo
)))
error
=
git_remote_load
(
remote
,
repo
,
remote_name
.
ptr
);
cleanup:
git_buf_free
(
&
key
);
git_reference_free
(
head
);
git_reference_free
(
remote
);
git_buf_free
(
&
remote_name
);
return
error
;
}
static
int
submodule_update_config
(
git_submodule
*
submodule
,
const
char
*
attr
,
const
char
*
value
,
bool
overwrite
,
bool
only_existing
)
/* Lookup remote, either from HEAD or fall back on origin */
static
int
lookup_default_remote
(
git_remote
**
remote
,
git_repository
*
repo
)
{
int
error
;
git_config
*
config
;
git_buf
key
=
GIT_BUF_INIT
;
const
git_config_entry
*
ce
=
NULL
;
int
error
=
lookup_head_remote
(
remote
,
repo
);
assert
(
submodule
);
/* if that failed, use 'origin' instead */
if
(
error
==
GIT_ENOTFOUND
)
error
=
git_remote_load
(
remote
,
repo
,
"origin"
);
error
=
git_repository_config__weakptr
(
&
config
,
submodule
->
repo
);
if
(
error
<
0
)
return
error
;
if
(
error
==
GIT_ENOTFOUND
)
giterr_set
(
GITERR_SUBMODULE
,
"Cannot get default remote for submodule - no local tracking "
"branch for HEAD and origin does not exist"
);
error
=
git_buf_printf
(
&
key
,
"submodule.%s.%s"
,
submodule
->
name
,
attr
);
if
(
error
<
0
)
goto
cleanup
;
if
((
error
=
git_config__lookup_entry
(
&
ce
,
config
,
key
.
ptr
,
false
))
<
0
)
goto
cleanup
;
return
error
;
}
if
(
!
ce
&&
only_existing
)
goto
cleanup
;
if
(
ce
&&
!
overwrite
)
goto
cleanup
;
if
(
value
&&
ce
&&
ce
->
value
&&
!
strcmp
(
ce
->
value
,
value
))
goto
cleanup
;
if
(
!
value
&&
(
!
ce
||
!
ce
->
value
))
goto
cleanup
;
static
int
get_url_base
(
git_buf
*
url
,
git_repository
*
repo
)
{
int
error
;
git_remote
*
remote
=
NULL
;
if
(
!
value
)
error
=
git_config_delete_entry
(
config
,
key
.
ptr
);
else
error
=
git_config_set_string
(
config
,
key
.
ptr
,
value
);
if
(
!
(
error
=
lookup_default_remote
(
&
remote
,
repo
)))
{
error
=
git_buf_sets
(
url
,
git_remote_url
(
remote
));
git_remote_free
(
remote
);
}
else
if
(
error
==
GIT_ENOTFOUND
)
{
/* if repository does not have a default remote, use workdir instead */
giterr_clear
();
error
=
git_buf_sets
(
url
,
git_repository_workdir
(
repo
));
}
cleanup:
git_buf_free
(
&
key
);
return
error
;
}
...
...
src/submodule.h
View file @
923c8400
...
...
@@ -99,6 +99,29 @@ struct git_submodule {
git_oid
wd_oid
;
};
/**
* The git_submodule_cache stores known submodules along with timestamps,
* etc. about when they were loaded
*/
typedef
struct
{
git_repository
*
repo
;
git_strmap
*
submodules
;
git_mutex
lock
;
/* cache invalidation data */
git_oid
head_id
;
git_futils_filestamp
index_stamp
;
git_buf
gitmodules_path
;
git_futils_filestamp
gitmodules_stamp
;
git_futils_filestamp
config_stamp
;
}
git_submodule_cache
;
/* Force revalidation of submodule data cache (alloc as needed) */
extern
int
git_submodule_cache_refresh
(
git_repository
*
repo
);
/* Release all submodules */
extern
void
git_submodule_cache_free
(
git_repository
*
repo
);
/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
enum
{
GIT_SUBMODULE_STATUS__WD_SCANNED
=
(
1u
<<
20
),
...
...
@@ -111,17 +134,16 @@ enum {
GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES
=
(
1u
<<
27
),
};
#define GIT_SUBMODULE_STATUS__ALL_WD_FLAGS \
(GIT_SUBMODULE_STATUS_IN_WD | \
GIT_SUBMODULE_STATUS__WD_OID_VALID | \
GIT_SUBMODULE_STATUS__WD_FLAGS)
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20))
/* Internal submodule check does not attempt to refresh cached data */
extern
bool
git_submodule__is_submodule
(
git_repository
*
repo
,
const
char
*
name
);
/* Internal lookup does not attempt to refresh cached data */
extern
int
git_submodule__lookup
(
git_submodule
**
out
,
git_repository
*
repo
,
const
char
*
path
);
/* Internal status fn returns status and optionally the various OIDs */
extern
int
git_submodule__status
(
unsigned
int
*
out_status
,
...
...
@@ -143,5 +165,6 @@ extern int git_submodule_parse_update(
extern
const
char
*
git_submodule_ignore_to_str
(
git_submodule_ignore_t
);
extern
const
char
*
git_submodule_update_to_str
(
git_submodule_update_t
);
extern
const
char
*
git_submodule_recurse_to_str
(
git_submodule_recurse_t
);
#endif
src/vector.c
View file @
923c8400
...
...
@@ -54,7 +54,7 @@ int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
bytes
=
src
->
length
*
sizeof
(
void
*
);
v
->
_alloc_size
=
src
->
length
;
v
->
_cmp
=
cmp
;
v
->
_cmp
=
cmp
?
cmp
:
src
->
_cmp
;
v
->
length
=
src
->
length
;
v
->
flags
=
src
->
flags
;
if
(
cmp
!=
src
->
_cmp
)
...
...
tests/core/buffer.c
View file @
923c8400
...
...
@@ -599,6 +599,38 @@ void test_core_buffer__10(void)
git_buf_free
(
&
a
);
}
void
test_core_buffer__join3
(
void
)
{
git_buf
a
=
GIT_BUF_INIT
;
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
"test"
,
"string"
,
"join"
));
cl_assert_equal_s
(
"test/string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
"test/"
,
"string"
,
"join"
));
cl_assert_equal_s
(
"test/string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
"test/"
,
"/string"
,
"join"
));
cl_assert_equal_s
(
"test/string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
"test/"
,
"/string/"
,
"join"
));
cl_assert_equal_s
(
"test/string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
"test/"
,
"/string/"
,
"/join"
));
cl_assert_equal_s
(
"test/string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
""
,
"string"
,
"join"
));
cl_assert_equal_s
(
"string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
""
,
"string/"
,
"join"
));
cl_assert_equal_s
(
"string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
""
,
"string/"
,
"/join"
));
cl_assert_equal_s
(
"string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
"string"
,
""
,
"join"
));
cl_assert_equal_s
(
"string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
"string/"
,
""
,
"join"
));
cl_assert_equal_s
(
"string/join"
,
a
.
ptr
);
cl_git_pass
(
git_buf_join3
(
&
a
,
'/'
,
"string/"
,
""
,
"/join"
));
cl_assert_equal_s
(
"string/join"
,
a
.
ptr
);
git_buf_free
(
&
a
);
}
void
test_core_buffer__11
(
void
)
{
git_buf
a
=
GIT_BUF_INIT
;
...
...
tests/diff/submodules.c
View file @
923c8400
...
...
@@ -131,8 +131,6 @@ void test_diff_submodules__dirty_submodule_2(void)
g_repo
=
setup_fixture_submodules
();
cl_git_pass
(
git_submodule_reload_all
(
g_repo
,
1
));
opts
.
flags
=
GIT_DIFF_INCLUDE_UNTRACKED
|
GIT_DIFF_SHOW_UNTRACKED_CONTENT
|
GIT_DIFF_RECURSE_UNTRACKED_DIRS
|
...
...
@@ -165,8 +163,6 @@ void test_diff_submodules__dirty_submodule_2(void)
git_diff_free
(
diff
);
cl_git_pass
(
git_submodule_reload_all
(
g_repo
,
1
));
cl_git_pass
(
git_diff_index_to_workdir
(
&
diff
,
g_repo
,
NULL
,
&
opts
));
check_diff_patches
(
diff
,
expected_dirty
);
git_diff_free
(
diff
);
...
...
@@ -299,7 +295,6 @@ void test_diff_submodules__invalid_cache(void)
git_submodule_free
(
sm
);
cl_git_pass
(
git_submodule_reload_all
(
g_repo
,
1
));
cl_git_pass
(
git_submodule_lookup
(
&
sm
,
g_repo
,
smpath
));
cl_git_pass
(
git_diff_index_to_workdir
(
&
diff
,
g_repo
,
NULL
,
&
opts
));
...
...
tests/submodule/add.c
0 → 100644
View file @
923c8400
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
static
git_repository
*
g_repo
=
NULL
;
void
test_submodule_add__cleanup
(
void
)
{
cl_git_sandbox_cleanup
();
}
static
void
assert_submodule_url
(
const
char
*
name
,
const
char
*
url
)
{
git_config
*
cfg
;
const
char
*
s
;
git_buf
key
=
GIT_BUF_INIT
;
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_buf_printf
(
&
key
,
"submodule.%s.url"
,
name
));
cl_git_pass
(
git_config_get_string
(
&
s
,
cfg
,
git_buf_cstr
(
&
key
)));
cl_assert_equal_s
(
s
,
url
);
git_config_free
(
cfg
);
git_buf_free
(
&
key
);
}
void
test_submodule_add__url_absolute
(
void
)
{
git_submodule
*
sm
;
g_repo
=
setup_fixture_submod2
();
/* re-add existing submodule */
cl_git_fail_with
(
GIT_EEXISTS
,
git_submodule_add_setup
(
NULL
,
g_repo
,
"whatever"
,
"sm_unchanged"
,
1
));
/* add a submodule using a gitlink */
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"https://github.com/libgit2/libgit2.git"
,
"sm_libgit2"
,
1
)
);
git_submodule_free
(
sm
);
cl_assert
(
git_path_isfile
(
"submod2/"
"sm_libgit2"
"/.git"
));
cl_assert
(
git_path_isdir
(
"submod2/.git/modules"
));
cl_assert
(
git_path_isdir
(
"submod2/.git/modules/"
"sm_libgit2"
));
cl_assert
(
git_path_isfile
(
"submod2/.git/modules/"
"sm_libgit2"
"/HEAD"
));
assert_submodule_url
(
"sm_libgit2"
,
"https://github.com/libgit2/libgit2.git"
);
/* add a submodule not using a gitlink */
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"https://github.com/libgit2/libgit2.git"
,
"sm_libgit2b"
,
0
)
);
git_submodule_free
(
sm
);
cl_assert
(
git_path_isdir
(
"submod2/"
"sm_libgit2b"
"/.git"
));
cl_assert
(
git_path_isfile
(
"submod2/"
"sm_libgit2b"
"/.git/HEAD"
));
cl_assert
(
!
git_path_exists
(
"submod2/.git/modules/"
"sm_libgit2b"
));
assert_submodule_url
(
"sm_libgit2b"
,
"https://github.com/libgit2/libgit2.git"
);
}
void
test_submodule_add__url_relative
(
void
)
{
git_submodule
*
sm
;
git_remote
*
remote
;
/* default remote url is https://github.com/libgit2/false.git */
g_repo
=
cl_git_sandbox_init
(
"testrepo2"
);
/* make sure we don't default to origin - rename origin -> test_remote */
cl_git_pass
(
git_remote_load
(
&
remote
,
g_repo
,
"origin"
));
cl_git_pass
(
git_remote_rename
(
remote
,
"test_remote"
,
NULL
,
NULL
));
cl_git_fail
(
git_remote_load
(
&
remote
,
g_repo
,
"origin"
));
git_remote_free
(
remote
);
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"../TestGitRepository"
,
"TestGitRepository"
,
1
)
);
git_submodule_free
(
sm
);
assert_submodule_url
(
"TestGitRepository"
,
"https://github.com/libgit2/TestGitRepository"
);
}
void
test_submodule_add__url_relative_to_origin
(
void
)
{
git_submodule
*
sm
;
/* default remote url is https://github.com/libgit2/false.git */
g_repo
=
cl_git_sandbox_init
(
"testrepo2"
);
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"../TestGitRepository"
,
"TestGitRepository"
,
1
)
);
git_submodule_free
(
sm
);
assert_submodule_url
(
"TestGitRepository"
,
"https://github.com/libgit2/TestGitRepository"
);
}
void
test_submodule_add__url_relative_to_workdir
(
void
)
{
git_submodule
*
sm
;
/* In this repo, HEAD (master) has no remote tracking branc h*/
g_repo
=
cl_git_sandbox_init
(
"testrepo"
);
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"./"
,
"TestGitRepository"
,
1
)
);
git_submodule_free
(
sm
);
assert_submodule_url
(
"TestGitRepository"
,
git_repository_workdir
(
g_repo
));
}
tests/submodule/lookup.c
View file @
923c8400
#include "clar_libgit2.h"
#include "submodule_helpers.h"
#include "posix.h"
#include "git2/sys/repository.h"
#include "fileops.h"
static
git_repository
*
g_repo
=
NULL
;
...
...
@@ -115,13 +115,7 @@ void test_submodule_lookup__lookup_even_with_unborn_head(void)
&
head
,
g_repo
,
"HEAD"
,
"refs/heads/garbage"
,
1
,
NULL
,
NULL
));
git_reference_free
(
head
);
assert_submodule_exists
(
g_repo
,
"sm_unchanged"
);
assert_submodule_exists
(
g_repo
,
"sm_added_and_uncommited"
);
assert_submodule_exists
(
g_repo
,
"sm_gitmodules_only"
);
refute_submodule_exists
(
g_repo
,
"not-submodule"
,
GIT_EEXISTS
);
refute_submodule_exists
(
g_repo
,
"just_a_dir"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"just_a_file"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"no_such_file"
,
GIT_ENOTFOUND
);
test_submodule_lookup__simple_lookup
();
/* baseline should still pass */
}
void
test_submodule_lookup__lookup_even_with_missing_index
(
void
)
...
...
@@ -133,44 +127,145 @@ void test_submodule_lookup__lookup_even_with_missing_index(void)
git_repository_set_index
(
g_repo
,
idx
);
git_index_free
(
idx
);
test_submodule_lookup__simple_lookup
();
/* baseline should still pass */
}
static
void
baseline_tests
(
void
)
{
/* small baseline that should work even if we change the index or make
* commits from the index
*/
assert_submodule_exists
(
g_repo
,
"sm_unchanged"
);
assert_submodule_exists
(
g_repo
,
"sm_added_and_uncommited"
);
assert_submodule_exists
(
g_repo
,
"sm_gitmodules_only"
);
refute_submodule_exists
(
g_repo
,
"not-submodule"
,
GIT_EEXISTS
);
refute_submodule_exists
(
g_repo
,
"just_a_dir"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"just_a_file"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"no_such_file"
,
GIT_ENOTFOUND
);
}
void
test_submodule_lookup__just_added
(
void
)
static
void
add_submodule_with_commit
(
const
char
*
name
)
{
git_submodule
*
sm
;
git_repository
*
smrepo
;
git_index
*
idx
;
git_buf
p
=
GIT_BUF_INIT
;
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"https://github.com/libgit2/libgit2.git"
,
name
,
1
));
assert_submodule_exists
(
g_repo
,
name
);
cl_git_pass
(
git_submodule_open
(
&
smrepo
,
sm
));
cl_git_pass
(
git_repository_index
(
&
idx
,
smrepo
));
cl_git_pass
(
git_buf_joinpath
(
&
p
,
git_repository_workdir
(
smrepo
),
"file"
));
cl_git_mkfile
(
p
.
ptr
,
"new file"
);
git_buf_free
(
&
p
);
cl_git_pass
(
git_index_add_bypath
(
idx
,
"file"
));
cl_git_pass
(
git_index_write
(
idx
));
git_index_free
(
idx
);
cl_repo_commit_from_index
(
NULL
,
smrepo
,
NULL
,
0
,
"initial commit"
);
git_repository_free
(
smrepo
);
cl_git_pass
(
git_submodule_add_finalize
(
sm
));
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"https://github.com/libgit2/libgit2.git"
,
"sm_just_added"
,
1
));
git_submodule_free
(
sm
);
}
void
test_submodule_lookup__just_added
(
void
)
{
git_submodule
*
sm
;
git_buf
snap1
=
GIT_BUF_INIT
,
snap2
=
GIT_BUF_INIT
;
git_reference
*
original_head
=
NULL
;
refute_submodule_exists
(
g_repo
,
"sm_just_added"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"sm_just_added_2"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"sm_just_added_idx"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"sm_just_added_head"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"mismatch_name"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"mismatch_path"
,
GIT_ENOTFOUND
);
baseline_tests
();
cl_git_pass
(
git_futils_readbuffer
(
&
snap1
,
"submod2/.gitmodules"
));
cl_git_pass
(
git_repository_head
(
&
original_head
,
g_repo
));
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"https://github.com/libgit2/libgit2.git"
,
"sm_just_added"
,
1
));
git_submodule_free
(
sm
);
assert_submodule_exists
(
g_repo
,
"sm_just_added"
);
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"https://github.com/libgit2/libgit2.git"
,
"sm_just_added_2"
,
1
));
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
"https://github.com/libgit2/libgit2.git"
,
"sm_just_added_2"
,
1
));
assert_submodule_exists
(
g_repo
,
"sm_just_added_2"
);
cl_git_fail
(
git_submodule_add_finalize
(
sm
));
/* fails if no HEAD */
git_submodule_free
(
sm
);
cl_git_append2file
(
"submod2/.gitmodules"
,
"
\n
[submodule
\"
mismatch_name
\"
]
\n\t
path = mismatch_path
\n\t
url = https://example.com/example.git
\n\n
"
);
add_submodule_with_commit
(
"sm_just_added_head"
);
cl_repo_commit_from_index
(
NULL
,
g_repo
,
NULL
,
0
,
"commit new sm to head"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_head"
);
add_submodule_with_commit
(
"sm_just_added_idx"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_idx"
);
cl_git_pass
(
git_submodule_reload_all
(
g_repo
,
1
));
cl_git_pass
(
git_futils_readbuffer
(
&
snap2
,
"submod2/.gitmodules"
));
cl_git_append2file
(
"submod2/.gitmodules"
,
"
\n
[submodule
\"
mismatch_name
\"
]
\n
"
"
\t
path = mismatch_path
\n
"
"
\t
url = https://example.com/example.git
\n\n
"
);
assert_submodule_exists
(
g_repo
,
"mismatch_name"
);
assert_submodule_exists
(
g_repo
,
"mismatch_path"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_2"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_idx"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_head"
);
baseline_tests
();
/* all the regular ones should still be working right, too */
cl_git_rewritefile
(
"submod2/.gitmodules"
,
snap2
.
ptr
);
git_buf_free
(
&
snap2
);
assert_submodule_exists
(
g_repo
,
"sm_unchanged"
);
assert_submodule_exists
(
g_repo
,
"sm_added_and_uncommited"
);
assert_submodule_exists
(
g_repo
,
"sm_gitmodules_only"
);
refute_submodule_exists
(
g_repo
,
"not-submodule"
,
GIT_EEXISTS
);
refute_submodule_exists
(
g_repo
,
"just_a_dir"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"just_a_file"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"no_such_file"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"mismatch_name"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"mismatch_path"
,
GIT_ENOTFOUND
);
assert_submodule_exists
(
g_repo
,
"sm_just_added"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_2"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_idx"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_head"
);
baseline_tests
();
cl_git_rewritefile
(
"submod2/.gitmodules"
,
snap1
.
ptr
);
git_buf_free
(
&
snap1
);
refute_submodule_exists
(
g_repo
,
"mismatch_name"
,
GIT_ENOTFOUND
);
refute_submodule_exists
(
g_repo
,
"mismatch_path"
,
GIT_ENOTFOUND
);
/* note error code change, because add_setup made a repo in the workdir */
refute_submodule_exists
(
g_repo
,
"sm_just_added"
,
GIT_EEXISTS
);
refute_submodule_exists
(
g_repo
,
"sm_just_added_2"
,
GIT_EEXISTS
);
/* these still exist in index and head respectively */
assert_submodule_exists
(
g_repo
,
"sm_just_added_idx"
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_head"
);
baseline_tests
();
{
git_index
*
idx
;
cl_git_pass
(
git_repository_index
(
&
idx
,
g_repo
));
cl_git_pass
(
git_index_remove_bypath
(
idx
,
"sm_just_added_idx"
));
cl_git_pass
(
git_index_remove_bypath
(
idx
,
"sm_just_added_head"
));
cl_git_pass
(
git_index_write
(
idx
));
git_index_free
(
idx
);
}
refute_submodule_exists
(
g_repo
,
"sm_just_added_idx"
,
GIT_EEXISTS
);
assert_submodule_exists
(
g_repo
,
"sm_just_added_head"
);
{
git_signature
*
sig
;
cl_git_pass
(
git_signature_now
(
&
sig
,
"resetter"
,
"resetter@email.com"
));
cl_git_pass
(
git_reference_create
(
NULL
,
g_repo
,
"refs/heads/master"
,
git_reference_target
(
original_head
),
1
,
sig
,
"move head back"
));
git_signature_free
(
sig
);
git_reference_free
(
original_head
);
}
refute_submodule_exists
(
g_repo
,
"sm_just_added_head"
,
GIT_EEXISTS
);
}
tests/submodule/modify.c
View file @
923c8400
...
...
@@ -7,61 +7,12 @@ static git_repository *g_repo = NULL;
#define SM_LIBGIT2_URL "https://github.com/libgit2/libgit2.git"
#define SM_LIBGIT2 "sm_libgit2"
#define SM_LIBGIT2B "sm_libgit2b"
void
test_submodule_modify__initialize
(
void
)
{
g_repo
=
setup_fixture_submod2
();
}
void
test_submodule_modify__add
(
void
)
{
git_submodule
*
sm
;
git_config
*
cfg
;
const
char
*
s
;
/* re-add existing submodule */
cl_assert_equal_i
(
GIT_EEXISTS
,
git_submodule_add_setup
(
NULL
,
g_repo
,
"whatever"
,
"sm_unchanged"
,
1
));
/* add a submodule using a gitlink */
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
SM_LIBGIT2_URL
,
SM_LIBGIT2
,
1
)
);
git_submodule_free
(
sm
);
cl_assert
(
git_path_isfile
(
"submod2/"
SM_LIBGIT2
"/.git"
));
cl_assert
(
git_path_isdir
(
"submod2/.git/modules"
));
cl_assert
(
git_path_isdir
(
"submod2/.git/modules/"
SM_LIBGIT2
));
cl_assert
(
git_path_isfile
(
"submod2/.git/modules/"
SM_LIBGIT2
"/HEAD"
));
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_get_string
(
&
s
,
cfg
,
"submodule."
SM_LIBGIT2
".url"
));
cl_assert_equal_s
(
s
,
SM_LIBGIT2_URL
);
git_config_free
(
cfg
);
/* add a submodule not using a gitlink */
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
g_repo
,
SM_LIBGIT2_URL
,
SM_LIBGIT2B
,
0
)
);
git_submodule_free
(
sm
);
cl_assert
(
git_path_isdir
(
"submod2/"
SM_LIBGIT2B
"/.git"
));
cl_assert
(
git_path_isfile
(
"submod2/"
SM_LIBGIT2B
"/.git/HEAD"
));
cl_assert
(
!
git_path_exists
(
"submod2/.git/modules/"
SM_LIBGIT2B
));
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_get_string
(
&
s
,
cfg
,
"submodule."
SM_LIBGIT2B
".url"
));
cl_assert_equal_s
(
s
,
SM_LIBGIT2_URL
);
git_config_free
(
cfg
);
}
static
int
delete_one_config
(
const
git_config_entry
*
entry
,
void
*
payload
)
{
git_config
*
cfg
=
payload
;
...
...
@@ -118,6 +69,26 @@ static int sync_one_submodule(
return
git_submodule_sync
(
sm
);
}
static
void
assert_submodule_url_is_synced
(
git_submodule
*
sm
,
const
char
*
parent_key
,
const
char
*
child_key
)
{
git_config
*
cfg
;
const
char
*
str
;
git_repository
*
smrepo
;
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_get_string
(
&
str
,
cfg
,
parent_key
));
cl_assert_equal_s
(
git_submodule_url
(
sm
),
str
);
git_config_free
(
cfg
);
cl_git_pass
(
git_submodule_open
(
&
smrepo
,
sm
));
cl_git_pass
(
git_repository_config
(
&
cfg
,
smrepo
));
cl_git_pass
(
git_config_get_string
(
&
str
,
cfg
,
child_key
));
cl_assert_equal_s
(
git_submodule_url
(
sm
),
str
);
git_config_free
(
cfg
);
git_repository_free
(
smrepo
);
}
void
test_submodule_modify__sync
(
void
)
{
git_submodule
*
sm1
,
*
sm2
,
*
sm3
;
...
...
@@ -153,14 +124,12 @@ void test_submodule_modify__sync(void)
cl_git_pass
(
git_submodule_foreach
(
g_repo
,
sync_one_submodule
,
NULL
));
/* check that submodule config is updated */
cl_git_pass
(
git_repository_config
(
&
cfg
,
g_repo
));
cl_git_pass
(
git_config_get_string
(
&
str
,
cfg
,
"submodule."
SM1
".url"
));
cl_assert_equal_s
(
git_submodule_url
(
sm1
),
str
);
cl_git_pass
(
git_config_get_string
(
&
str
,
cfg
,
"submodule."
SM2
".url"
));
cl_assert_equal_s
(
git_submodule_url
(
sm2
),
str
);
cl_git_pass
(
git_config_get_string
(
&
str
,
cfg
,
"submodule."
SM3
".url"
));
cl_assert_equal_s
(
git_submodule_url
(
sm3
),
str
);
git_config_free
(
cfg
);
assert_submodule_url_is_synced
(
sm1
,
"submodule."
SM1
".url"
,
"branch.origin.remote"
);
assert_submodule_url_is_synced
(
sm2
,
"submodule."
SM2
".url"
,
"branch.origin.remote"
);
assert_submodule_url_is_synced
(
sm3
,
"submodule."
SM3
".url"
,
"branch.origin.remote"
);
git_submodule_free
(
sm1
);
git_submodule_free
(
sm2
);
...
...
tests/submodule/nosubs.c
View file @
923c8400
...
...
@@ -2,6 +2,7 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "fileops.h"
void
test_submodule_nosubs__cleanup
(
void
)
{
...
...
@@ -68,7 +69,10 @@ void test_submodule_nosubs__reload_add_reload(void)
cl_git_pass
(
git_submodule_reload_all
(
repo
,
0
));
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
repo
,
"https://github.com/libgit2/libgit2.git"
,
"submodules/libgit2"
,
1
));
/* try one add with a reload (to make sure no errors happen) */
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
repo
,
"https://github.com/libgit2/libgit2.git"
,
"submodules/libgit2"
,
1
));
cl_git_pass
(
git_submodule_reload_all
(
repo
,
0
));
...
...
@@ -78,6 +82,17 @@ void test_submodule_nosubs__reload_add_reload(void)
cl_git_pass
(
git_submodule_lookup
(
&
sm
,
repo
,
"submodules/libgit2"
));
cl_assert_equal_s
(
"submodules/libgit2"
,
git_submodule_name
(
sm
));
git_submodule_free
(
sm
);
/* try one add without a reload (to make sure cache inval works, too) */
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
repo
,
"https://github.com/libgit2/libgit2.git"
,
"libgit2-again"
,
1
));
cl_assert_equal_s
(
"libgit2-again"
,
git_submodule_name
(
sm
));
git_submodule_free
(
sm
);
cl_git_pass
(
git_submodule_lookup
(
&
sm
,
repo
,
"libgit2-again"
));
cl_assert_equal_s
(
"libgit2-again"
,
git_submodule_name
(
sm
));
git_submodule_free
(
sm
);
}
void
test_submodule_nosubs__bad_gitmodules
(
void
)
...
...
@@ -93,3 +108,69 @@ void test_submodule_nosubs__bad_gitmodules(void)
cl_git_pass
(
git_submodule_lookup
(
NULL
,
repo
,
"foobar"
));
cl_assert_equal_i
(
GIT_ENOTFOUND
,
git_submodule_lookup
(
NULL
,
repo
,
"subdir"
));
}
void
test_submodule_nosubs__add_and_delete
(
void
)
{
git_repository
*
repo
=
cl_git_sandbox_init
(
"status"
);
git_submodule
*
sm
;
git_buf
buf
=
GIT_BUF_INIT
;
/* note lack of calls to git_submodule_reload_all - this *should* work */
cl_git_fail
(
git_submodule_lookup
(
NULL
,
repo
,
"libgit2"
));
cl_git_fail
(
git_submodule_lookup
(
NULL
,
repo
,
"submodules/libgit2"
));
/* create */
cl_git_pass
(
git_submodule_add_setup
(
&
sm
,
repo
,
"https://github.com/libgit2/libgit2.git"
,
"submodules/libgit2"
,
1
));
cl_assert_equal_s
(
"submodules/libgit2"
,
git_submodule_name
(
sm
));
cl_assert_equal_s
(
"submodules/libgit2"
,
git_submodule_path
(
sm
));
git_submodule_free
(
sm
);
cl_git_pass
(
git_futils_readbuffer
(
&
buf
,
"status/.gitmodules"
));
cl_assert
(
strstr
(
buf
.
ptr
,
"[submodule
\"
submodules/libgit2
\"
]"
)
!=
NULL
);
cl_assert
(
strstr
(
buf
.
ptr
,
"path = submodules/libgit2"
)
!=
NULL
);
git_buf_free
(
&
buf
);
/* lookup */
cl_git_fail
(
git_submodule_lookup
(
&
sm
,
repo
,
"libgit2"
));
cl_git_pass
(
git_submodule_lookup
(
&
sm
,
repo
,
"submodules/libgit2"
));
cl_assert_equal_s
(
"submodules/libgit2"
,
git_submodule_name
(
sm
));
cl_assert_equal_s
(
"submodules/libgit2"
,
git_submodule_path
(
sm
));
git_submodule_free
(
sm
);
/* update name */
cl_git_rewritefile
(
"status/.gitmodules"
,
"[submodule
\"
libgit2
\"
]
\n
"
" path = submodules/libgit2
\n
"
" url = https://github.com/libgit2/libgit2.git
\n
"
);
cl_git_pass
(
git_submodule_lookup
(
&
sm
,
repo
,
"libgit2"
));
cl_assert_equal_s
(
"libgit2"
,
git_submodule_name
(
sm
));
cl_assert_equal_s
(
"submodules/libgit2"
,
git_submodule_path
(
sm
));
git_submodule_free
(
sm
);
cl_git_pass
(
git_submodule_lookup
(
&
sm
,
repo
,
"submodules/libgit2"
));
git_submodule_free
(
sm
);
/* revert name update */
cl_git_rewritefile
(
"status/.gitmodules"
,
"[submodule
\"
submodules/libgit2
\"
]
\n
"
" path = submodules/libgit2
\n
"
" url = https://github.com/libgit2/libgit2.git
\n
"
);
cl_git_fail
(
git_submodule_lookup
(
&
sm
,
repo
,
"libgit2"
));
cl_git_pass
(
git_submodule_lookup
(
&
sm
,
repo
,
"submodules/libgit2"
));
git_submodule_free
(
sm
);
/* remove completely */
cl_must_pass
(
p_unlink
(
"status/.gitmodules"
));
cl_git_fail
(
git_submodule_lookup
(
&
sm
,
repo
,
"libgit2"
));
cl_git_fail
(
git_submodule_lookup
(
&
sm
,
repo
,
"submodules/libgit2"
));
}
tests/submodule/submodule_helpers.c
View file @
923c8400
...
...
@@ -126,20 +126,26 @@ git_repository *setup_fixture_submod2(void)
return
repo
;
}
void
assert_submodule_exists
(
git_repository
*
repo
,
const
char
*
name
)
void
assert__submodule_exists
(
git_repository
*
repo
,
const
char
*
name
,
const
char
*
msg
,
const
char
*
file
,
int
line
)
{
git_submodule
*
sm
;
cl_git_pass
(
git_submodule_lookup
(
&
sm
,
repo
,
name
));
cl_assert
(
sm
);
int
error
=
git_submodule_lookup
(
&
sm
,
repo
,
name
);
if
(
error
)
cl_git_report_failure
(
error
,
file
,
line
,
msg
);
cl_assert_at_line
(
sm
!=
NULL
,
file
,
line
);
git_submodule_free
(
sm
);
}
void
refute_submodule_exists
(
git_repository
*
repo
,
const
char
*
name
,
int
expected_error
)
void
refute__submodule_exists
(
git_repository
*
repo
,
const
char
*
name
,
int
expected_error
,
const
char
*
msg
,
const
char
*
file
,
int
line
)
{
git_submodule
*
sm
;
cl_assert_equal_i
(
expected_error
,
git_submodule_lookup
(
&
sm
,
repo
,
name
));
clar__assert_equal
(
file
,
line
,
msg
,
1
,
"%i"
,
expected_error
,
(
int
)(
git_submodule_lookup
(
&
sm
,
repo
,
name
)));
}
unsigned
int
get_submodule_status
(
git_repository
*
repo
,
const
char
*
name
)
...
...
@@ -154,3 +160,19 @@ unsigned int get_submodule_status(git_repository *repo, const char *name)
return
status
;
}
static
int
print_submodules
(
git_submodule
*
sm
,
const
char
*
name
,
void
*
p
)
{
unsigned
int
loc
=
0
;
GIT_UNUSED
(
p
);
git_submodule_location
(
&
loc
,
sm
);
fprintf
(
stderr
,
"# submodule %s (at %s) flags %x
\n
"
,
name
,
git_submodule_path
(
sm
),
loc
);
return
0
;
}
void
dump_submodules
(
git_repository
*
repo
)
{
git_submodule_foreach
(
repo
,
print_submodules
,
NULL
);
}
tests/submodule/submodule_helpers.h
View file @
923c8400
...
...
@@ -6,5 +6,16 @@ extern git_repository *setup_fixture_submod2(void);
extern
unsigned
int
get_submodule_status
(
git_repository
*
,
const
char
*
);
extern
void
assert_submodule_exists
(
git_repository
*
,
const
char
*
);
extern
void
refute_submodule_exists
(
git_repository
*
,
const
char
*
,
int
err
);
extern
void
assert__submodule_exists
(
git_repository
*
,
const
char
*
,
const
char
*
,
const
char
*
,
int
);
#define assert_submodule_exists(repo,name) \
assert__submodule_exists(repo, name, "git_submodule_lookup(" #name ") failed", __FILE__, __LINE__)
extern
void
refute__submodule_exists
(
git_repository
*
,
const
char
*
,
int
err
,
const
char
*
,
const
char
*
,
int
);
#define refute_submodule_exists(repo,name,code) \
refute__submodule_exists(repo, name, code, "expected git_submodule_lookup(" #name ") to fail with error " #code, __FILE__, __LINE__)
extern
void
dump_submodules
(
git_repository
*
repo
);
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