vendor/libgit2/src/checkout.c in rugged-0.22.0b5 vs vendor/libgit2/src/checkout.c in rugged-0.22.1b1
- old
+ new
@@ -65,10 +65,12 @@
unsigned int strategy;
int can_symlink;
bool reload_submodules;
size_t total_steps;
size_t completed_steps;
+ git_checkout_perfdata perfdata;
+ git_buf last_mkdir;
} checkout_data;
typedef struct {
const git_index_entry *ancestor;
const git_index_entry *ours;
@@ -1099,11 +1101,11 @@
"Index inconsistency, truncated index while loading expected conflict '%s'", path);
error = -1;
goto done;
}
- prefixed = git_path_equal_or_prefixed(path, entry->path);
+ prefixed = git_path_equal_or_prefixed(path, entry->path, NULL);
if (prefixed == GIT_PATH_EQUAL)
continue;
if (prefixed == GIT_PATH_PREFIX)
@@ -1287,99 +1289,188 @@
git_pool_clear(&pathpool);
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)
+{
+ int ignorecase = 0;
+
+ git_repository__cvar(&ignorecase, data->repo, GIT_CVAR_IGNORECASE);
+
+ return (ignorecase &&
+ (data->strategy & GIT_CHECKOUT_DONT_REMOVE_EXISTING) == 0);
+}
+
+#define MKDIR_NORMAL \
+ GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR
+#define MKDIR_REMOVE_EXISTING \
+ MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS
+
+static int mkpath2file(
+ checkout_data *data, const char *path, unsigned int mode)
+{
+ git_buf *mkdir_path = &data->tmp;
+ struct stat st;
+ bool remove_existing = should_remove_existing(data);
+ int error;
+
+ if ((error = git_buf_sets(mkdir_path, path)) < 0)
+ 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) {
+ data->perfdata.stat_calls++;
+
+ if (p_lstat(path, &st) == 0) {
+
+ /* Some file, symlink or folder already exists at this name.
+ * We would have removed it in remove_the_old unless we're on
+ * a case inensitive filesystem (or the user has asked us not
+ * to). Remove the similarly named file to write the new.
+ */
+ error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES);
+ } else if (errno != ENOENT) {
+ giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+ return GIT_EEXISTS;
+ } else {
+ giterr_clear();
+ }
+ }
+
+ return error;
+}
+
static int buffer_to_file(
+ checkout_data *data,
struct stat *st,
git_buf *buf,
const char *path,
- mode_t dir_mode,
- int file_open_flags,
mode_t file_mode)
{
int error;
- if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
+ if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
return error;
if ((error = git_futils_writebuffer(
- buf, path, file_open_flags, file_mode)) < 0)
+ buf, path, data->opts.file_open_flags, file_mode)) < 0)
return error;
- if (st != NULL && (error = p_stat(path, st)) < 0)
- giterr_set(GITERR_OS, "Error statting '%s'", path);
+ if (st) {
+ data->perfdata.stat_calls++;
- else if (GIT_PERMS_IS_EXEC(file_mode) &&
- (error = p_chmod(path, file_mode)) < 0)
- giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
+ if ((error = p_stat(path, st)) < 0) {
+ giterr_set(GITERR_OS, "Error statting '%s'", path);
+ return error;
+ }
+ }
+ if (GIT_PERMS_IS_EXEC(file_mode)) {
+ data->perfdata.chmod_calls++;
+
+ if ((error = p_chmod(path, file_mode)) < 0)
+ giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
+ }
+
return error;
}
static int blob_content_to_file(
+ checkout_data *data,
struct stat *st,
git_blob *blob,
const char *path,
const char * hint_path,
- mode_t entry_filemode,
- git_checkout_options *opts)
+ mode_t entry_filemode)
{
- int error = 0;
- mode_t file_mode = opts->file_mode ? opts->file_mode : entry_filemode;
+ mode_t file_mode = data->opts.file_mode ?
+ data->opts.file_mode : entry_filemode;
git_buf out = GIT_BUF_INIT;
git_filter_list *fl = NULL;
+ int error = 0;
if (hint_path == NULL)
hint_path = path;
- if (!opts->disable_filters)
+ if (!data->opts.disable_filters)
error = git_filter_list_load(
&fl, git_blob_owner(blob), blob, hint_path,
GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT);
if (!error)
error = git_filter_list_apply_to_blob(&out, fl, blob);
git_filter_list_free(fl);
if (!error) {
- error = buffer_to_file(
- st, &out, path, opts->dir_mode, opts->file_open_flags, file_mode);
-
+ error = buffer_to_file(data, st, &out, path, file_mode);
st->st_mode = entry_filemode;
git_buf_free(&out);
}
return error;
}
static int blob_content_to_link(
+ checkout_data *data,
struct stat *st,
git_blob *blob,
- const char *path,
- mode_t dir_mode,
- int can_symlink)
+ const char *path)
{
git_buf linktarget = GIT_BUF_INIT;
int error;
- if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
+ if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
return error;
if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
return error;
- if (can_symlink) {
+ if (data->can_symlink) {
if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
- giterr_set(GITERR_OS, "Could not create symlink %s\n", path);
+ giterr_set(GITERR_OS, "Could not create symlink %s", path);
} else {
error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
}
if (!error) {
+ data->perfdata.stat_calls++;
+
if ((error = p_lstat(path, st)) < 0)
giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path);
st->st_mode = GIT_FILEMODE_LINK;
}
@@ -1419,10 +1510,11 @@
git_buf_truncate(&data->path, data->workdir_len);
if (git_buf_puts(&data->path, file->path) < 0)
return -1;
+ data->perfdata.stat_calls++;
if (p_stat(git_buf_cstr(&data->path), &st) < 0) {
giterr_set(
GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
return GIT_ENOTFOUND;
}
@@ -1434,19 +1526,21 @@
static int checkout_submodule(
checkout_data *data,
const git_diff_file *file)
{
+ bool remove_existing = should_remove_existing(data);
int error = 0;
/* Until submodules are supported, UPDATE_ONLY means do nothing here */
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
return 0;
- if ((error = git_futils_mkdir(
- file->path, data->opts.target_directory,
- data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
+ if ((error = checkout_mkdir(
+ data,
+ file->path, data->opts.target_directory, data->opts.dir_mode,
+ remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0)
return error;
if ((error = git_submodule_lookup(NULL, data->repo, file->path)) < 0) {
/* I've observed repos with submodules in the tree that do not
* have a .gitmodules - core Git just makes an empty directory
@@ -1479,14 +1573,17 @@
data->opts.progress_cb(
path, data->completed_steps, data->total_steps,
data->opts.progress_payload);
}
-static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
+static int checkout_safe_for_update_only(
+ checkout_data *data, const char *path, mode_t expected_mode)
{
struct stat st;
+ data->perfdata.stat_calls++;
+
if (p_lstat(path, &st) < 0) {
/* if doesn't exist, then no error and no update */
if (errno == ENOENT || errno == ENOTDIR)
return 0;
@@ -1515,15 +1612,13 @@
if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0)
return error;
if (S_ISLNK(mode))
- error = blob_content_to_link(
- st, blob, full_path, data->opts.dir_mode, data->can_symlink);
+ error = blob_content_to_link(data, st, blob, full_path);
else
- error = blob_content_to_file(
- st, blob, full_path, hint_path, mode, &data->opts);
+ error = blob_content_to_file(data, st, blob, full_path, hint_path, mode);
git_blob_free(blob);
/* if we try to create the blob and an existing directory blocks it from
* being written, then there must have been a typechange conflict in a
@@ -1550,11 +1645,11 @@
if (git_buf_puts(&data->path, file->path) < 0)
return -1;
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
int rval = checkout_safe_for_update_only(
- git_buf_cstr(&data->path), file->mode);
+ data, git_buf_cstr(&data->path), file->mode);
if (rval <= 0)
return rval;
}
error = checkout_write_content(
@@ -1805,11 +1900,11 @@
hint_path = side->path;
}
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
- (error = checkout_safe_for_update_only(git_buf_cstr(&data->path), side->mode)) <= 0)
+ (error = checkout_safe_for_update_only(data, git_buf_cstr(&data->path), side->mode)) <= 0)
return error;
return checkout_write_content(data,
&side->id, git_buf_cstr(&data->path), hint_path, side->mode, &st);
}
@@ -1904,11 +1999,11 @@
if ((error = checkout_merge_path(&path_workdir, data, conflict, &result)) < 0)
goto done;
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
- (error = checkout_safe_for_update_only(git_buf_cstr(&path_workdir), result.mode)) <= 0)
+ (error = checkout_safe_for_update_only(data, git_buf_cstr(&path_workdir), result.mode)) <= 0)
goto done;
if (!data->opts.disable_filters) {
in_data.ptr = (char *)result.ptr;
in_data.size = result.len;
@@ -1920,11 +2015,11 @@
} else {
out_data.ptr = (char *)result.ptr;
out_data.size = result.len;
}
- if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 ||
+ if ((error = mkpath2file(data, path_workdir.ptr, data->opts.dir_mode)) < 0 ||
(error = git_filebuf_open(&output, git_buf_cstr(&path_workdir), GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 ||
(error = git_filebuf_write(&output, out_data.ptr, out_data.size)) < 0 ||
(error = git_filebuf_commit(&output)) < 0)
goto done;
@@ -2155,12 +2250,13 @@
memmove(&data->opts, proposed, sizeof(git_checkout_options));
if (!data->opts.target_directory)
data->opts.target_directory = git_repository_workdir(repo);
else if (!git_path_isdir(data->opts.target_directory) &&
- (error = git_futils_mkdir(data->opts.target_directory, NULL,
- GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0)
+ (error = checkout_mkdir(data,
+ data->opts.target_directory, NULL,
+ GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0)
goto cleanup;
/* refresh config and index content unless NO_REFRESH is given */
if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
git_config *cfg;
@@ -2472,10 +2568,10 @@
}
if ((error = git_repository_index(&index, repo)) < 0)
return error;
- if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL)))
+ if (!(error = git_iterator_for_tree(&tree_i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)))
error = git_checkout_iterator(tree_i, index, opts);
git_iterator_free(tree_i);
git_index_free(index);
git_tree_free(tree);