Merge branch 'sd/branch-copy'
"git branch" learned "-c/-C" to create a new branch by copying an existing one. * sd/branch-copy: branch: fix "copy" to never touch HEAD branch: add a --copy (-c) option to go with --move (-m) branch: add test for -m renaming multiple config sections config: create a function to format section headers
This commit is contained in:
commit
3b48045c6c
@ -18,6 +18,7 @@ SYNOPSIS
|
|||||||
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
|
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
|
||||||
'git branch' --unset-upstream [<branchname>]
|
'git branch' --unset-upstream [<branchname>]
|
||||||
'git branch' (-m | -M) [<oldbranch>] <newbranch>
|
'git branch' (-m | -M) [<oldbranch>] <newbranch>
|
||||||
|
'git branch' (-c | -C) [<oldbranch>] <newbranch>
|
||||||
'git branch' (-d | -D) [-r] <branchname>...
|
'git branch' (-d | -D) [-r] <branchname>...
|
||||||
'git branch' --edit-description [<branchname>]
|
'git branch' --edit-description [<branchname>]
|
||||||
|
|
||||||
@ -64,6 +65,10 @@ If <oldbranch> had a corresponding reflog, it is renamed to match
|
|||||||
renaming. If <newbranch> exists, -M must be used to force the rename
|
renaming. If <newbranch> exists, -M must be used to force the rename
|
||||||
to happen.
|
to happen.
|
||||||
|
|
||||||
|
The `-c` and `-C` options have the exact same semantics as `-m` and
|
||||||
|
`-M`, except instead of the branch being renamed it along with its
|
||||||
|
config and reflog will be copied to a new name.
|
||||||
|
|
||||||
With a `-d` or `-D` option, `<branchname>` will be deleted. You may
|
With a `-d` or `-D` option, `<branchname>` will be deleted. You may
|
||||||
specify more than one branch for deletion. If the branch currently
|
specify more than one branch for deletion. If the branch currently
|
||||||
has a reflog then the reflog will also be deleted.
|
has a reflog then the reflog will also be deleted.
|
||||||
@ -104,7 +109,7 @@ OPTIONS
|
|||||||
In combination with `-d` (or `--delete`), allow deleting the
|
In combination with `-d` (or `--delete`), allow deleting the
|
||||||
branch irrespective of its merged status. In combination with
|
branch irrespective of its merged status. In combination with
|
||||||
`-m` (or `--move`), allow renaming the branch even if the new
|
`-m` (or `--move`), allow renaming the branch even if the new
|
||||||
branch name already exists.
|
branch name already exists, the same applies for `-c` (or `--copy`).
|
||||||
|
|
||||||
-m::
|
-m::
|
||||||
--move::
|
--move::
|
||||||
@ -113,6 +118,13 @@ OPTIONS
|
|||||||
-M::
|
-M::
|
||||||
Shortcut for `--move --force`.
|
Shortcut for `--move --force`.
|
||||||
|
|
||||||
|
-c::
|
||||||
|
--copy::
|
||||||
|
Copy a branch and the corresponding reflog.
|
||||||
|
|
||||||
|
-C::
|
||||||
|
Shortcut for `--copy --force`.
|
||||||
|
|
||||||
--color[=<when>]::
|
--color[=<when>]::
|
||||||
Color branches to highlight current, local, and
|
Color branches to highlight current, local, and
|
||||||
remote-tracking branches.
|
remote-tracking branches.
|
||||||
|
@ -28,6 +28,7 @@ static const char * const builtin_branch_usage[] = {
|
|||||||
N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"),
|
N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"),
|
||||||
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
|
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
|
||||||
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
|
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
|
||||||
|
N_("git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"),
|
||||||
N_("git branch [<options>] [-r | -a] [--points-at]"),
|
N_("git branch [<options>] [-r | -a] [--points-at]"),
|
||||||
N_("git branch [<options>] [-r | -a] [--format]"),
|
N_("git branch [<options>] [-r | -a] [--format]"),
|
||||||
NULL
|
NULL
|
||||||
@ -456,15 +457,19 @@ static void reject_rebase_or_bisect_branch(const char *target)
|
|||||||
free_worktrees(worktrees);
|
free_worktrees(worktrees);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rename_branch(const char *oldname, const char *newname, int force)
|
static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
|
||||||
{
|
{
|
||||||
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
|
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
|
||||||
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
|
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
|
||||||
int recovery = 0;
|
int recovery = 0;
|
||||||
int clobber_head_ok;
|
int clobber_head_ok;
|
||||||
|
|
||||||
if (!oldname)
|
if (!oldname) {
|
||||||
die(_("cannot rename the current branch while not on any."));
|
if (copy)
|
||||||
|
die(_("cannot copy the current branch while not on any."));
|
||||||
|
else
|
||||||
|
die(_("cannot rename the current branch while not on any."));
|
||||||
|
}
|
||||||
|
|
||||||
if (strbuf_check_branch_ref(&oldref, oldname)) {
|
if (strbuf_check_branch_ref(&oldref, oldname)) {
|
||||||
/*
|
/*
|
||||||
@ -487,16 +492,29 @@ static void rename_branch(const char *oldname, const char *newname, int force)
|
|||||||
|
|
||||||
reject_rebase_or_bisect_branch(oldref.buf);
|
reject_rebase_or_bisect_branch(oldref.buf);
|
||||||
|
|
||||||
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
|
if (copy)
|
||||||
oldref.buf, newref.buf);
|
strbuf_addf(&logmsg, "Branch: copied %s to %s",
|
||||||
|
oldref.buf, newref.buf);
|
||||||
|
else
|
||||||
|
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
|
||||||
|
oldref.buf, newref.buf);
|
||||||
|
|
||||||
if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
|
if (!copy && rename_ref(oldref.buf, newref.buf, logmsg.buf))
|
||||||
die(_("Branch rename failed"));
|
die(_("Branch rename failed"));
|
||||||
|
if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
|
||||||
|
die(_("Branch copy failed"));
|
||||||
|
|
||||||
if (recovery)
|
if (recovery) {
|
||||||
warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
|
if (copy)
|
||||||
|
warning(_("Copied a misnamed branch '%s' away"),
|
||||||
|
oldref.buf + 11);
|
||||||
|
else
|
||||||
|
warning(_("Renamed a misnamed branch '%s' away"),
|
||||||
|
oldref.buf + 11);
|
||||||
|
}
|
||||||
|
|
||||||
if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
|
if (!copy &&
|
||||||
|
replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
|
||||||
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
|
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
|
||||||
|
|
||||||
strbuf_release(&logmsg);
|
strbuf_release(&logmsg);
|
||||||
@ -505,8 +523,10 @@ static void rename_branch(const char *oldname, const char *newname, int force)
|
|||||||
strbuf_release(&oldref);
|
strbuf_release(&oldref);
|
||||||
strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
|
strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
|
||||||
strbuf_release(&newref);
|
strbuf_release(&newref);
|
||||||
if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
|
if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
|
||||||
die(_("Branch is renamed, but update of config-file failed"));
|
die(_("Branch is renamed, but update of config-file failed"));
|
||||||
|
if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
|
||||||
|
die(_("Branch is copied, but update of config-file failed"));
|
||||||
strbuf_release(&oldsection);
|
strbuf_release(&oldsection);
|
||||||
strbuf_release(&newsection);
|
strbuf_release(&newsection);
|
||||||
}
|
}
|
||||||
@ -544,7 +564,7 @@ static int edit_branch_description(const char *branch_name)
|
|||||||
|
|
||||||
int cmd_branch(int argc, const char **argv, const char *prefix)
|
int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
int delete = 0, rename = 0, force = 0, list = 0;
|
int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
|
||||||
int reflog = 0, edit_description = 0;
|
int reflog = 0, edit_description = 0;
|
||||||
int quiet = 0, unset_upstream = 0;
|
int quiet = 0, unset_upstream = 0;
|
||||||
const char *new_upstream = NULL;
|
const char *new_upstream = NULL;
|
||||||
@ -581,6 +601,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||||||
OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
|
OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
|
||||||
OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
|
OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
|
||||||
OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
|
OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
|
||||||
|
OPT_BIT('c', "copy", ©, N_("copy a branch and its reflog"), 1),
|
||||||
|
OPT_BIT('C', NULL, ©, N_("copy a branch, even if target exists"), 2),
|
||||||
OPT_BOOL(0, "list", &list, N_("list branch names")),
|
OPT_BOOL(0, "list", &list, N_("list branch names")),
|
||||||
OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")),
|
OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")),
|
||||||
OPT_BOOL(0, "edit-description", &edit_description,
|
OPT_BOOL(0, "edit-description", &edit_description,
|
||||||
@ -624,14 +646,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||||||
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
|
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
|
if (!delete && !rename && !copy && !edit_description && !new_upstream && !unset_upstream && argc == 0)
|
||||||
list = 1;
|
list = 1;
|
||||||
|
|
||||||
if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
|
if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
|
||||||
filter.no_commit)
|
filter.no_commit)
|
||||||
list = 1;
|
list = 1;
|
||||||
|
|
||||||
if (!!delete + !!rename + !!new_upstream +
|
if (!!delete + !!rename + !!copy + !!new_upstream +
|
||||||
list + unset_upstream > 1)
|
list + unset_upstream > 1)
|
||||||
usage_with_options(builtin_branch_usage, options);
|
usage_with_options(builtin_branch_usage, options);
|
||||||
|
|
||||||
@ -649,6 +671,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||||||
if (force) {
|
if (force) {
|
||||||
delete *= 2;
|
delete *= 2;
|
||||||
rename *= 2;
|
rename *= 2;
|
||||||
|
copy *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delete) {
|
if (delete) {
|
||||||
@ -703,13 +726,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||||||
|
|
||||||
if (edit_branch_description(branch_name))
|
if (edit_branch_description(branch_name))
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (copy) {
|
||||||
|
if (!argc)
|
||||||
|
die(_("branch name required"));
|
||||||
|
else if (argc == 1)
|
||||||
|
copy_or_rename_branch(head, argv[0], 1, copy > 1);
|
||||||
|
else if (argc == 2)
|
||||||
|
copy_or_rename_branch(argv[0], argv[1], 1, copy > 1);
|
||||||
|
else
|
||||||
|
die(_("too many branches for a copy operation"));
|
||||||
} else if (rename) {
|
} else if (rename) {
|
||||||
if (!argc)
|
if (!argc)
|
||||||
die(_("branch name required"));
|
die(_("branch name required"));
|
||||||
else if (argc == 1)
|
else if (argc == 1)
|
||||||
rename_branch(head, argv[0], rename > 1);
|
copy_or_rename_branch(head, argv[0], 0, rename > 1);
|
||||||
else if (argc == 2)
|
else if (argc == 2)
|
||||||
rename_branch(argv[0], argv[1], rename > 1);
|
copy_or_rename_branch(argv[0], argv[1], 0, rename > 1);
|
||||||
else
|
else
|
||||||
die(_("too many branches for a rename operation"));
|
die(_("too many branches for a rename operation"));
|
||||||
} else if (new_upstream) {
|
} else if (new_upstream) {
|
||||||
|
114
config.c
114
config.c
@ -2292,11 +2292,10 @@ static int write_error(const char *filename)
|
|||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t write_section(int fd, const char *key)
|
static struct strbuf store_create_section(const char *key)
|
||||||
{
|
{
|
||||||
const char *dot;
|
const char *dot;
|
||||||
int i;
|
int i;
|
||||||
ssize_t ret;
|
|
||||||
struct strbuf sb = STRBUF_INIT;
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
|
||||||
dot = memchr(key, '.', store.baselen);
|
dot = memchr(key, '.', store.baselen);
|
||||||
@ -2312,7 +2311,15 @@ static ssize_t write_section(int fd, const char *key)
|
|||||||
strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
|
strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = write_in_full(fd, sb.buf, sb.len);
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t write_section(int fd, const char *key)
|
||||||
|
{
|
||||||
|
struct strbuf sb = store_create_section(key);
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
ret = write_in_full(fd, sb.buf, sb.len) == sb.len;
|
||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -2743,8 +2750,8 @@ static int section_name_is_ok(const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* if new_name == NULL, the section is removed instead */
|
/* if new_name == NULL, the section is removed instead */
|
||||||
int git_config_rename_section_in_file(const char *config_filename,
|
static int git_config_copy_or_rename_section_in_file(const char *config_filename,
|
||||||
const char *old_name, const char *new_name)
|
const char *old_name, const char *new_name, int copy)
|
||||||
{
|
{
|
||||||
int ret = 0, remove = 0;
|
int ret = 0, remove = 0;
|
||||||
char *filename_buf = NULL;
|
char *filename_buf = NULL;
|
||||||
@ -2753,6 +2760,7 @@ int git_config_rename_section_in_file(const char *config_filename,
|
|||||||
char buf[1024];
|
char buf[1024];
|
||||||
FILE *config_file = NULL;
|
FILE *config_file = NULL;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
struct strbuf copystr = STRBUF_INIT;
|
||||||
|
|
||||||
if (new_name && !section_name_is_ok(new_name)) {
|
if (new_name && !section_name_is_ok(new_name)) {
|
||||||
ret = error("invalid section name: %s", new_name);
|
ret = error("invalid section name: %s", new_name);
|
||||||
@ -2791,12 +2799,30 @@ int git_config_rename_section_in_file(const char *config_filename,
|
|||||||
while (fgets(buf, sizeof(buf), config_file)) {
|
while (fgets(buf, sizeof(buf), config_file)) {
|
||||||
int i;
|
int i;
|
||||||
int length;
|
int length;
|
||||||
|
int is_section = 0;
|
||||||
char *output = buf;
|
char *output = buf;
|
||||||
for (i = 0; buf[i] && isspace(buf[i]); i++)
|
for (i = 0; buf[i] && isspace(buf[i]); i++)
|
||||||
; /* do nothing */
|
; /* do nothing */
|
||||||
if (buf[i] == '[') {
|
if (buf[i] == '[') {
|
||||||
/* it's a section */
|
/* it's a section */
|
||||||
int offset = section_name_match(&buf[i], old_name);
|
int offset;
|
||||||
|
is_section = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When encountering a new section under -c we
|
||||||
|
* need to flush out any section we're already
|
||||||
|
* coping and begin anew. There might be
|
||||||
|
* multiple [branch "$name"] sections.
|
||||||
|
*/
|
||||||
|
if (copystr.len > 0) {
|
||||||
|
if (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {
|
||||||
|
ret = write_error(get_lock_file_path(lock));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
strbuf_reset(©str);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = section_name_match(&buf[i], old_name);
|
||||||
if (offset > 0) {
|
if (offset > 0) {
|
||||||
ret++;
|
ret++;
|
||||||
if (new_name == NULL) {
|
if (new_name == NULL) {
|
||||||
@ -2804,25 +2830,29 @@ int git_config_rename_section_in_file(const char *config_filename,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
store.baselen = strlen(new_name);
|
store.baselen = strlen(new_name);
|
||||||
if (write_section(out_fd, new_name) < 0) {
|
if (!copy) {
|
||||||
ret = write_error(get_lock_file_path(lock));
|
if (write_section(out_fd, new_name) < 0) {
|
||||||
goto out;
|
ret = write_error(get_lock_file_path(lock));
|
||||||
}
|
goto out;
|
||||||
/*
|
}
|
||||||
* We wrote out the new section, with
|
|
||||||
* a newline, now skip the old
|
|
||||||
* section's length
|
|
||||||
*/
|
|
||||||
output += offset + i;
|
|
||||||
if (strlen(output) > 0) {
|
|
||||||
/*
|
/*
|
||||||
* More content means there's
|
* We wrote out the new section, with
|
||||||
* a declaration to put on the
|
* a newline, now skip the old
|
||||||
* next line; indent with a
|
* section's length
|
||||||
* tab
|
|
||||||
*/
|
*/
|
||||||
output -= 1;
|
output += offset + i;
|
||||||
output[0] = '\t';
|
if (strlen(output) > 0) {
|
||||||
|
/*
|
||||||
|
* More content means there's
|
||||||
|
* a declaration to put on the
|
||||||
|
* next line; indent with a
|
||||||
|
* tab
|
||||||
|
*/
|
||||||
|
output -= 1;
|
||||||
|
output[0] = '\t';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copystr = store_create_section(new_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remove = 0;
|
remove = 0;
|
||||||
@ -2830,11 +2860,30 @@ int git_config_rename_section_in_file(const char *config_filename,
|
|||||||
if (remove)
|
if (remove)
|
||||||
continue;
|
continue;
|
||||||
length = strlen(output);
|
length = strlen(output);
|
||||||
|
|
||||||
|
if (!is_section && copystr.len > 0) {
|
||||||
|
strbuf_add(©str, output, length);
|
||||||
|
}
|
||||||
|
|
||||||
if (write_in_full(out_fd, output, length) < 0) {
|
if (write_in_full(out_fd, output, length) < 0) {
|
||||||
ret = write_error(get_lock_file_path(lock));
|
ret = write_error(get_lock_file_path(lock));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy a trailing section at the end of the config, won't be
|
||||||
|
* flushed by the usual "flush because we have a new section
|
||||||
|
* logic in the loop above.
|
||||||
|
*/
|
||||||
|
if (copystr.len > 0) {
|
||||||
|
if (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {
|
||||||
|
ret = write_error(get_lock_file_path(lock));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
strbuf_reset(©str);
|
||||||
|
}
|
||||||
|
|
||||||
fclose(config_file);
|
fclose(config_file);
|
||||||
config_file = NULL;
|
config_file = NULL;
|
||||||
commit_and_out:
|
commit_and_out:
|
||||||
@ -2850,11 +2899,30 @@ out_no_rollback:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_config_rename_section_in_file(const char *config_filename,
|
||||||
|
const char *old_name, const char *new_name)
|
||||||
|
{
|
||||||
|
return git_config_copy_or_rename_section_in_file(config_filename,
|
||||||
|
old_name, new_name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
int git_config_rename_section(const char *old_name, const char *new_name)
|
int git_config_rename_section(const char *old_name, const char *new_name)
|
||||||
{
|
{
|
||||||
return git_config_rename_section_in_file(NULL, old_name, new_name);
|
return git_config_rename_section_in_file(NULL, old_name, new_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_config_copy_section_in_file(const char *config_filename,
|
||||||
|
const char *old_name, const char *new_name)
|
||||||
|
{
|
||||||
|
return git_config_copy_or_rename_section_in_file(config_filename,
|
||||||
|
old_name, new_name, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_config_copy_section(const char *old_name, const char *new_name)
|
||||||
|
{
|
||||||
|
return git_config_copy_section_in_file(NULL, old_name, new_name);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call this to report error for your variable that should not
|
* Call this to report error for your variable that should not
|
||||||
* get a boolean value (i.e. "[my] var" means "true").
|
* get a boolean value (i.e. "[my] var" means "true").
|
||||||
|
2
config.h
2
config.h
@ -70,6 +70,8 @@ extern int git_config_set_multivar_in_file_gently(const char *, const char *, co
|
|||||||
extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
|
extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
|
||||||
extern int git_config_rename_section(const char *, const char *);
|
extern int git_config_rename_section(const char *, const char *);
|
||||||
extern int git_config_rename_section_in_file(const char *, const char *, const char *);
|
extern int git_config_rename_section_in_file(const char *, const char *, const char *);
|
||||||
|
extern int git_config_copy_section(const char *, const char *);
|
||||||
|
extern int git_config_copy_section_in_file(const char *, const char *, const char *);
|
||||||
extern const char *git_etc_gitconfig(void);
|
extern const char *git_etc_gitconfig(void);
|
||||||
extern int git_env_bool(const char *, int);
|
extern int git_env_bool(const char *, int);
|
||||||
extern unsigned long git_env_ulong(const char *, unsigned long);
|
extern unsigned long git_env_ulong(const char *, unsigned long);
|
||||||
|
11
refs.c
11
refs.c
@ -2036,3 +2036,14 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
|
|||||||
{
|
{
|
||||||
return refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);
|
return refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
|
||||||
|
const char *newref, const char *logmsg)
|
||||||
|
{
|
||||||
|
return refs->be->copy_ref(refs, oldref, newref, logmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
|
||||||
|
{
|
||||||
|
return refs_copy_existing_ref(get_main_ref_store(), oldref, newref, logmsg);
|
||||||
|
}
|
||||||
|
9
refs.h
9
refs.h
@ -442,7 +442,14 @@ char *shorten_unambiguous_ref(const char *refname, int strict);
|
|||||||
/** rename ref, return 0 on success **/
|
/** rename ref, return 0 on success **/
|
||||||
int refs_rename_ref(struct ref_store *refs, const char *oldref,
|
int refs_rename_ref(struct ref_store *refs, const char *oldref,
|
||||||
const char *newref, const char *logmsg);
|
const char *newref, const char *logmsg);
|
||||||
int rename_ref(const char *oldref, const char *newref, const char *logmsg);
|
int rename_ref(const char *oldref, const char *newref,
|
||||||
|
const char *logmsg);
|
||||||
|
|
||||||
|
/** copy ref, return 0 on success **/
|
||||||
|
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
|
||||||
|
const char *newref, const char *logmsg);
|
||||||
|
int copy_existing_ref(const char *oldref, const char *newref,
|
||||||
|
const char *logmsg);
|
||||||
|
|
||||||
int refs_create_symref(struct ref_store *refs, const char *refname,
|
int refs_create_symref(struct ref_store *refs, const char *refname,
|
||||||
const char *target, const char *logmsg);
|
const char *target, const char *logmsg);
|
||||||
|
@ -1258,9 +1258,9 @@ static int commit_ref_update(struct files_ref_store *refs,
|
|||||||
const struct object_id *oid, const char *logmsg,
|
const struct object_id *oid, const char *logmsg,
|
||||||
struct strbuf *err);
|
struct strbuf *err);
|
||||||
|
|
||||||
static int files_rename_ref(struct ref_store *ref_store,
|
static int files_copy_or_rename_ref(struct ref_store *ref_store,
|
||||||
const char *oldrefname, const char *newrefname,
|
const char *oldrefname, const char *newrefname,
|
||||||
const char *logmsg)
|
const char *logmsg, int copy)
|
||||||
{
|
{
|
||||||
struct files_ref_store *refs =
|
struct files_ref_store *refs =
|
||||||
files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
|
files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
|
||||||
@ -1292,8 +1292,12 @@ static int files_rename_ref(struct ref_store *ref_store,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (flag & REF_ISSYMREF) {
|
if (flag & REF_ISSYMREF) {
|
||||||
ret = error("refname %s is a symbolic ref, renaming it is not supported",
|
if (copy)
|
||||||
oldrefname);
|
ret = error("refname %s is a symbolic ref, copying it is not supported",
|
||||||
|
oldrefname);
|
||||||
|
else
|
||||||
|
ret = error("refname %s is a symbolic ref, renaming it is not supported",
|
||||||
|
oldrefname);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {
|
if (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {
|
||||||
@ -1301,13 +1305,19 @@ static int files_rename_ref(struct ref_store *ref_store,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {
|
if (!copy && log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {
|
||||||
ret = error("unable to move logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
|
ret = error("unable to move logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
|
||||||
oldrefname, strerror(errno));
|
oldrefname, strerror(errno));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refs_delete_ref(&refs->base, logmsg, oldrefname,
|
if (copy && log && copy_file(tmp_renamed_log.buf, sb_oldref.buf, 0644)) {
|
||||||
|
ret = error("unable to copy logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
|
||||||
|
oldrefname, strerror(errno));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname,
|
||||||
orig_oid.hash, REF_NODEREF)) {
|
orig_oid.hash, REF_NODEREF)) {
|
||||||
error("unable to delete old %s", oldrefname);
|
error("unable to delete old %s", oldrefname);
|
||||||
goto rollback;
|
goto rollback;
|
||||||
@ -1320,7 +1330,7 @@ static int files_rename_ref(struct ref_store *ref_store,
|
|||||||
* the safety anyway; we want to delete the reference whatever
|
* the safety anyway; we want to delete the reference whatever
|
||||||
* its current value.
|
* its current value.
|
||||||
*/
|
*/
|
||||||
if (!refs_read_ref_full(&refs->base, newrefname,
|
if (!copy && !refs_read_ref_full(&refs->base, newrefname,
|
||||||
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
|
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
|
||||||
oid.hash, NULL) &&
|
oid.hash, NULL) &&
|
||||||
refs_delete_ref(&refs->base, NULL, newrefname,
|
refs_delete_ref(&refs->base, NULL, newrefname,
|
||||||
@ -1351,7 +1361,10 @@ static int files_rename_ref(struct ref_store *ref_store,
|
|||||||
lock = lock_ref_sha1_basic(refs, newrefname, NULL, NULL, NULL,
|
lock = lock_ref_sha1_basic(refs, newrefname, NULL, NULL, NULL,
|
||||||
REF_NODEREF, NULL, &err);
|
REF_NODEREF, NULL, &err);
|
||||||
if (!lock) {
|
if (!lock) {
|
||||||
error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
|
if (copy)
|
||||||
|
error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf);
|
||||||
|
else
|
||||||
|
error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
|
||||||
strbuf_release(&err);
|
strbuf_release(&err);
|
||||||
goto rollback;
|
goto rollback;
|
||||||
}
|
}
|
||||||
@ -1402,6 +1415,22 @@ static int files_rename_ref(struct ref_store *ref_store,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int files_rename_ref(struct ref_store *ref_store,
|
||||||
|
const char *oldrefname, const char *newrefname,
|
||||||
|
const char *logmsg)
|
||||||
|
{
|
||||||
|
return files_copy_or_rename_ref(ref_store, oldrefname,
|
||||||
|
newrefname, logmsg, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int files_copy_ref(struct ref_store *ref_store,
|
||||||
|
const char *oldrefname, const char *newrefname,
|
||||||
|
const char *logmsg)
|
||||||
|
{
|
||||||
|
return files_copy_or_rename_ref(ref_store, oldrefname,
|
||||||
|
newrefname, logmsg, 1);
|
||||||
|
}
|
||||||
|
|
||||||
static int close_ref_gently(struct ref_lock *lock)
|
static int close_ref_gently(struct ref_lock *lock)
|
||||||
{
|
{
|
||||||
if (close_lock_file_gently(&lock->lk))
|
if (close_lock_file_gently(&lock->lk))
|
||||||
@ -3064,6 +3093,7 @@ struct ref_storage_be refs_be_files = {
|
|||||||
files_create_symref,
|
files_create_symref,
|
||||||
files_delete_refs,
|
files_delete_refs,
|
||||||
files_rename_ref,
|
files_rename_ref,
|
||||||
|
files_copy_ref,
|
||||||
|
|
||||||
files_ref_iterator_begin,
|
files_ref_iterator_begin,
|
||||||
files_read_raw_ref,
|
files_read_raw_ref,
|
||||||
|
@ -966,6 +966,13 @@ static int packed_rename_ref(struct ref_store *ref_store,
|
|||||||
die("BUG: packed reference store does not support renaming references");
|
die("BUG: packed reference store does not support renaming references");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int packed_copy_ref(struct ref_store *ref_store,
|
||||||
|
const char *oldrefname, const char *newrefname,
|
||||||
|
const char *logmsg)
|
||||||
|
{
|
||||||
|
die("BUG: packed reference store does not support copying references");
|
||||||
|
}
|
||||||
|
|
||||||
static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store)
|
static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store)
|
||||||
{
|
{
|
||||||
return empty_ref_iterator_begin();
|
return empty_ref_iterator_begin();
|
||||||
@ -1031,6 +1038,7 @@ struct ref_storage_be refs_be_packed = {
|
|||||||
packed_create_symref,
|
packed_create_symref,
|
||||||
packed_delete_refs,
|
packed_delete_refs,
|
||||||
packed_rename_ref,
|
packed_rename_ref,
|
||||||
|
packed_copy_ref,
|
||||||
|
|
||||||
packed_ref_iterator_begin,
|
packed_ref_iterator_begin,
|
||||||
packed_read_raw_ref,
|
packed_read_raw_ref,
|
||||||
|
@ -559,6 +559,9 @@ typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg,
|
|||||||
typedef int rename_ref_fn(struct ref_store *ref_store,
|
typedef int rename_ref_fn(struct ref_store *ref_store,
|
||||||
const char *oldref, const char *newref,
|
const char *oldref, const char *newref,
|
||||||
const char *logmsg);
|
const char *logmsg);
|
||||||
|
typedef int copy_ref_fn(struct ref_store *ref_store,
|
||||||
|
const char *oldref, const char *newref,
|
||||||
|
const char *logmsg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iterate over the references in `ref_store` whose names start with
|
* Iterate over the references in `ref_store` whose names start with
|
||||||
@ -657,6 +660,7 @@ struct ref_storage_be {
|
|||||||
create_symref_fn *create_symref;
|
create_symref_fn *create_symref;
|
||||||
delete_refs_fn *delete_refs;
|
delete_refs_fn *delete_refs;
|
||||||
rename_ref_fn *rename_ref;
|
rename_ref_fn *rename_ref;
|
||||||
|
copy_ref_fn *copy_ref;
|
||||||
|
|
||||||
ref_iterator_begin_fn *iterator_begin;
|
ref_iterator_begin_fn *iterator_begin;
|
||||||
read_raw_ref_fn *read_raw_ref;
|
read_raw_ref_fn *read_raw_ref;
|
||||||
|
@ -381,6 +381,262 @@ test_expect_success 'config information was renamed, too' '
|
|||||||
test_must_fail git config branch.s/s.dummy
|
test_must_fail git config branch.s/s.dummy
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -m correctly renames multiple config sections' '
|
||||||
|
test_when_finished "git checkout master" &&
|
||||||
|
git checkout -b source master &&
|
||||||
|
|
||||||
|
# Assert that a config file with multiple config sections has
|
||||||
|
# those sections preserved...
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
branch.dest.key1=value1
|
||||||
|
some.gar.b=age
|
||||||
|
branch.dest.key2=value2
|
||||||
|
EOF
|
||||||
|
cat >config.branch <<\EOF &&
|
||||||
|
;; Note the lack of -\EOF above & mixed indenting here. This is
|
||||||
|
;; intentional, we are also testing that the formatting of copied
|
||||||
|
;; sections is preserved.
|
||||||
|
|
||||||
|
;; Comment for source. Tabs
|
||||||
|
[branch "source"]
|
||||||
|
;; Comment for the source value
|
||||||
|
key1 = value1
|
||||||
|
;; Comment for some.gar. Spaces
|
||||||
|
[some "gar"]
|
||||||
|
;; Comment for the some.gar value
|
||||||
|
b = age
|
||||||
|
;; Comment for source, again. Mixed tabs/spaces.
|
||||||
|
[branch "source"]
|
||||||
|
;; Comment for the source value, again
|
||||||
|
key2 = value2
|
||||||
|
EOF
|
||||||
|
cat config.branch >>.git/config &&
|
||||||
|
git branch -m source dest &&
|
||||||
|
git config -f .git/config -l | grep -F -e source -e dest -e some.gar >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
# ...and that the comments for those sections are also
|
||||||
|
# preserved.
|
||||||
|
cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
|
||||||
|
sed -n -e "/Note the lack/,\$p" .git/config >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c dumps usage' '
|
||||||
|
test_expect_code 128 git branch -c 2>err &&
|
||||||
|
test_i18ngrep "branch name required" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch --copy dumps usage' '
|
||||||
|
test_expect_code 128 git branch --copy 2>err &&
|
||||||
|
test_i18ngrep "branch name required" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c d e should work' '
|
||||||
|
git branch -l d &&
|
||||||
|
git reflog exists refs/heads/d &&
|
||||||
|
git config branch.d.dummy Hello &&
|
||||||
|
git branch -c d e &&
|
||||||
|
git reflog exists refs/heads/d &&
|
||||||
|
git reflog exists refs/heads/e &&
|
||||||
|
echo Hello >expect &&
|
||||||
|
git config branch.e.dummy >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
echo Hello >expect &&
|
||||||
|
git config branch.d.dummy >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch --copy is a synonym for -c' '
|
||||||
|
git branch -l copy &&
|
||||||
|
git reflog exists refs/heads/copy &&
|
||||||
|
git config branch.copy.dummy Hello &&
|
||||||
|
git branch --copy copy copy-to &&
|
||||||
|
git reflog exists refs/heads/copy &&
|
||||||
|
git reflog exists refs/heads/copy-to &&
|
||||||
|
echo Hello >expect &&
|
||||||
|
git config branch.copy.dummy >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
echo Hello >expect &&
|
||||||
|
git config branch.copy-to.dummy >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c ee ef should copy ee to create branch ef' '
|
||||||
|
git checkout -b ee &&
|
||||||
|
git reflog exists refs/heads/ee &&
|
||||||
|
git config branch.ee.dummy Hello &&
|
||||||
|
git branch -c ee ef &&
|
||||||
|
git reflog exists refs/heads/ee &&
|
||||||
|
git reflog exists refs/heads/ef &&
|
||||||
|
test $(git config branch.ee.dummy) = Hello &&
|
||||||
|
test $(git config branch.ef.dummy) = Hello &&
|
||||||
|
test $(git rev-parse --abbrev-ref HEAD) = ee
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c f/f g/g should work' '
|
||||||
|
git branch -l f/f &&
|
||||||
|
git reflog exists refs/heads/f/f &&
|
||||||
|
git config branch.f/f.dummy Hello &&
|
||||||
|
git branch -c f/f g/g &&
|
||||||
|
git reflog exists refs/heads/f/f &&
|
||||||
|
git reflog exists refs/heads/g/g &&
|
||||||
|
test $(git config branch.f/f.dummy) = Hello &&
|
||||||
|
test $(git config branch.g/g.dummy) = Hello
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c m2 m2 should work' '
|
||||||
|
git branch -l m2 &&
|
||||||
|
git reflog exists refs/heads/m2 &&
|
||||||
|
git config branch.m2.dummy Hello &&
|
||||||
|
git branch -c m2 m2 &&
|
||||||
|
git reflog exists refs/heads/m2 &&
|
||||||
|
test $(git config branch.m2.dummy) = Hello
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c zz zz/zz should fail' '
|
||||||
|
git branch -l zz &&
|
||||||
|
git reflog exists refs/heads/zz &&
|
||||||
|
test_must_fail git branch -c zz zz/zz
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c b/b b should fail' '
|
||||||
|
git branch -l b/b &&
|
||||||
|
test_must_fail git branch -c b/b b
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -C o/q o/p should work when o/p exists' '
|
||||||
|
git branch -l o/q &&
|
||||||
|
git reflog exists refs/heads/o/q &&
|
||||||
|
git reflog exists refs/heads/o/p &&
|
||||||
|
git branch -C o/q o/p
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c -f o/q o/p should work when o/p exists' '
|
||||||
|
git reflog exists refs/heads/o/q &&
|
||||||
|
git reflog exists refs/heads/o/p &&
|
||||||
|
git branch -c -f o/q o/p
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c qq rr/qq should fail when r exists' '
|
||||||
|
git branch qq &&
|
||||||
|
git branch rr &&
|
||||||
|
test_must_fail git branch -c qq rr/qq
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -C b1 b2 should fail when b2 is checked out' '
|
||||||
|
git branch b1 &&
|
||||||
|
git checkout -b b2 &&
|
||||||
|
test_must_fail git branch -C b1 b2
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out' '
|
||||||
|
git checkout -b c1 &&
|
||||||
|
git branch c2 &&
|
||||||
|
git branch -C c1 c2 &&
|
||||||
|
test $(git rev-parse --abbrev-ref HEAD) = c1
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -C c1 c2 should never touch HEAD' '
|
||||||
|
msg="Branch: copied refs/heads/c1 to refs/heads/c2" &&
|
||||||
|
! grep "$msg$" .git/logs/HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -C master should work when master is checked out' '
|
||||||
|
git checkout master &&
|
||||||
|
git branch -C master
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -C master master should work when master is checked out' '
|
||||||
|
git checkout master &&
|
||||||
|
git branch -C master master
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -C master5 master5 should work when master is checked out' '
|
||||||
|
git checkout master &&
|
||||||
|
git branch master5 &&
|
||||||
|
git branch -C master5 master5
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -C ab cd should overwrite existing config for cd' '
|
||||||
|
git branch -l cd &&
|
||||||
|
git reflog exists refs/heads/cd &&
|
||||||
|
git config branch.cd.dummy CD &&
|
||||||
|
git branch -l ab &&
|
||||||
|
git reflog exists refs/heads/ab &&
|
||||||
|
git config branch.ab.dummy AB &&
|
||||||
|
git branch -C ab cd &&
|
||||||
|
git reflog exists refs/heads/ab &&
|
||||||
|
git reflog exists refs/heads/cd &&
|
||||||
|
test $(git config branch.ab.dummy) = AB &&
|
||||||
|
test $(git config branch.cd.dummy) = AB
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git branch -c correctly copies multiple config sections' '
|
||||||
|
FOO=1 &&
|
||||||
|
export FOO &&
|
||||||
|
test_when_finished "git checkout master" &&
|
||||||
|
git checkout -b source2 master &&
|
||||||
|
|
||||||
|
# Assert that a config file with multiple config sections has
|
||||||
|
# those sections preserved...
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
branch.source2.key1=value1
|
||||||
|
branch.dest2.key1=value1
|
||||||
|
more.gar.b=age
|
||||||
|
branch.source2.key2=value2
|
||||||
|
branch.dest2.key2=value2
|
||||||
|
EOF
|
||||||
|
cat >config.branch <<\EOF &&
|
||||||
|
;; Note the lack of -\EOF above & mixed indenting here. This is
|
||||||
|
;; intentional, we are also testing that the formatting of copied
|
||||||
|
;; sections is preserved.
|
||||||
|
|
||||||
|
;; Comment for source2. Tabs
|
||||||
|
[branch "source2"]
|
||||||
|
;; Comment for the source2 value
|
||||||
|
key1 = value1
|
||||||
|
;; Comment for more.gar. Spaces
|
||||||
|
[more "gar"]
|
||||||
|
;; Comment for the more.gar value
|
||||||
|
b = age
|
||||||
|
;; Comment for source2, again. Mixed tabs/spaces.
|
||||||
|
[branch "source2"]
|
||||||
|
;; Comment for the source2 value, again
|
||||||
|
key2 = value2
|
||||||
|
EOF
|
||||||
|
cat config.branch >>.git/config &&
|
||||||
|
git branch -c source2 dest2 &&
|
||||||
|
git config -f .git/config -l | grep -F -e source2 -e dest2 -e more.gar >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
# ...and that the comments and formatting for those sections
|
||||||
|
# is also preserved.
|
||||||
|
cat >expect <<\EOF &&
|
||||||
|
;; Comment for source2. Tabs
|
||||||
|
[branch "source2"]
|
||||||
|
;; Comment for the source2 value
|
||||||
|
key1 = value1
|
||||||
|
;; Comment for more.gar. Spaces
|
||||||
|
[branch "dest2"]
|
||||||
|
;; Comment for the source2 value
|
||||||
|
key1 = value1
|
||||||
|
;; Comment for more.gar. Spaces
|
||||||
|
[more "gar"]
|
||||||
|
;; Comment for the more.gar value
|
||||||
|
b = age
|
||||||
|
;; Comment for source2, again. Mixed tabs/spaces.
|
||||||
|
[branch "source2"]
|
||||||
|
;; Comment for the source2 value, again
|
||||||
|
key2 = value2
|
||||||
|
[branch "dest2"]
|
||||||
|
;; Comment for the source2 value, again
|
||||||
|
key2 = value2
|
||||||
|
EOF
|
||||||
|
sed -n -e "/Comment for source2/,\$p" .git/config >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'deleting a symref' '
|
test_expect_success 'deleting a symref' '
|
||||||
git branch target &&
|
git branch target &&
|
||||||
git symbolic-ref refs/heads/symref refs/heads/target &&
|
git symbolic-ref refs/heads/symref refs/heads/target &&
|
||||||
|
Loading…
Reference in New Issue
Block a user