diff --git a/src/config_file.c b/src/config_file.c index a3fec1b34..46f21c0f1 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1711,6 +1711,7 @@ static const char *quotes_for_value(const char *value) struct write_data { git_buf *buf; + git_buf buffered_comment; unsigned int in_section : 1, preg_replaced : 1; const char *section; @@ -1719,16 +1720,21 @@ struct write_data { const char *value; }; -static int write_line(struct write_data *write_data, const char *line, size_t line_len) +static int write_line_to(git_buf *buf, const char *line, size_t line_len) { - int result = git_buf_put(write_data->buf, line, line_len); + int result = git_buf_put(buf, line, line_len); if (!result && line_len && line[line_len-1] != '\n') - result = git_buf_printf(write_data->buf, "\n"); + result = git_buf_printf(buf, "\n"); return result; } +static int write_line(struct write_data *write_data, const char *line, size_t line_len) +{ + return write_line_to(write_data->buf, line, line_len); +} + static int write_value(struct write_data *write_data) { const char *q; @@ -1770,6 +1776,14 @@ static int write_on_section( write_data->in_section = strcmp(current_section, write_data->section) == 0; + /* + * If there were comments just before this section, dump them as well. + */ + if (!result) { + result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size); + git_buf_clear(&write_data->buffered_comment); + } + if (!result) result = write_line(write_data, line, line_len); @@ -1787,10 +1801,19 @@ static int write_on_variable( { struct write_data *write_data = (struct write_data *)data; bool has_matched = false; + int error; GIT_UNUSED(reader); GIT_UNUSED(current_section); + /* + * If there were comments just before this variable, let's dump them as well. + */ + if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0) + return error; + + git_buf_clear(&write_data->buffered_comment); + /* See if we are to update this name/value pair; first examine name */ if (write_data->in_section && strcasecmp(write_data->name, var_name) == 0) @@ -1825,7 +1848,7 @@ static int write_on_comment(struct reader **reader, const char *line, size_t lin GIT_UNUSED(reader); write_data = (struct write_data *)data; - return write_line(write_data, line, line_len); + return write_line_to(&write_data->buffered_comment, line, line_len); } static int write_on_eof(struct reader **reader, void *data) @@ -1835,6 +1858,12 @@ static int write_on_eof(struct reader **reader, void *data) GIT_UNUSED(reader); + /* + * If we've buffered comments when reaching EOF, make sure to dump them. + */ + if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0) + return result; + /* If we are at the EOF and have not written our value (again, for a * simple name/value set, not a multivar) then we have never seen the * section in question and should create a new section and write the @@ -1892,6 +1921,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p section = git__strndup(key, ldot - key); write_data.buf = &buf; + git_buf_init(&write_data.buffered_comment, 0); write_data.section = section; write_data.in_section = 0; write_data.preg_replaced = 0; @@ -1901,6 +1931,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data); git__free(section); + git_buf_free(&write_data.buffered_comment); if (result < 0) { git_filebuf_cleanup(&file); diff --git a/tests/config/write.c b/tests/config/write.c index 9ad11ab27..e634aa326 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -530,6 +530,9 @@ void test_config_write__outside_change(void) git_config_free(cfg); } +#define FOO_COMMENT \ + "; another comment!\n" + #define SECTION_FOO \ "\n" \ " \n" \ @@ -537,7 +540,8 @@ void test_config_write__outside_change(void) " # here's a comment\n" \ "\tname = \"value\"\n" \ " name2 = \"value2\"\n" \ - "; another comment!\n" + +#define SECTION_FOO_WITH_COMMENT SECTION_FOO FOO_COMMENT #define SECTION_BAR \ "[section \"bar\"]\t\n" \ @@ -553,7 +557,7 @@ void test_config_write__preserves_whitespace_and_comments(void) git_buf newfile = GIT_BUF_INIT; /* This config can occur after removing and re-adding the origin remote */ - const char *file_content = SECTION_FOO SECTION_BAR; + const char *file_content = SECTION_FOO_WITH_COMMENT SECTION_BAR; /* Write the test config and make sure the expected entry exists */ cl_git_mkfile(file_name, file_content); @@ -567,9 +571,10 @@ void test_config_write__preserves_whitespace_and_comments(void) cl_assert_equal_strn(SECTION_FOO, n, strlen(SECTION_FOO)); n += strlen(SECTION_FOO); - cl_assert_equal_strn("\tother = otherval\n", n, strlen("\tother = otherval\n")); n += strlen("\tother = otherval\n"); + cl_assert_equal_strn(FOO_COMMENT, n, strlen(FOO_COMMENT)); + n += strlen(FOO_COMMENT); cl_assert_equal_strn(SECTION_BAR, n, strlen(SECTION_BAR)); n += strlen(SECTION_BAR);