Merge pull request #4392 from libgit2/cmn/config-write-preserve-case Preserve the input casing when writing config files
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 43724b5..b07a0a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@ v0.26 + 1
* Improved `p_unlink` in `posix_w32.c` to try and make a file writable
before sleeping in the retry loop to prevent unnecessary calls to sleep.
+* Writing to a configuration file now preserves the case of the key given by the
+ caller for the case-insensitive portions of the key (existing sections are
+ used even if they don't match).
+
### API additions
* `git_remote_create_detached()` creates a remote that is not associated
diff --git a/src/config_file.c b/src/config_file.c
index 00d6d9e..9a6d815 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -120,7 +120,7 @@ typedef struct {
} diskfile_readonly_backend;
static int config_read(git_strmap *values, struct config_file *file, git_config_level_t level, int depth);
-static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
+static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
static char *escape_value(const char *ptr);
int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
@@ -513,7 +513,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
GITERR_CHECK_ALLOC(esc_value);
}
- if ((ret = config_write(b, key, NULL, esc_value)) < 0)
+ if ((ret = config_write(b, name, key, NULL, esc_value)) < 0)
goto out;
ret = config_refresh(cfg);
@@ -591,7 +591,7 @@ static int config_set_multivar(
}
/* If we do have it, set call config_write() and reload */
- if ((result = config_write(b, key, &preg, value)) < 0)
+ if ((result = config_write(b, name, key, &preg, value)) < 0)
goto out;
result = config_refresh(cfg);
@@ -641,7 +641,7 @@ static int config_delete(git_config_backend *cfg, const char *name)
return -1;
}
- if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
+ if ((result = config_write(b, name, var->entry->name, NULL, NULL)) < 0)
return result;
return config_refresh(cfg);
@@ -682,7 +682,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
goto out;
}
- if ((result = config_write(b, key, &preg, NULL)) < 0)
+ if ((result = config_write(b, name, key, &preg, NULL)) < 0)
goto out;
result = config_refresh(cfg);
@@ -1739,7 +1739,9 @@ struct write_data {
git_buf buffered_comment;
unsigned int in_section : 1,
preg_replaced : 1;
+ const char *orig_section;
const char *section;
+ const char *orig_name;
const char *name;
const regex_t *preg;
const char *value;
@@ -1767,7 +1769,7 @@ static int write_value(struct write_data *write_data)
q = quotes_for_value(write_data->value);
result = git_buf_printf(write_data->buf,
- "\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
+ "\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q);
/* If we are updating a single name/value, we're done. Setting `value`
* to `NULL` will prevent us from trying to write it again later (in
@@ -1898,7 +1900,7 @@ static int write_on_eof(
if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
/* write the section header unless we're already in it */
if (!current_section || strcmp(current_section, write_data->section))
- result = write_section(write_data->buf, write_data->section);
+ result = write_section(write_data->buf, write_data->orig_section);
if (!result)
result = write_value(write_data);
@@ -1910,10 +1912,10 @@ static int write_on_eof(
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
-static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
+static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char* value)
{
int result;
- char *section, *name, *ldot;
+ char *orig_section, *section, *orig_name, *name, *ldot;
git_filebuf file = GIT_FILEBUF_INIT;
git_buf buf = GIT_BUF_INIT;
struct reader reader;
@@ -1953,18 +1955,27 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
ldot = strrchr(key, '.');
name = ldot + 1;
section = git__strndup(key, ldot - key);
+ GITERR_CHECK_ALLOC(section);
+
+ ldot = strrchr(orig_key, '.');
+ orig_name = ldot + 1;
+ orig_section = git__strndup(orig_key, ldot - orig_key);
+ GITERR_CHECK_ALLOC(orig_section);
write_data.buf = &buf;
git_buf_init(&write_data.buffered_comment, 0);
+ write_data.orig_section = orig_section;
write_data.section = section;
write_data.in_section = 0;
write_data.preg_replaced = 0;
+ write_data.orig_name = orig_name;
write_data.name = name;
write_data.preg = preg;
write_data.value = value;
result = config_parse(&reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
git__free(section);
+ git__free(orig_section);
git_buf_free(&write_data.buffered_comment);
if (result < 0) {
diff --git a/tests/config/write.c b/tests/config/write.c
index 56ef2e9..01b018b 100644
--- a/tests/config/write.c
+++ b/tests/config/write.c
@@ -722,3 +722,26 @@ void test_config_write__repeated(void)
git_config_free(cfg);
}
+
+void test_config_write__preserve_case(void)
+{
+ const char *filename = "config-preserve-case";
+ git_config *cfg;
+ git_buf result = GIT_BUF_INIT;
+ const char *expected = "[sOMe]\n" \
+ "\tThInG = foo\n" \
+ "\tOtheR = thing\n";
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
+ cl_git_pass(git_config_set_string(cfg, "SomE.OtheR", "thing"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+
+ cl_git_pass(git_futils_readbuffer(&result, filename));
+ cl_assert_equal_s(expected, result.ptr);
+ git_buf_free(&result);
+
+ git_config_free(cfg);
+}