зеркало из https://github.com/microsoft/git.git
Merge branch 'mv/remote-rename'
* mv/remote-rename: git-remote: document the migration feature of the rename subcommand git-remote rename: migrate from remotes/ and branches/ remote: add a new 'origin' variable to the struct Implement git remote rename
This commit is contained in:
Коммит
a5b2d4ac24
|
@ -11,6 +11,7 @@ SYNOPSIS
|
|||
[verse]
|
||||
'git remote' [-v | --verbose]
|
||||
'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
|
||||
'git remote rename' <old> <new>
|
||||
'git remote rm' <name>
|
||||
'git remote show' [-n] <name>
|
||||
'git remote prune' [-n | --dry-run] <name>
|
||||
|
@ -61,6 +62,15 @@ only makes sense in bare repositories. If a remote uses mirror
|
|||
mode, furthermore, `git push` will always behave as if `\--mirror`
|
||||
was passed.
|
||||
|
||||
'rename'::
|
||||
|
||||
Rename the remote named <old> to <new>. All remote tracking branches and
|
||||
configuration settings for the remote are updated.
|
||||
+
|
||||
In case <old> and <new> are the same, and <old> is a file under
|
||||
`$GIT_DIR/remotes` or `$GIT_DIR/branches`, the remote is converted to
|
||||
the configuration file format.
|
||||
|
||||
'rm'::
|
||||
|
||||
Remove the remote named <name>. All remote tracking branches and
|
||||
|
|
188
builtin-remote.c
188
builtin-remote.c
|
@ -10,6 +10,7 @@
|
|||
static const char * const builtin_remote_usage[] = {
|
||||
"git remote",
|
||||
"git remote add <name> <url>",
|
||||
"git remote rename <old> <new>",
|
||||
"git remote rm <name>",
|
||||
"git remote show <name>",
|
||||
"git remote prune <name>",
|
||||
|
@ -329,6 +330,191 @@ static int add_branch_for_removal(const char *refname,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct rename_info {
|
||||
const char *old;
|
||||
const char *new;
|
||||
struct string_list *remote_branches;
|
||||
};
|
||||
|
||||
static int read_remote_branches(const char *refname,
|
||||
const unsigned char *sha1, int flags, void *cb_data)
|
||||
{
|
||||
struct rename_info *rename = cb_data;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct string_list_item *item;
|
||||
int flag;
|
||||
unsigned char orig_sha1[20];
|
||||
const char *symref;
|
||||
|
||||
strbuf_addf(&buf, "refs/remotes/%s", rename->old);
|
||||
if(!prefixcmp(refname, buf.buf)) {
|
||||
item = string_list_append(xstrdup(refname), rename->remote_branches);
|
||||
symref = resolve_ref(refname, orig_sha1, 1, &flag);
|
||||
if (flag & REF_ISSYMREF)
|
||||
item->util = xstrdup(symref);
|
||||
else
|
||||
item->util = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int migrate_file(struct remote *remote)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int i;
|
||||
char *path = NULL;
|
||||
|
||||
strbuf_addf(&buf, "remote.%s.url", remote->name);
|
||||
for (i = 0; i < remote->url_nr; i++)
|
||||
if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
|
||||
return error("Could not append '%s' to '%s'",
|
||||
remote->url[i], buf.buf);
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "remote.%s.push", remote->name);
|
||||
for (i = 0; i < remote->push_refspec_nr; i++)
|
||||
if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
|
||||
return error("Could not append '%s' to '%s'",
|
||||
remote->push_refspec[i], buf.buf);
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "remote.%s.fetch", remote->name);
|
||||
for (i = 0; i < remote->fetch_refspec_nr; i++)
|
||||
if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
|
||||
return error("Could not append '%s' to '%s'",
|
||||
remote->fetch_refspec[i], buf.buf);
|
||||
if (remote->origin == REMOTE_REMOTES)
|
||||
path = git_path("remotes/%s", remote->name);
|
||||
else if (remote->origin == REMOTE_BRANCHES)
|
||||
path = git_path("branches/%s", remote->name);
|
||||
if (path && unlink(path))
|
||||
warning("failed to remove '%s'", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv(int argc, const char **argv)
|
||||
{
|
||||
struct option options[] = {
|
||||
OPT_END()
|
||||
};
|
||||
struct remote *oldremote, *newremote;
|
||||
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
|
||||
struct string_list remote_branches = { NULL, 0, 0, 0 };
|
||||
struct rename_info rename;
|
||||
int i;
|
||||
|
||||
if (argc != 3)
|
||||
usage_with_options(builtin_remote_usage, options);
|
||||
|
||||
rename.old = argv[1];
|
||||
rename.new = argv[2];
|
||||
rename.remote_branches = &remote_branches;
|
||||
|
||||
oldremote = remote_get(rename.old);
|
||||
if (!oldremote)
|
||||
die("No such remote: %s", rename.old);
|
||||
|
||||
if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
|
||||
return migrate_file(oldremote);
|
||||
|
||||
newremote = remote_get(rename.new);
|
||||
if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
|
||||
die("remote %s already exists.", rename.new);
|
||||
|
||||
strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
|
||||
if (!valid_fetch_refspec(buf.buf))
|
||||
die("'%s' is not a valid remote name", rename.new);
|
||||
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "remote.%s", rename.old);
|
||||
strbuf_addf(&buf2, "remote.%s", rename.new);
|
||||
if (git_config_rename_section(buf.buf, buf2.buf) < 1)
|
||||
return error("Could not rename config section '%s' to '%s'",
|
||||
buf.buf, buf2.buf);
|
||||
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "remote.%s.fetch", rename.new);
|
||||
if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
|
||||
return error("Could not remove config section '%s'", buf.buf);
|
||||
for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
|
||||
char *ptr;
|
||||
|
||||
strbuf_reset(&buf2);
|
||||
strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
|
||||
ptr = strstr(buf2.buf, rename.old);
|
||||
if (ptr)
|
||||
strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),
|
||||
rename.new, strlen(rename.new));
|
||||
if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
|
||||
return error("Could not append '%s'", buf.buf);
|
||||
}
|
||||
|
||||
read_branches();
|
||||
for (i = 0; i < branch_list.nr; i++) {
|
||||
struct string_list_item *item = branch_list.items + i;
|
||||
struct branch_info *info = item->util;
|
||||
if (info->remote && !strcmp(info->remote, rename.old)) {
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "branch.%s.remote", item->string);
|
||||
if (git_config_set(buf.buf, rename.new)) {
|
||||
return error("Could not set '%s'", buf.buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* First remove symrefs, then rename the rest, finally create
|
||||
* the new symrefs.
|
||||
*/
|
||||
for_each_ref(read_remote_branches, &rename);
|
||||
for (i = 0; i < remote_branches.nr; i++) {
|
||||
struct string_list_item *item = remote_branches.items + i;
|
||||
int flag = 0;
|
||||
unsigned char sha1[20];
|
||||
const char *symref;
|
||||
|
||||
symref = resolve_ref(item->string, sha1, 1, &flag);
|
||||
if (!(flag & REF_ISSYMREF))
|
||||
continue;
|
||||
if (delete_ref(item->string, NULL, REF_NODEREF))
|
||||
die("deleting '%s' failed", item->string);
|
||||
}
|
||||
for (i = 0; i < remote_branches.nr; i++) {
|
||||
struct string_list_item *item = remote_branches.items + i;
|
||||
|
||||
if (item->util)
|
||||
continue;
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addstr(&buf, item->string);
|
||||
strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
|
||||
rename.new, strlen(rename.new));
|
||||
strbuf_reset(&buf2);
|
||||
strbuf_addf(&buf2, "remote: renamed %s to %s",
|
||||
item->string, buf.buf);
|
||||
if (rename_ref(item->string, buf.buf, buf2.buf))
|
||||
die("renaming '%s' failed", item->string);
|
||||
}
|
||||
for (i = 0; i < remote_branches.nr; i++) {
|
||||
struct string_list_item *item = remote_branches.items + i;
|
||||
|
||||
if (!item->util)
|
||||
continue;
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addstr(&buf, item->string);
|
||||
strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
|
||||
rename.new, strlen(rename.new));
|
||||
strbuf_reset(&buf2);
|
||||
strbuf_addstr(&buf2, item->util);
|
||||
strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
|
||||
rename.new, strlen(rename.new));
|
||||
strbuf_reset(&buf3);
|
||||
strbuf_addf(&buf3, "remote: renamed %s to %s",
|
||||
item->string, buf.buf);
|
||||
if (create_symref(buf.buf, buf2.buf, buf3.buf))
|
||||
die("creating '%s' failed", buf.buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_branches(struct string_list *branches)
|
||||
{
|
||||
int i, result = 0;
|
||||
|
@ -695,6 +881,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
|
|||
result = show_all();
|
||||
else if (!strcmp(argv[0], "add"))
|
||||
result = add(argc, argv);
|
||||
else if (!strcmp(argv[0], "rename"))
|
||||
result = mv(argc, argv);
|
||||
else if (!strcmp(argv[0], "rm"))
|
||||
result = rm(argc, argv);
|
||||
else if (!strcmp(argv[0], "show"))
|
||||
|
|
3
remote.c
3
remote.c
|
@ -201,6 +201,7 @@ static void read_remotes_file(struct remote *remote)
|
|||
|
||||
if (!f)
|
||||
return;
|
||||
remote->origin = REMOTE_REMOTES;
|
||||
while (fgets(buffer, BUF_SIZE, f)) {
|
||||
int value_list;
|
||||
char *s, *p;
|
||||
|
@ -261,6 +262,7 @@ static void read_branches_file(struct remote *remote)
|
|||
s++;
|
||||
if (!*s)
|
||||
return;
|
||||
remote->origin = REMOTE_BRANCHES;
|
||||
p = s + strlen(s);
|
||||
while (isspace(p[-1]))
|
||||
*--p = 0;
|
||||
|
@ -350,6 +352,7 @@ static int handle_config(const char *key, const char *value, void *cb)
|
|||
if (!subkey)
|
||||
return error("Config with no key for remote %s", name);
|
||||
remote = make_remote(name, subkey - name);
|
||||
remote->origin = REMOTE_CONFIG;
|
||||
if (!strcmp(subkey, ".mirror"))
|
||||
remote->mirror = git_config_bool(key, value);
|
||||
else if (!strcmp(subkey, ".skipdefaultupdate"))
|
||||
|
|
7
remote.h
7
remote.h
|
@ -1,8 +1,15 @@
|
|||
#ifndef REMOTE_H
|
||||
#define REMOTE_H
|
||||
|
||||
enum {
|
||||
REMOTE_CONFIG,
|
||||
REMOTE_REMOTES,
|
||||
REMOTE_BRANCHES
|
||||
};
|
||||
|
||||
struct remote {
|
||||
const char *name;
|
||||
int origin;
|
||||
|
||||
const char **url;
|
||||
int url_nr;
|
||||
|
|
|
@ -328,4 +328,52 @@ test_expect_success 'reject adding remote with an invalid name' '
|
|||
|
||||
'
|
||||
|
||||
# The first three test if the tracking branches are properly renamed,
|
||||
# the last two ones check if the config is updated.
|
||||
|
||||
test_expect_success 'rename a remote' '
|
||||
|
||||
git clone one four &&
|
||||
(cd four &&
|
||||
git remote rename origin upstream &&
|
||||
rmdir .git/refs/remotes/origin &&
|
||||
test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
|
||||
test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
|
||||
test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
|
||||
test "$(git config branch.master.remote)" = "upstream")
|
||||
|
||||
'
|
||||
|
||||
cat > remotes_origin << EOF
|
||||
URL: $(pwd)/one
|
||||
Push: refs/heads/master:refs/heads/upstream
|
||||
Pull: refs/heads/master:refs/heads/origin
|
||||
EOF
|
||||
|
||||
test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
|
||||
git clone one five &&
|
||||
origin_url=$(pwd)/one &&
|
||||
(cd five &&
|
||||
git remote rm origin &&
|
||||
mkdir -p .git/remotes &&
|
||||
cat ../remotes_origin > .git/remotes/origin &&
|
||||
git remote rename origin origin &&
|
||||
! test -f .git/remotes/origin &&
|
||||
test "$(git config remote.origin.url)" = "$origin_url" &&
|
||||
test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" &&
|
||||
test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
|
||||
'
|
||||
|
||||
test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
|
||||
git clone one six &&
|
||||
origin_url=$(pwd)/one &&
|
||||
(cd six &&
|
||||
git remote rm origin &&
|
||||
echo "$origin_url" > .git/branches/origin &&
|
||||
git remote rename origin origin &&
|
||||
! test -f .git/branches/origin &&
|
||||
test "$(git config remote.origin.url)" = "$origin_url" &&
|
||||
test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
Загрузка…
Ссылка в новой задаче