Commit 500ec543 by Edward Thomson Committed by Edward Thomson

checkout: hold seen dir paths in a map

parent f58cc280
...@@ -29,6 +29,10 @@ ...@@ -29,6 +29,10 @@
#include "merge_file.h" #include "merge_file.h"
#include "path.h" #include "path.h"
#include "attr.h" #include "attr.h"
#include "pool.h"
#include "strmap.h"
GIT__USE_STRMAP;
/* See docs/checkout-internals.md for more information */ /* See docs/checkout-internals.md for more information */
...@@ -69,7 +73,7 @@ typedef struct { ...@@ -69,7 +73,7 @@ typedef struct {
size_t total_steps; size_t total_steps;
size_t completed_steps; size_t completed_steps;
git_checkout_perfdata perfdata; git_checkout_perfdata perfdata;
git_buf last_mkdir; git_strmap *mkdir_map;
git_attr_session attr_session; git_attr_session attr_session;
} checkout_data; } checkout_data;
...@@ -1293,25 +1297,6 @@ fail: ...@@ -1293,25 +1297,6 @@ fail:
return error; return error;
} }
static int checkout_mkdir(
checkout_data *data,
const char *path,
const char *base,
mode_t mode,
unsigned int flags)
{
struct git_futils_mkdir_perfdata mkdir_perfdata = {0};
int error = git_futils_mkdir_withperf(
path, base, mode, flags, &mkdir_perfdata);
data->perfdata.mkdir_calls += mkdir_perfdata.mkdir_calls;
data->perfdata.stat_calls += mkdir_perfdata.stat_calls;
data->perfdata.chmod_calls += mkdir_perfdata.chmod_calls;
return error;
}
static bool should_remove_existing(checkout_data *data) static bool should_remove_existing(checkout_data *data)
{ {
int ignorecase = 0; int ignorecase = 0;
...@@ -1327,31 +1312,43 @@ static bool should_remove_existing(checkout_data *data) ...@@ -1327,31 +1312,43 @@ static bool should_remove_existing(checkout_data *data)
#define MKDIR_REMOVE_EXISTING \ #define MKDIR_REMOVE_EXISTING \
MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS
static int checkout_mkdir(
checkout_data *data,
const char *path,
const char *base,
mode_t mode,
unsigned int flags)
{
struct git_futils_mkdir_options mkdir_opts = {0};
int error;
mkdir_opts.dir_map = data->mkdir_map;
mkdir_opts.pool = &data->pool;
error = git_futils_mkdir_ext(
path, base, mode, flags, &mkdir_opts);
data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls;
data->perfdata.stat_calls += mkdir_opts.perfdata.stat_calls;
data->perfdata.chmod_calls += mkdir_opts.perfdata.chmod_calls;
return error;
}
static int mkpath2file( static int mkpath2file(
checkout_data *data, const char *path, unsigned int mode) checkout_data *data, const char *path, unsigned int mode)
{ {
git_buf *mkdir_path = &data->tmp;
struct stat st; struct stat st;
bool remove_existing = should_remove_existing(data); bool remove_existing = should_remove_existing(data);
unsigned int flags =
(remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL) |
GIT_MKDIR_SKIP_LAST;
int error; int error;
if ((error = git_buf_sets(mkdir_path, path)) < 0) if ((error = checkout_mkdir(
data, path, data->opts.target_directory, mode, flags)) < 0)
return error; return error;
git_buf_rtruncate_at_char(mkdir_path, '/');
if (!data->last_mkdir.size ||
data->last_mkdir.size != mkdir_path->size ||
memcmp(mkdir_path->ptr, data->last_mkdir.ptr, mkdir_path->size) != 0) {
if ((error = checkout_mkdir(
data, mkdir_path->ptr, data->opts.target_directory, mode,
remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0)
return error;
git_buf_swap(&data->last_mkdir, mkdir_path);
}
if (remove_existing) { if (remove_existing) {
data->perfdata.stat_calls++; data->perfdata.stat_calls++;
...@@ -2215,13 +2212,16 @@ static void checkout_data_clear(checkout_data *data) ...@@ -2215,13 +2212,16 @@ static void checkout_data_clear(checkout_data *data)
git__free(data->pfx); git__free(data->pfx);
data->pfx = NULL; data->pfx = NULL;
git_buf_free(&data->last_mkdir); git_strmap_free(data->mkdir_map);
git_buf_free(&data->path); git_buf_free(&data->path);
git_buf_free(&data->tmp); git_buf_free(&data->tmp);
git_index_free(data->index); git_index_free(data->index);
data->index = NULL; data->index = NULL;
git_strmap_free(data->mkdir_map);
git_attr_session__free(&data->attr_session); git_attr_session__free(&data->attr_session);
} }
...@@ -2360,7 +2360,8 @@ static int checkout_data_init( ...@@ -2360,7 +2360,8 @@ static int checkout_data_init(
(error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 || (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 ||
(error = git_pool_init(&data->pool, 1, 0)) < 0 || (error = git_pool_init(&data->pool, 1, 0)) < 0 ||
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 || (error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
(error = git_path_to_dir(&data->path)) < 0) (error = git_path_to_dir(&data->path)) < 0 ||
(error = git_strmap_alloc(&data->mkdir_map)) < 0)
goto cleanup; goto cleanup;
data->workdir_len = git_buf_len(&data->path); data->workdir_len = git_buf_len(&data->path);
......
...@@ -7,11 +7,14 @@ ...@@ -7,11 +7,14 @@
#include "common.h" #include "common.h"
#include "fileops.h" #include "fileops.h"
#include "global.h" #include "global.h"
#include "strmap.h"
#include <ctype.h> #include <ctype.h>
#if GIT_WIN32 #if GIT_WIN32
#include "win32/findfile.h" #include "win32/findfile.h"
#endif #endif
GIT__USE_STRMAP;
int git_futils_mkpath2file(const char *file_path, const mode_t mode) int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{ {
return git_futils_mkdir( return git_futils_mkdir(
...@@ -321,12 +324,12 @@ GIT_INLINE(int) validate_existing( ...@@ -321,12 +324,12 @@ GIT_INLINE(int) validate_existing(
return 0; return 0;
} }
int git_futils_mkdir_withperf( int git_futils_mkdir_ext(
const char *path, const char *path,
const char *base, const char *base,
mode_t mode, mode_t mode,
uint32_t flags, uint32_t flags,
struct git_futils_mkdir_perfdata *perfdata) struct git_futils_mkdir_options *opts)
{ {
int error = -1; int error = -1;
git_buf make_path = GIT_BUF_INIT; git_buf make_path = GIT_BUF_INIT;
...@@ -394,11 +397,14 @@ int git_futils_mkdir_withperf( ...@@ -394,11 +397,14 @@ int git_futils_mkdir_withperf(
*tail = '\0'; *tail = '\0';
st.st_mode = 0; st.st_mode = 0;
if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr))
continue;
/* See what's going on with this path component */ /* See what's going on with this path component */
perfdata->stat_calls++; opts->perfdata.stat_calls++;
if (p_lstat(make_path.ptr, &st) < 0) { if (p_lstat(make_path.ptr, &st) < 0) {
perfdata->mkdir_calls++; opts->perfdata.mkdir_calls++;
if (errno != ENOENT || p_mkdir(make_path.ptr, mode) < 0) { if (errno != ENOENT || p_mkdir(make_path.ptr, mode) < 0) {
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
...@@ -416,7 +422,7 @@ int git_futils_mkdir_withperf( ...@@ -416,7 +422,7 @@ int git_futils_mkdir_withperf(
} }
if ((error = validate_existing( if ((error = validate_existing(
make_path.ptr, &st, mode, flags, perfdata)) < 0) make_path.ptr, &st, mode, flags, &opts->perfdata)) < 0)
goto done; goto done;
} }
...@@ -425,7 +431,7 @@ int git_futils_mkdir_withperf( ...@@ -425,7 +431,7 @@ int git_futils_mkdir_withperf(
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) && (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
st.st_mode != mode) { st.st_mode != mode) {
perfdata->chmod_calls++; opts->perfdata.chmod_calls++;
if ((error = p_chmod(make_path.ptr, mode)) < 0 && if ((error = p_chmod(make_path.ptr, mode)) < 0 &&
lastch == '\0') { lastch == '\0') {
...@@ -434,6 +440,17 @@ int git_futils_mkdir_withperf( ...@@ -434,6 +440,17 @@ int git_futils_mkdir_withperf(
goto done; goto done;
} }
} }
if (opts->dir_map && opts->pool) {
char *cache_path = git_pool_malloc(opts->pool, make_path.size + 1);
GITERR_CHECK_ALLOC(cache_path);
memcpy(cache_path, make_path.ptr, make_path.size + 1);
git_strmap_insert(opts->dir_map, cache_path, cache_path, error);
if (error < 0)
goto done;
}
} }
error = 0; error = 0;
...@@ -441,7 +458,7 @@ int git_futils_mkdir_withperf( ...@@ -441,7 +458,7 @@ int git_futils_mkdir_withperf(
/* check that full path really is a directory if requested & needed */ /* check that full path really is a directory if requested & needed */
if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 && if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
lastch != '\0') { lastch != '\0') {
perfdata->stat_calls++; opts->perfdata.stat_calls++;
if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
giterr_set(GITERR_OS, "Path is not a directory '%s'", giterr_set(GITERR_OS, "Path is not a directory '%s'",
...@@ -461,8 +478,8 @@ int git_futils_mkdir( ...@@ -461,8 +478,8 @@ int git_futils_mkdir(
mode_t mode, mode_t mode,
uint32_t flags) uint32_t flags)
{ {
struct git_futils_mkdir_perfdata perfdata = {0}; struct git_futils_mkdir_options options = {0};
return git_futils_mkdir_withperf(path, base, mode, flags, &perfdata); return git_futils_mkdir_ext(path, base, mode, flags, &options);
} }
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode) int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#include "map.h" #include "map.h"
#include "posix.h" #include "posix.h"
#include "path.h" #include "path.h"
#include "pool.h"
#include "strmap.h"
/** /**
* Filebuffer methods * Filebuffer methods
...@@ -95,6 +97,13 @@ struct git_futils_mkdir_perfdata ...@@ -95,6 +97,13 @@ struct git_futils_mkdir_perfdata
size_t chmod_calls; size_t chmod_calls;
}; };
struct git_futils_mkdir_options
{
git_strmap *dir_map;
git_pool *pool;
struct git_futils_mkdir_perfdata perfdata;
};
/** /**
* Create a directory or entire path. * Create a directory or entire path.
* *
...@@ -106,10 +115,10 @@ struct git_futils_mkdir_perfdata ...@@ -106,10 +115,10 @@ struct git_futils_mkdir_perfdata
* @param base Root for relative path. These directories will never be made. * @param base Root for relative path. These directories will never be made.
* @param mode The mode to use for created directories. * @param mode The mode to use for created directories.
* @param flags Combination of the mkdir flags above. * @param flags Combination of the mkdir flags above.
* @param perfdata Performance data, use `git_futils_mkdir` if you don't want this data. * @param opts Extended options, use `git_futils_mkdir` if you are not interested.
* @return 0 on success, else error code * @return 0 on success, else error code
*/ */
extern int git_futils_mkdir_withperf(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_perfdata *perfdata); extern int git_futils_mkdir_ext(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts);
/** /**
* Create a directory or entire path. Similar to `git_futils_mkdir_withperf` * Create a directory or entire path. Similar to `git_futils_mkdir_withperf`
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment