From 5463caab15fb795da2aab01827419b094146d1c4 Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Sun, 18 Jun 2017 23:16:31 +0200 Subject: config: create a function to format section headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor out the logic which creates section headers in the config file, e.g. the 'branch.foo' key will be turned into '[branch "foo"]'. This introduces no function changes, but is needed for a later change which adds support for copying branch sections in the config file. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Ramsay Jones Signed-off-by: Sahil Dua Signed-off-by: Junio C Hamano --- config.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 146cb3452..856e42e4d 100644 --- a/config.c +++ b/config.c @@ -2169,10 +2169,10 @@ static int write_error(const char *filename) return 4; } -static int store_write_section(int fd, const char *key) +static struct strbuf store_create_section(const char *key) { const char *dot; - int i, success; + int i; struct strbuf sb = STRBUF_INIT; dot = memchr(key, '.', store.baselen); @@ -2188,6 +2188,15 @@ static int store_write_section(int fd, const char *key) strbuf_addf(&sb, "[%.*s]\n", store.baselen, key); } + return sb; +} + +static int store_write_section(int fd, const char *key) +{ + int success; + + struct strbuf sb = store_create_section(key); + success = write_in_full(fd, sb.buf, sb.len) == sb.len; strbuf_release(&sb); -- cgit v1.2.1 From 52d59cc6452ed1aeec91f4c168a853ea8d9d5496 Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Sun, 18 Jun 2017 23:19:16 +0200 Subject: branch: add a --copy (-c) option to go with --move (-m) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the ability to --copy a branch and its reflog and configuration, this uses the same underlying machinery as the --move (-m) option except the reflog and configuration is copied instead of being moved. This is useful for e.g. copying a topic branch to a new version, e.g. work to work-2 after submitting the work topic to the list, while preserving all the tracking info and other configuration that goes with the branch, and unlike --move keeping the other already-submitted branch around for reference. Like --move, when the source branch is the currently checked out branch the HEAD is moved to the destination branch. In the case of --move we don't really have a choice (other than remaining on a detached HEAD) and in order to keep the functionality consistent, we are doing it in similar way for --copy too. The most common usage of this feature is expected to be moving to a new topic branch which is a copy of the current one, in that case moving to the target branch is what the user wants, and doesn't unexpectedly behave differently than --move would. One outstanding caveat of this implementation is that: git checkout maint && git checkout master && git branch -c topic && git checkout - Will check out 'maint' instead of 'master'. This is because the @{-N} feature (or its -1 shorthand "-") relies on HEAD reflogs created by the checkout command, so in this case we'll checkout maint instead of master, as the user might expect. What to do about that is left to a future change. Helped-by: Ævar Arnfjörð Bjarmason Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Sahil Dua Signed-off-by: Junio C Hamano --- config.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 20 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 856e42e4d..d0d5aa1dc 100644 --- a/config.c +++ b/config.c @@ -2638,8 +2638,8 @@ static int section_name_is_ok(const char *name) } /* if new_name == NULL, the section is removed instead */ -int git_config_rename_section_in_file(const char *config_filename, - const char *old_name, const char *new_name) +static int git_config_copy_or_rename_section_in_file(const char *config_filename, + const char *old_name, const char *new_name, int copy) { int ret = 0, remove = 0; char *filename_buf = NULL; @@ -2648,6 +2648,7 @@ int git_config_rename_section_in_file(const char *config_filename, char buf[1024]; FILE *config_file = NULL; struct stat st; + struct strbuf copystr = STRBUF_INIT; if (new_name && !section_name_is_ok(new_name)) { ret = error("invalid section name: %s", new_name); @@ -2683,12 +2684,30 @@ int git_config_rename_section_in_file(const char *config_filename, while (fgets(buf, sizeof(buf), config_file)) { int i; int length; + int is_section = 0; char *output = buf; for (i = 0; buf[i] && isspace(buf[i]); i++) ; /* do nothing */ if (buf[i] == '[') { /* 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) { ret++; if (new_name == NULL) { @@ -2696,25 +2715,30 @@ int git_config_rename_section_in_file(const char *config_filename, continue; } store.baselen = strlen(new_name); - if (!store_write_section(out_fd, new_name)) { - 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) { + if (!copy) { + if (!store_write_section(out_fd, new_name)) { + ret = write_error(get_lock_file_path(lock)); + goto out; + } + /* - * More content means there's - * a declaration to put on the - * next line; indent with a - * tab + * We wrote out the new section, with + * a newline, now skip the old + * section's length */ - output -= 1; - output[0] = '\t'; + output += offset + i; + 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; @@ -2722,11 +2746,30 @@ int git_config_rename_section_in_file(const char *config_filename, if (remove) continue; length = strlen(output); + + if (!is_section && copystr.len > 0) { + strbuf_add(©str, output, length); + } + if (write_in_full(out_fd, output, length) != length) { ret = write_error(get_lock_file_path(lock)); 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); config_file = NULL; commit_and_out: @@ -2742,11 +2785,30 @@ out_no_rollback: 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) { 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 * get a boolean value (i.e. "[my] var" means "true"). -- cgit v1.2.1