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
fcccf304
Commit
fcccf304
authored
Sep 09, 2012
by
nulltoken
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
remote: introduce git_remote_rename()
parent
3a14d3e2
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
524 additions
and
0 deletions
+524
-0
include/git2/remote.h
+18
-0
src/remote.c
+285
-0
tests-clar/config/config_helpers.c
+15
-0
tests-clar/config/config_helpers.h
+5
-0
tests-clar/network/remoterename.c
+201
-0
No files found.
include/git2/remote.h
View file @
fcccf304
...
...
@@ -336,6 +336,24 @@ GIT_EXTERN(int) git_remote_autotag(git_remote *remote);
*/
GIT_EXTERN
(
void
)
git_remote_set_autotag
(
git_remote
*
remote
,
int
value
);
/**
* Give the remote a new name
*
* All remote-tracking branches and configuration settings
* for the remote are updated.
*
* @param remote the remote to rename
* @param new_name the new name the remote should bear
* @param callback Optional callback to notify the consumer of fetch refspecs
* that haven't been automatically updated and need potential manual tweaking.
* @param payload Additional data to pass to the callback
* @return 0 or an error code
*/
GIT_EXTERN
(
int
)
git_remote_rename
(
git_remote
*
remote
,
const
char
*
new_name
,
int
(
*
callback
)(
const
char
*
problematic_refspec
,
void
*
payload
),
void
*
payload
);
/** @} */
GIT_END_DECL
...
...
src/remote.c
View file @
fcccf304
...
...
@@ -798,3 +798,288 @@ void git_remote_set_autotag(git_remote *remote, int value)
{
remote
->
download_tags
=
value
;
}
static
int
ensure_remote_doesnot_exist
(
git_repository
*
repo
,
const
char
*
name
)
{
int
error
;
git_remote
*
remote
;
error
=
git_remote_load
(
&
remote
,
repo
,
name
);
if
(
error
==
GIT_ENOTFOUND
)
return
0
;
if
(
error
<
0
)
return
error
;
git_remote_free
(
remote
);
giterr_set
(
GITERR_CONFIG
,
"Remote '%s' already exists."
,
name
);
return
GIT_EEXISTS
;
}
static
int
rename_remote_config_section
(
git_repository
*
repo
,
const
char
*
old_name
,
const
char
*
new_name
)
{
git_buf
old_section_name
=
GIT_BUF_INIT
,
new_section_name
=
GIT_BUF_INIT
;
int
error
=
-
1
;
if
(
git_buf_printf
(
&
old_section_name
,
"remote.%s"
,
old_name
)
<
0
)
goto
cleanup
;
if
(
git_buf_printf
(
&
new_section_name
,
"remote.%s"
,
new_name
)
<
0
)
goto
cleanup
;
error
=
git_config_rename_section
(
repo
,
git_buf_cstr
(
&
old_section_name
),
git_buf_cstr
(
&
new_section_name
));
cleanup:
git_buf_free
(
&
old_section_name
);
git_buf_free
(
&
new_section_name
);
return
error
;
}
struct
update_data
{
git_config
*
config
;
const
char
*
old_remote_name
;
const
char
*
new_remote_name
;
};
static
int
update_config_entries_cb
(
const
git_config_entry
*
entry
,
void
*
payload
)
{
struct
update_data
*
data
=
(
struct
update_data
*
)
payload
;
if
(
strcmp
(
entry
->
value
,
data
->
old_remote_name
))
return
0
;
return
git_config_set_string
(
data
->
config
,
entry
->
name
,
data
->
new_remote_name
);
}
static
int
update_branch_remote_config_entry
(
git_repository
*
repo
,
const
char
*
old_name
,
const
char
*
new_name
)
{
git_config
*
config
;
struct
update_data
data
;
if
(
git_repository_config__weakptr
(
&
config
,
repo
)
<
0
)
return
-
1
;
data
.
config
=
config
;
data
.
old_remote_name
=
old_name
;
data
.
new_remote_name
=
new_name
;
return
git_config_foreach_match
(
config
,
"branch
\\
..+
\\
.remote"
,
update_config_entries_cb
,
&
data
);
}
static
int
rename_cb
(
const
char
*
ref
,
void
*
data
)
{
if
(
git__prefixcmp
(
ref
,
GIT_REFS_REMOTES_DIR
))
return
0
;
return
git_vector_insert
((
git_vector
*
)
data
,
git__strdup
(
ref
));
}
static
int
rename_one_remote_reference
(
git_repository
*
repo
,
const
char
*
reference_name
,
const
char
*
old_remote_name
,
const
char
*
new_remote_name
)
{
int
error
;
git_buf
new_name
=
GIT_BUF_INIT
;
git_reference
*
reference
=
NULL
;
if
(
git_buf_printf
(
&
new_name
,
GIT_REFS_REMOTES_DIR
"%s%s"
,
new_remote_name
,
reference_name
+
strlen
(
GIT_REFS_REMOTES_DIR
)
+
strlen
(
old_remote_name
))
<
0
)
return
-
1
;
if
(
git_reference_lookup
(
&
reference
,
repo
,
reference_name
)
<
0
)
goto
cleanup
;
error
=
git_reference_rename
(
reference
,
git_buf_cstr
(
&
new_name
),
0
);
cleanup:
git_reference_free
(
reference
);
git_buf_free
(
&
new_name
);
return
error
;
}
static
int
rename_remote_references
(
git_repository
*
repo
,
const
char
*
old_name
,
const
char
*
new_name
)
{
git_vector
refnames
;
int
error
=
-
1
;
unsigned
int
i
;
char
*
name
;
if
(
git_vector_init
(
&
refnames
,
8
,
NULL
)
<
0
)
goto
cleanup
;
if
(
git_reference_foreach
(
repo
,
GIT_REF_LISTALL
,
rename_cb
,
&
refnames
)
<
0
)
goto
cleanup
;
git_vector_foreach
(
&
refnames
,
i
,
name
)
{
if
((
error
=
rename_one_remote_reference
(
repo
,
name
,
old_name
,
new_name
))
<
0
)
goto
cleanup
;
}
error
=
0
;
cleanup:
git_vector_foreach
(
&
refnames
,
i
,
name
)
{
git__free
(
name
);
}
git_vector_free
(
&
refnames
);
return
error
;
}
static
int
rename_fetch_refspecs
(
git_remote
*
remote
,
const
char
*
new_name
,
int
(
*
callback
)(
const
char
*
problematic_refspec
,
void
*
payload
),
void
*
payload
)
{
git_config
*
config
;
const
git_refspec
*
fetch_refspec
;
git_buf
dst_prefix
=
GIT_BUF_INIT
,
serialized
=
GIT_BUF_INIT
;
const
char
*
pos
;
int
error
=
-
1
;
fetch_refspec
=
git_remote_fetchspec
(
remote
);
/* Is there a refspec to deal with? */
if
(
fetch_refspec
->
src
==
NULL
&&
fetch_refspec
->
dst
==
NULL
)
return
0
;
if
(
git_refspec__serialize
(
&
serialized
,
fetch_refspec
)
<
0
)
goto
cleanup
;
/* Is it an in-memory remote? */
if
(
remote
->
name
==
'\0'
)
{
error
=
(
callback
(
git_buf_cstr
(
&
serialized
),
payload
)
<
0
)
?
GIT_EUSER
:
0
;
goto
cleanup
;
}
if
(
git_buf_printf
(
&
dst_prefix
,
":refs/remotes/%s/"
,
remote
->
name
)
<
0
)
goto
cleanup
;
pos
=
strstr
(
git_buf_cstr
(
&
serialized
),
git_buf_cstr
(
&
dst_prefix
));
/* Does the dst part of the refspec follow the extected standard format? */
if
(
!
pos
)
{
error
=
(
callback
(
git_buf_cstr
(
&
serialized
),
payload
)
<
0
)
?
GIT_EUSER
:
0
;
goto
cleanup
;
}
if
(
git_buf_splice
(
&
serialized
,
pos
-
git_buf_cstr
(
&
serialized
)
+
strlen
(
":refs/remotes/"
),
strlen
(
remote
->
name
),
new_name
,
strlen
(
new_name
))
<
0
)
goto
cleanup
;
git_refspec__free
(
&
remote
->
fetch
);
if
(
git_refspec__parse
(
&
remote
->
fetch
,
git_buf_cstr
(
&
serialized
),
true
)
<
0
)
goto
cleanup
;
if
(
git_repository_config__weakptr
(
&
config
,
remote
->
repo
)
<
0
)
goto
cleanup
;
error
=
update_config_refspec
(
config
,
new_name
,
&
remote
->
fetch
,
GIT_DIR_FETCH
);
cleanup:
git_buf_free
(
&
serialized
);
git_buf_free
(
&
dst_prefix
);
return
error
;
}
int
git_remote_rename
(
git_remote
*
remote
,
const
char
*
new_name
,
int
(
*
callback
)(
const
char
*
problematic_refspec
,
void
*
payload
),
void
*
payload
)
{
int
error
;
assert
(
remote
&&
new_name
);
if
((
error
=
ensure_remote_doesnot_exist
(
remote
->
repo
,
new_name
))
<
0
)
return
error
;
if
((
error
=
ensure_remote_name_is_valid
(
new_name
))
<
0
)
return
error
;
if
(
!
remote
->
name
)
{
if
((
error
=
rename_fetch_refspecs
(
remote
,
new_name
,
callback
,
payload
))
<
0
)
return
error
;
remote
->
name
=
git__strdup
(
new_name
);
return
git_remote_save
(
remote
);
}
if
((
error
=
rename_remote_config_section
(
remote
->
repo
,
remote
->
name
,
new_name
))
<
0
)
return
error
;
if
((
error
=
update_branch_remote_config_entry
(
remote
->
repo
,
remote
->
name
,
new_name
))
<
0
)
return
error
;
if
((
error
=
rename_remote_references
(
remote
->
repo
,
remote
->
name
,
new_name
))
<
0
)
return
error
;
if
((
error
=
rename_fetch_refspecs
(
remote
,
new_name
,
callback
,
payload
))
<
0
)
return
error
;
git__free
(
remote
->
name
);
remote
->
name
=
git__strdup
(
new_name
);
return
0
;
}
tests-clar/config/config_helpers.c
View file @
fcccf304
...
...
@@ -20,3 +20,18 @@ void assert_config_entry_existence(
else
cl_assert_equal_i
(
GIT_ENOTFOUND
,
result
);
}
void
assert_config_entry_value
(
git_repository
*
repo
,
const
char
*
name
,
const
char
*
expected_value
)
{
git_config
*
config
;
const
char
*
out
;
cl_git_pass
(
git_repository_config__weakptr
(
&
config
,
repo
));
cl_git_pass
(
git_config_get_string
(
&
out
,
config
,
name
));
cl_assert_equal_s
(
expected_value
,
out
);
}
tests-clar/config/config_helpers.h
View file @
fcccf304
...
...
@@ -2,3 +2,8 @@ extern void assert_config_entry_existence(
git_repository
*
repo
,
const
char
*
name
,
bool
is_supposed_to_exist
);
extern
void
assert_config_entry_value
(
git_repository
*
repo
,
const
char
*
name
,
const
char
*
expected_value
);
tests-clar/network/remoterename.c
0 → 100644
View file @
fcccf304
#include "clar_libgit2.h"
#include "config/config_helpers.h"
#include "repository.h"
static
git_remote
*
_remote
;
static
git_repository
*
_repo
;
void
test_network_remoterename__initialize
(
void
)
{
_repo
=
cl_git_sandbox_init
(
"testrepo.git"
);
cl_git_pass
(
git_remote_load
(
&
_remote
,
_repo
,
"test"
));
}
void
test_network_remoterename__cleanup
(
void
)
{
git_remote_free
(
_remote
);
cl_git_sandbox_cleanup
();
}
static
int
dont_call_me_cb
(
const
char
*
fetch_refspec
,
void
*
payload
)
{
GIT_UNUSED
(
fetch_refspec
);
GIT_UNUSED
(
payload
);
cl_assert
(
false
);
return
-
1
;
}
void
test_network_remoterename__renaming_a_remote_moves_related_configuration_section
(
void
)
{
assert_config_entry_existence
(
_repo
,
"remote.test.fetch"
,
true
);
assert_config_entry_existence
(
_repo
,
"remote.just/renamed.fetch"
,
false
);
cl_git_pass
(
git_remote_rename
(
_remote
,
"just/renamed"
,
dont_call_me_cb
,
NULL
));
assert_config_entry_existence
(
_repo
,
"remote.test.fetch"
,
false
);
assert_config_entry_existence
(
_repo
,
"remote.just/renamed.fetch"
,
true
);
}
void
test_network_remoterename__renaming_a_remote_updates_branch_related_configuration_entries
(
void
)
{
assert_config_entry_value
(
_repo
,
"branch.master.remote"
,
"test"
);
cl_git_pass
(
git_remote_rename
(
_remote
,
"just/renamed"
,
dont_call_me_cb
,
NULL
));
assert_config_entry_value
(
_repo
,
"branch.master.remote"
,
"just/renamed"
);
}
void
test_network_remoterename__renaming_a_remote_updates_default_fetchrefspec
(
void
)
{
cl_git_pass
(
git_remote_rename
(
_remote
,
"just/renamed"
,
dont_call_me_cb
,
NULL
));
assert_config_entry_value
(
_repo
,
"remote.just/renamed.fetch"
,
"+refs/heads/*:refs/remotes/just/renamed/*"
);
}
void
test_network_remoterename__renaming_a_remote_without_a_fetchrefspec_doesnt_create_one
(
void
)
{
git_config
*
config
;
git_remote_free
(
_remote
);
cl_git_pass
(
git_repository_config__weakptr
(
&
config
,
_repo
));
cl_git_pass
(
git_config_delete
(
config
,
"remote.test.fetch"
));
cl_git_pass
(
git_remote_load
(
&
_remote
,
_repo
,
"test"
));
assert_config_entry_existence
(
_repo
,
"remote.test.fetch"
,
false
);
cl_git_pass
(
git_remote_rename
(
_remote
,
"just/renamed"
,
dont_call_me_cb
,
NULL
));
assert_config_entry_existence
(
_repo
,
"remote.just/renamed.fetch"
,
false
);
}
static
int
ensure_refspecs
(
const
char
*
refspec_name
,
void
*
payload
)
{
int
i
=
0
;
bool
found
=
false
;
const
char
**
exp
=
(
const
char
**
)
payload
;
while
(
exp
[
i
])
{
if
(
strcmp
(
exp
[
i
++
],
refspec_name
))
continue
;
found
=
true
;
break
;
}
cl_assert
(
found
);
return
0
;
}
void
test_network_remoterename__renaming_a_remote_notifies_of_non_default_fetchrefspec
(
void
)
{
git_config
*
config
;
char
*
expected_refspecs
[]
=
{
"+refs/*:refs/*"
,
NULL
};
git_remote_free
(
_remote
);
cl_git_pass
(
git_repository_config__weakptr
(
&
config
,
_repo
));
cl_git_pass
(
git_config_set_string
(
config
,
"remote.test.fetch"
,
"+refs/*:refs/*"
));
cl_git_pass
(
git_remote_load
(
&
_remote
,
_repo
,
"test"
));
cl_git_pass
(
git_remote_rename
(
_remote
,
"just/renamed"
,
ensure_refspecs
,
&
expected_refspecs
));
assert_config_entry_value
(
_repo
,
"remote.just/renamed.fetch"
,
"+refs/*:refs/*"
);
}
void
test_network_remoterename__new_name_can_contain_dots
(
void
)
{
cl_git_pass
(
git_remote_rename
(
_remote
,
"just.renamed"
,
dont_call_me_cb
,
NULL
));
cl_assert_equal_s
(
"just.renamed"
,
git_remote_name
(
_remote
));
}
void
test_network_remoterename__new_name_must_conform_to_reference_naming_conventions
(
void
)
{
cl_git_fail
(
git_remote_rename
(
_remote
,
"new@{name"
,
dont_call_me_cb
,
NULL
));
}
void
test_network_remoterename__renamed_name_is_persisted
(
void
)
{
git_remote
*
renamed
;
git_repository
*
another_repo
;
cl_git_fail
(
git_remote_load
(
&
renamed
,
_repo
,
"just/renamed"
));
cl_git_pass
(
git_remote_rename
(
_remote
,
"just/renamed"
,
dont_call_me_cb
,
NULL
));
cl_git_pass
(
git_repository_open
(
&
another_repo
,
"testrepo.git"
));
cl_git_pass
(
git_remote_load
(
&
renamed
,
_repo
,
"just/renamed"
));
git_remote_free
(
renamed
);
git_repository_free
(
another_repo
);
}
void
test_network_remoterename__cannot_overwrite_an_existing_remote
(
void
)
{
cl_assert_equal_i
(
GIT_EEXISTS
,
git_remote_rename
(
_remote
,
"test"
,
dont_call_me_cb
,
NULL
));
cl_assert_equal_i
(
GIT_EEXISTS
,
git_remote_rename
(
_remote
,
"test_with_pushurl"
,
dont_call_me_cb
,
NULL
));
}
void
test_network_remoterename__renaming_an_inmemory_remote_persists_it
(
void
)
{
git_remote
*
remote
;
assert_config_entry_existence
(
_repo
,
"remote.durable.url"
,
false
);
cl_git_pass
(
git_remote_new
(
&
remote
,
_repo
,
NULL
,
"git://github.com/libgit2/durable.git"
,
NULL
));
assert_config_entry_existence
(
_repo
,
"remote.durable.url"
,
false
);
cl_git_pass
(
git_remote_rename
(
remote
,
"durable"
,
dont_call_me_cb
,
NULL
));
assert_config_entry_value
(
_repo
,
"remote.durable.url"
,
"git://github.com/libgit2/durable.git"
);
git_remote_free
(
remote
);
}
void
test_network_remoterename__renaming_an_inmemory_nameless_remote_notifies_the_inability_to_update_the_fetch_refspec
(
void
)
{
git_remote
*
remote
;
char
*
expected_refspecs
[]
=
{
"+refs/heads/*:refs/remotes/volatile/*"
,
NULL
};
assert_config_entry_existence
(
_repo
,
"remote.volatile.url"
,
false
);
cl_git_pass
(
git_remote_new
(
&
remote
,
_repo
,
NULL
,
"git://github.com/libgit2/volatile.git"
,
"+refs/heads/*:refs/remotes/volatile/*"
));
cl_git_pass
(
git_remote_rename
(
remote
,
"durable"
,
ensure_refspecs
,
&
expected_refspecs
));
git_remote_free
(
remote
);
}
void
test_network_remoterename__renaming_a_remote_moves_the_underlying_reference
(
void
)
{
git_reference
*
underlying
;
cl_assert_equal_i
(
GIT_ENOTFOUND
,
git_reference_lookup
(
&
underlying
,
_repo
,
"refs/remotes/just/renamed"
));
cl_git_pass
(
git_reference_lookup
(
&
underlying
,
_repo
,
"refs/remotes/test/master"
));
git_reference_free
(
underlying
);
cl_git_pass
(
git_remote_rename
(
_remote
,
"just/renamed"
,
dont_call_me_cb
,
NULL
));
cl_assert_equal_i
(
GIT_ENOTFOUND
,
git_reference_lookup
(
&
underlying
,
_repo
,
"refs/remotes/test/master"
));
cl_git_pass
(
git_reference_lookup
(
&
underlying
,
_repo
,
"refs/remotes/just/renamed/master"
));
git_reference_free
(
underlying
);
}
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