Skip to content

Commit 6f8a6c3

Browse files
authored
Merge pull request libgit2#4392 from libgit2/cmn/config-write-preserve-case
Preserve the input casing when writing config files
2 parents 9e3fb59 + 94e30d9 commit 6f8a6c3

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ v0.26 + 1
66
* Improved `p_unlink` in `posix_w32.c` to try and make a file writable
77
before sleeping in the retry loop to prevent unnecessary calls to sleep.
88

9+
* Writing to a configuration file now preserves the case of the key given by the
10+
caller for the case-insensitive portions of the key (existing sections are
11+
used even if they don't match).
12+
913
### API additions
1014

1115
* `git_remote_create_detached()` creates a remote that is not associated

src/config_file.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ typedef struct {
120120
} diskfile_readonly_backend;
121121

122122
static int config_read(git_strmap *values, struct config_file *file, git_config_level_t level, int depth);
123-
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
123+
static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
124124
static char *escape_value(const char *ptr);
125125

126126
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
513513
GITERR_CHECK_ALLOC(esc_value);
514514
}
515515

516-
if ((ret = config_write(b, key, NULL, esc_value)) < 0)
516+
if ((ret = config_write(b, name, key, NULL, esc_value)) < 0)
517517
goto out;
518518

519519
ret = config_refresh(cfg);
@@ -591,7 +591,7 @@ static int config_set_multivar(
591591
}
592592

593593
/* If we do have it, set call config_write() and reload */
594-
if ((result = config_write(b, key, &preg, value)) < 0)
594+
if ((result = config_write(b, name, key, &preg, value)) < 0)
595595
goto out;
596596

597597
result = config_refresh(cfg);
@@ -641,7 +641,7 @@ static int config_delete(git_config_backend *cfg, const char *name)
641641
return -1;
642642
}
643643

644-
if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
644+
if ((result = config_write(b, name, var->entry->name, NULL, NULL)) < 0)
645645
return result;
646646

647647
return config_refresh(cfg);
@@ -682,7 +682,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
682682
goto out;
683683
}
684684

685-
if ((result = config_write(b, key, &preg, NULL)) < 0)
685+
if ((result = config_write(b, name, key, &preg, NULL)) < 0)
686686
goto out;
687687

688688
result = config_refresh(cfg);
@@ -1739,7 +1739,9 @@ struct write_data {
17391739
git_buf buffered_comment;
17401740
unsigned int in_section : 1,
17411741
preg_replaced : 1;
1742+
const char *orig_section;
17421743
const char *section;
1744+
const char *orig_name;
17431745
const char *name;
17441746
const regex_t *preg;
17451747
const char *value;
@@ -1767,7 +1769,7 @@ static int write_value(struct write_data *write_data)
17671769

17681770
q = quotes_for_value(write_data->value);
17691771
result = git_buf_printf(write_data->buf,
1770-
"\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
1772+
"\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q);
17711773

17721774
/* If we are updating a single name/value, we're done. Setting `value`
17731775
* to `NULL` will prevent us from trying to write it again later (in
@@ -1898,7 +1900,7 @@ static int write_on_eof(
18981900
if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
18991901
/* write the section header unless we're already in it */
19001902
if (!current_section || strcmp(current_section, write_data->section))
1901-
result = write_section(write_data->buf, write_data->section);
1903+
result = write_section(write_data->buf, write_data->orig_section);
19021904

19031905
if (!result)
19041906
result = write_value(write_data);
@@ -1910,10 +1912,10 @@ static int write_on_eof(
19101912
/*
19111913
* This is pretty much the parsing, except we write out anything we don't have
19121914
*/
1913-
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
1915+
static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char* value)
19141916
{
19151917
int result;
1916-
char *section, *name, *ldot;
1918+
char *orig_section, *section, *orig_name, *name, *ldot;
19171919
git_filebuf file = GIT_FILEBUF_INIT;
19181920
git_buf buf = GIT_BUF_INIT;
19191921
struct reader reader;
@@ -1953,18 +1955,27 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
19531955
ldot = strrchr(key, '.');
19541956
name = ldot + 1;
19551957
section = git__strndup(key, ldot - key);
1958+
GITERR_CHECK_ALLOC(section);
1959+
1960+
ldot = strrchr(orig_key, '.');
1961+
orig_name = ldot + 1;
1962+
orig_section = git__strndup(orig_key, ldot - orig_key);
1963+
GITERR_CHECK_ALLOC(orig_section);
19561964

19571965
write_data.buf = &buf;
19581966
git_buf_init(&write_data.buffered_comment, 0);
1967+
write_data.orig_section = orig_section;
19591968
write_data.section = section;
19601969
write_data.in_section = 0;
19611970
write_data.preg_replaced = 0;
1971+
write_data.orig_name = orig_name;
19621972
write_data.name = name;
19631973
write_data.preg = preg;
19641974
write_data.value = value;
19651975

19661976
result = config_parse(&reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
19671977
git__free(section);
1978+
git__free(orig_section);
19681979
git_buf_free(&write_data.buffered_comment);
19691980

19701981
if (result < 0) {

tests/config/write.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,3 +722,26 @@ void test_config_write__repeated(void)
722722

723723
git_config_free(cfg);
724724
}
725+
726+
void test_config_write__preserve_case(void)
727+
{
728+
const char *filename = "config-preserve-case";
729+
git_config *cfg;
730+
git_buf result = GIT_BUF_INIT;
731+
const char *expected = "[sOMe]\n" \
732+
"\tThInG = foo\n" \
733+
"\tOtheR = thing\n";
734+
735+
cl_git_pass(git_config_open_ondisk(&cfg, filename));
736+
cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
737+
cl_git_pass(git_config_set_string(cfg, "SomE.OtheR", "thing"));
738+
git_config_free(cfg);
739+
740+
cl_git_pass(git_config_open_ondisk(&cfg, filename));
741+
742+
cl_git_pass(git_futils_readbuffer(&result, filename));
743+
cl_assert_equal_s(expected, result.ptr);
744+
git_buf_free(&result);
745+
746+
git_config_free(cfg);
747+
}

0 commit comments

Comments
 (0)