config.c: create missing parent directories when modifying config files

'git config' (--add / --unset etc.) automatically creates missing config
files. However, it fails with a misleading error message "could not lock
config file" if the parent directory doesn't exist.

Also create missing parent directories.

This is particularly important when calling

	git config -f /non/existing/directory/config ...

We *must not* create the leading directories when locking a config file.
It is the caller's responsibility to ensure that the directory exists,
just like it is the caller's responsibility to call `git init` before
running repository operations.

Point in case: if we simply create all leading directories, calling
`git config user.name me` *outside* of a Git worktree will *create*
.git/!

This fixes https://github.com/git-for-windows/git/issues/643 and
https://groups.google.com/forum/#!topic/git-for-windows/fVRdnDIKVuw

[jes: prevented bogus .git/ directories from being created]

Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Karsten Blees
2015-05-22 15:20:21 +02:00
committed by Johannes Schindelin
parent 84470b9b54
commit 498f679adc

View File

@@ -1971,6 +1971,20 @@ int git_config_key_is_valid(const char *key)
return !git_config_parse_key_1(key, NULL, NULL, 1);
}
static int lock_config_file(const char *config_filename,
struct lock_file **result)
{
int fd;
*result = xcalloc(1, sizeof(struct lock_file));
fd = hold_lock_file_for_update(*result, config_filename, 0);
if (fd < 0)
error("could not lock config file %s: %s", config_filename,
strerror(errno));
return fd;
}
/*
* If value==NULL, unset in (remove from) config,
* if value_regex!=NULL, disregard key/value pairs where value does not match.
@@ -2022,8 +2036,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
* The lock serves a purpose in addition to locking: the new
* contents of .git/config will be written into it.
*/
lock = xcalloc(1, sizeof(struct lock_file));
fd = hold_lock_file_for_update(lock, config_filename, 0);
fd = lock_config_file(config_filename, &lock);
if (fd < 0) {
error_errno("could not lock config file %s", config_filename);
free(store.key);
@@ -2324,12 +2337,9 @@ int git_config_rename_section_in_file(const char *config_filename,
if (!config_filename)
config_filename = filename_buf = git_pathdup("config");
lock = xcalloc(1, sizeof(struct lock_file));
out_fd = hold_lock_file_for_update(lock, config_filename, 0);
if (out_fd < 0) {
ret = error("could not lock config file %s", config_filename);
out_fd = lock_config_file(config_filename, &lock);
if (out_fd < 0)
goto out;
}
if (!(config_file = fopen(config_filename, "rb"))) {
/* no config file means nothing to rename, no error */