summaryrefslogtreecommitdiff
path: root/src/checkout.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/checkout.c')
-rw-r--r--src/checkout.c466
1 files changed, 314 insertions, 152 deletions
diff --git a/src/checkout.c b/src/checkout.c
index e4a397cd5..2bad06501 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -22,20 +22,19 @@
#include "filter.h"
#include "blob.h"
#include "diff.h"
+#include "pathspec.h"
-struct checkout_diff_data
-{
+typedef struct {
+ git_repository *repo;
+ git_diff_list *diff;
+ git_checkout_opts *opts;
git_buf *path;
size_t workdir_len;
- git_checkout_opts *checkout_opts;
- git_repository *owner;
bool can_symlink;
- bool found_submodules;
- bool create_submodules;
int error;
size_t total_steps;
size_t completed_steps;
-};
+} checkout_diff_data;
static int buffer_to_file(
git_buf *buffer,
@@ -112,7 +111,8 @@ static int blob_content_to_file(
if (!file_mode)
file_mode = entry_filemode;
- error = buffer_to_file(&filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
+ error = buffer_to_file(
+ &filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
cleanup:
git_filters_free(&filters);
@@ -123,7 +123,8 @@ cleanup:
return error;
}
-static int blob_content_to_link(git_blob *blob, const char *path, bool can_symlink)
+static int blob_content_to_link(
+ git_blob *blob, const char *path, bool can_symlink)
{
git_buf linktarget = GIT_BUF_INIT;
int error;
@@ -142,58 +143,52 @@ static int blob_content_to_link(git_blob *blob, const char *path, bool can_symli
}
static int checkout_submodule(
- struct checkout_diff_data *data,
+ checkout_diff_data *data,
const git_diff_file *file)
{
+ /* Until submodules are supported, UPDATE_ONLY means do nothing here */
+ if ((data->opts->checkout_strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+ return 0;
+
if (git_futils_mkdir(
- file->path, git_repository_workdir(data->owner),
- data->checkout_opts->dir_mode, GIT_MKDIR_PATH) < 0)
+ file->path, git_repository_workdir(data->repo),
+ data->opts->dir_mode, GIT_MKDIR_PATH) < 0)
return -1;
- /* TODO: two cases:
+ /* TODO: Support checkout_strategy options. Two circumstances:
* 1 - submodule already checked out, but we need to move the HEAD
* to the new OID, or
* 2 - submodule not checked out and we should recursively check it out
*
- * Checkout will not execute a pull request on the submodule, but a
- * clone command should probably be able to. Do we need a submodule
- * callback option?
+ * Checkout will not execute a pull on the submodule, but a clone
+ * command should probably be able to. Do we need a submodule callback?
*/
return 0;
}
static void report_progress(
- struct checkout_diff_data *data,
+ checkout_diff_data *data,
const char *path)
{
- if (data->checkout_opts->progress_cb)
- data->checkout_opts->progress_cb(
- path,
- data->completed_steps,
- data->total_steps,
- data->checkout_opts->progress_payload);
+ if (data->opts->progress_cb)
+ data->opts->progress_cb(
+ path, data->completed_steps, data->total_steps,
+ data->opts->progress_payload);
}
static int checkout_blob(
- struct checkout_diff_data *data,
+ checkout_diff_data *data,
const git_diff_file *file)
{
- git_blob *blob;
int error = 0;
+ git_blob *blob;
git_buf_truncate(data->path, data->workdir_len);
- if (git_buf_joinpath(data->path, git_buf_cstr(data->path), file->path) < 0)
+ if (git_buf_puts(data->path, file->path) < 0)
return -1;
- /* If there is a directory where this blob should go, then there is an
- * existing tree that conflicts with this file, but REMOVE_UNTRACKED must
- * not have been passed in. Signal to caller with GIT_ENOTFOUND.
- */
- if (git_path_isdir(git_buf_cstr(data->path)))
- return GIT_ENOTFOUND;
-
- if ((error = git_blob_lookup(&blob, data->owner, &file->oid)) < 0)
+ if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
return error;
if (S_ISLNK(file->mode))
@@ -201,14 +196,14 @@ static int checkout_blob(
blob, git_buf_cstr(data->path), data->can_symlink);
else
error = blob_content_to_file(
- blob, git_buf_cstr(data->path), file->mode, data->checkout_opts);
+ blob, git_buf_cstr(data->path), file->mode, data->opts);
git_blob_free(blob);
return error;
}
-static int retrieve_symlink_capabilities(git_repository *repo, bool *can_symlink)
+static int retrieve_symlink_caps(git_repository *repo, bool *can_symlink)
{
git_config *cfg;
int error;
@@ -218,10 +213,7 @@ static int retrieve_symlink_capabilities(git_repository *repo, bool *can_symlink
error = git_config_get_bool((int *)can_symlink, cfg, "core.symlinks");
- /*
- * When no "core.symlinks" entry is found in any of the configuration
- * store (local, global or system), default value is "true".
- */
+ /* If "core.symlinks" is not found anywhere, default to true. */
if (error == GIT_ENOTFOUND) {
*can_symlink = true;
error = 0;
@@ -230,7 +222,8 @@ static int retrieve_symlink_capabilities(git_repository *repo, bool *can_symlink
return error;
}
-static void normalize_options(git_checkout_opts *normalized, git_checkout_opts *proposed)
+static void normalize_options(
+ git_checkout_opts *normalized, git_checkout_opts *proposed)
{
assert(normalized);
@@ -239,11 +232,16 @@ static void normalize_options(git_checkout_opts *normalized, git_checkout_opts *
else
memmove(normalized, proposed, sizeof(git_checkout_opts));
- /* Default options */
- if (!normalized->checkout_strategy)
- normalized->checkout_strategy = GIT_CHECKOUT_DEFAULT;
+ /* implied checkout strategies */
+ if ((normalized->checkout_strategy & GIT_CHECKOUT_UPDATE_MODIFIED) != 0 ||
+ (normalized->checkout_strategy & GIT_CHECKOUT_UPDATE_UNTRACKED) != 0)
+ normalized->checkout_strategy |= GIT_CHECKOUT_UPDATE_UNMODIFIED;
+
+ if ((normalized->checkout_strategy & GIT_CHECKOUT_UPDATE_UNTRACKED) != 0)
+ normalized->checkout_strategy |= GIT_CHECKOUT_UPDATE_MISSING;
/* opts->disable_filters is false by default */
+
if (!normalized->dir_mode)
normalized->dir_mode = GIT_DIR_MODE;
@@ -254,50 +252,174 @@ static void normalize_options(git_checkout_opts *normalized, git_checkout_opts *
enum {
CHECKOUT_ACTION__NONE = 0,
CHECKOUT_ACTION__REMOVE = 1,
- CHECKOUT_ACTION__CREATE_BLOB = 2,
- CHECKOUT_ACTION__CREATE_SUBMODULE = 4,
- CHECKOUT_ACTION__NOTIFY = 8
+ CHECKOUT_ACTION__UPDATE_BLOB = 2,
+ CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
+ CHECKOUT_ACTION__CONFLICT = 8,
+ CHECKOUT_ACTION__MAX = 8
};
-static uint32_t checkout_action_for_delta(
- git_checkout_opts *opts,
- const git_diff_delta *delta)
+static int checkout_confirm_update_blob(
+ checkout_diff_data *data,
+ const git_diff_delta *delta,
+ int action)
{
- uint32_t action = CHECKOUT_ACTION__NONE;
+ int error;
+ unsigned int strat = data->opts->checkout_strategy;
+ struct stat st;
+ bool update_only = ((strat & GIT_CHECKOUT_UPDATE_ONLY) != 0);
+
+ /* for typechange, remove the old item first */
+ if (delta->status == GIT_DELTA_TYPECHANGE) {
+ if (update_only)
+ action = CHECKOUT_ACTION__NONE;
+ else
+ action |= CHECKOUT_ACTION__REMOVE;
- switch (delta->status) {
- case GIT_DELTA_UNTRACKED:
- if ((opts->checkout_strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0)
+ return action;
+ }
+
+ git_buf_truncate(data->path, data->workdir_len);
+ if (git_buf_puts(data->path, delta->new_file.path) < 0)
+ return -1;
+
+ if ((error = p_stat(git_buf_cstr(data->path), &st)) < 0) {
+ if (errno == ENOENT) {
+ if (update_only)
+ action = CHECKOUT_ACTION__NONE;
+ } else if (errno == ENOTDIR) {
+ /* File exists where a parent dir needs to go - i.e. untracked
+ * typechange. Ignore if UPDATE_ONLY, remove if allowed.
+ */
+ if (update_only)
+ action = CHECKOUT_ACTION__NONE;
+ else if ((strat & GIT_CHECKOUT_UPDATE_UNTRACKED) != 0)
+ action |= CHECKOUT_ACTION__REMOVE;
+ else
+ action = CHECKOUT_ACTION__CONFLICT;
+ }
+ /* otherwise let error happen when we attempt blob checkout later */
+ }
+ else if (S_ISDIR(st.st_mode)) {
+ /* Directory exists where a blob needs to go - i.e. untracked
+ * typechange. Ignore if UPDATE_ONLY, remove if allowed.
+ */
+ if (update_only)
+ action = CHECKOUT_ACTION__NONE;
+ else if ((strat & GIT_CHECKOUT_UPDATE_UNTRACKED) != 0)
action |= CHECKOUT_ACTION__REMOVE;
+ else
+ action = CHECKOUT_ACTION__CONFLICT;
+ }
+
+ return action;
+}
+
+static int checkout_action_for_delta(
+ checkout_diff_data *data,
+ const git_diff_delta *delta,
+ const git_index_entry *head_entry)
+{
+ int action = CHECKOUT_ACTION__NONE;
+ unsigned int strat = data->opts->checkout_strategy;
+
+ switch (delta->status) {
+ case GIT_DELTA_UNMODIFIED:
+ if (!head_entry) {
+ /* file independently created in wd, even though not in HEAD */
+ if ((strat & GIT_CHECKOUT_UPDATE_MISSING) == 0)
+ action = CHECKOUT_ACTION__CONFLICT;
+ }
+ else if (!git_oid_equal(&head_entry->oid, &delta->old_file.oid)) {
+ /* working directory was independently updated to match index */
+ if ((strat & GIT_CHECKOUT_UPDATE_MODIFIED) == 0)
+ action = CHECKOUT_ACTION__CONFLICT;
+ }
break;
- case GIT_DELTA_TYPECHANGE:
- if ((opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED) != 0)
- action |= CHECKOUT_ACTION__REMOVE | CHECKOUT_ACTION__CREATE_BLOB;
- else if (opts->skipped_notify_cb != NULL)
- action = CHECKOUT_ACTION__NOTIFY;
+ case GIT_DELTA_ADDED:
+ /* Impossible. New files should be UNTRACKED or TYPECHANGE */
+ action = CHECKOUT_ACTION__CONFLICT;
break;
case GIT_DELTA_DELETED:
- if ((opts->checkout_strategy & GIT_CHECKOUT_CREATE_MISSING) != 0)
- action |= CHECKOUT_ACTION__CREATE_BLOB;
+ if (head_entry && /* working dir missing, but exists in HEAD */
+ (strat & GIT_CHECKOUT_UPDATE_MISSING) == 0)
+ action = CHECKOUT_ACTION__CONFLICT;
+ else
+ action = CHECKOUT_ACTION__UPDATE_BLOB;
break;
case GIT_DELTA_MODIFIED:
- if ((opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED) != 0)
- action |= CHECKOUT_ACTION__CREATE_BLOB;
- else if (opts->skipped_notify_cb != NULL)
- action = CHECKOUT_ACTION__NOTIFY;
+ case GIT_DELTA_TYPECHANGE:
+ if (!head_entry) {
+ /* working dir was independently updated & does not match index */
+ if ((strat & GIT_CHECKOUT_UPDATE_UNTRACKED) == 0)
+ action = CHECKOUT_ACTION__CONFLICT;
+ else
+ action = CHECKOUT_ACTION__UPDATE_BLOB;
+ }
+ else if (git_oid_equal(&head_entry->oid, &delta->new_file.oid))
+ action = CHECKOUT_ACTION__UPDATE_BLOB;
+ else if ((strat & GIT_CHECKOUT_UPDATE_MODIFIED) == 0)
+ action = CHECKOUT_ACTION__CONFLICT;
+ else
+ action = CHECKOUT_ACTION__UPDATE_BLOB;
break;
+ case GIT_DELTA_UNTRACKED:
+ if (!head_entry) {
+ if ((strat & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0)
+ action = CHECKOUT_ACTION__REMOVE;
+ }
+ else if ((strat & GIT_CHECKOUT_UPDATE_MODIFIED) != 0) {
+ action = CHECKOUT_ACTION__REMOVE;
+ } else if ((strat & GIT_CHECKOUT_UPDATE_UNMODIFIED) != 0) {
+ git_oid wd_oid;
+
+ /* if HEAD matches workdir, then remove, else conflict */
+
+ if (git_oid_iszero(&delta->new_file.oid) &&
+ git_diff__oid_for_file(
+ data->repo, delta->new_file.path, delta->new_file.mode,
+ delta->new_file.size, &wd_oid) < 0)
+ action = -1;
+ else if (git_oid_equal(&head_entry->oid, &wd_oid))
+ action = CHECKOUT_ACTION__REMOVE;
+ else
+ action = CHECKOUT_ACTION__CONFLICT;
+ } else {
+ /* present in HEAD and workdir, but absent in index */
+ action = CHECKOUT_ACTION__CONFLICT;
+ }
+ break;
+
+ case GIT_DELTA_IGNORED:
default:
+ /* just skip these files */
break;
}
- if ((action & CHECKOUT_ACTION__CREATE_BLOB) != 0 &&
- S_ISGITLINK(delta->old_file.mode))
- action = (action & ~CHECKOUT_ACTION__CREATE_BLOB) |
- CHECKOUT_ACTION__CREATE_SUBMODULE;
+ if (action > 0 && (action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) {
+ if (S_ISGITLINK(delta->old_file.mode))
+ action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
+ CHECKOUT_ACTION__UPDATE_SUBMODULE;
+
+ action = checkout_confirm_update_blob(data, delta, action);
+ }
+
+ if (action == CHECKOUT_ACTION__CONFLICT &&
+ data->opts->conflict_cb != NULL &&
+ data->opts->conflict_cb(
+ delta->old_file.path, &delta->old_file.oid,
+ delta->old_file.mode, delta->new_file.mode,
+ data->opts->conflict_payload) != 0)
+ {
+ giterr_clear();
+ action = GIT_EUSER;
+ }
+
+ if (action > 0 && (strat & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+ action = (action & ~CHECKOUT_ACTION__REMOVE);
return action;
}
@@ -305,53 +427,119 @@ static uint32_t checkout_action_for_delta(
static int checkout_get_actions(
uint32_t **actions_ptr,
size_t **counts_ptr,
- git_diff_list *diff,
- git_checkout_opts *opts)
+ checkout_diff_data *data)
{
+ int error;
+ git_diff_list *diff = data->diff;
git_diff_delta *delta;
size_t i, *counts;
uint32_t *actions;
+ git_tree *head = NULL;
+ git_iterator *hiter = NULL;
+ char *pfx = git_pathspec_prefix(&data->opts->paths);
+ const git_index_entry *he;
+
+ /* if there is no HEAD, that's okay - we'll make an empty iterator */
+ (void)git_repository_head_tree(&head, data->repo);
+
+ if ((error = git_iterator_for_tree_range(
+ &hiter, data->repo, head, pfx, pfx)) < 0)
+ goto fail;
+
+ if ((diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
+ !hiter->ignore_case &&
+ (error = git_iterator_spoolandsort(
+ &hiter, hiter, diff->entrycmp, true)) < 0)
+ goto fail;
+
+ if ((error = git_iterator_current(hiter, &he)) < 0)
+ goto fail;
+
+ git__free(pfx);
+
+ *counts_ptr = counts = git__calloc(CHECKOUT_ACTION__MAX+1, sizeof(size_t));
+ *actions_ptr = actions = git__calloc(diff->deltas.length, sizeof(uint32_t));
+ if (!counts || !actions) {
+ error = -1;
+ goto fail;
+ }
- *counts_ptr = counts =
- git__calloc(CHECKOUT_ACTION__NOTIFY, sizeof(size_t));
- GITERR_CHECK_ALLOC(counts);
+ git_vector_foreach(&diff->deltas, i, delta) {
+ int cmp = -1, act;
+
+ /* try to track HEAD entries parallel to deltas */
+ while (he) {
+ cmp = S_ISDIR(delta->new_file.mode) ?
+ diff->prefixcmp(he->path, delta->new_file.path) :
+ diff->strcmp(he->path, delta->old_file.path);
+ if (cmp >= 0)
+ break;
+ if (git_iterator_advance(hiter, &he) < 0)
+ he = NULL;
+ }
- *actions_ptr = actions =
- git__calloc(diff->deltas.length, sizeof(uint32_t));
- GITERR_CHECK_ALLOC(actions);
+ act = checkout_action_for_delta(data, delta, !cmp ? he : NULL);
- git_vector_foreach(&diff->deltas, i, delta) {
- actions[i] = checkout_action_for_delta(opts, delta);
+ if (act < 0) {
+ error = act;
+ goto fail;
+ }
- if (actions[i] & CHECKOUT_ACTION__REMOVE)
- counts[CHECKOUT_ACTION__REMOVE]++;
+ if (!cmp && git_iterator_advance(hiter, &he) < 0)
+ he = NULL;
- if (actions[i] & CHECKOUT_ACTION__CREATE_BLOB)
- counts[CHECKOUT_ACTION__CREATE_BLOB]++;
+ actions[i] = act;
- if (actions[i] & CHECKOUT_ACTION__CREATE_SUBMODULE)
- counts[CHECKOUT_ACTION__CREATE_SUBMODULE]++;
+ if (act & CHECKOUT_ACTION__REMOVE)
+ counts[CHECKOUT_ACTION__REMOVE]++;
+ if (act & CHECKOUT_ACTION__UPDATE_BLOB)
+ counts[CHECKOUT_ACTION__UPDATE_BLOB]++;
+ if (act & CHECKOUT_ACTION__UPDATE_SUBMODULE)
+ counts[CHECKOUT_ACTION__UPDATE_SUBMODULE]++;
+ if (act & CHECKOUT_ACTION__CONFLICT)
+ counts[CHECKOUT_ACTION__CONFLICT]++;
+ }
- if (actions[i] & CHECKOUT_ACTION__NOTIFY)
- counts[CHECKOUT_ACTION__NOTIFY]++;
+ if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
+ (data->opts->checkout_strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
+ {
+ giterr_set(GITERR_CHECKOUT, "%d conflicts prevent checkout",
+ (int)counts[CHECKOUT_ACTION__CONFLICT]);
+ goto fail;
}
+ git_iterator_free(hiter);
return 0;
+
+fail:
+ *counts_ptr = NULL;
+ git__free(counts);
+ *actions_ptr = NULL;
+ git__free(actions);
+
+ git_iterator_free(hiter);
+ git__free(pfx);
+
+ return -1;
}
static int checkout_remove_the_old(
git_diff_list *diff,
unsigned int *actions,
- struct checkout_diff_data *data)
+ checkout_diff_data *data)
{
git_diff_delta *delta;
size_t i;
+ git_buf_truncate(data->path, data->workdir_len);
+
git_vector_foreach(&diff->deltas, i, delta) {
if (actions[i] & CHECKOUT_ACTION__REMOVE) {
int error = git_futils_rmdir_r(
- delta->new_file.path, git_buf_cstr(data->path),
- GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_EMPTY_PARENTS);
+ delta->new_file.path,
+ git_buf_cstr(data->path), /* here set to work dir root */
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_EMPTY_PARENTS |
+ GIT_RMDIR_REMOVE_BLOCKERS);
if (error < 0)
return error;
@@ -366,35 +554,20 @@ static int checkout_remove_the_old(
static int checkout_create_the_new(
git_diff_list *diff,
unsigned int *actions,
- struct checkout_diff_data *data)
+ checkout_diff_data *data)
{
git_diff_delta *delta;
size_t i;
git_vector_foreach(&diff->deltas, i, delta) {
- if (actions[i] & CHECKOUT_ACTION__CREATE_BLOB) {
+ if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) {
int error = checkout_blob(data, &delta->old_file);
-
- /* ENOTFOUND means unable to create the file because of
- * an existing parent dir. Probably flags were given
- * asking to create new blobs without allowing removal
- * of a conflicting tree (or vice versa).
- */
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
+ if (error < 0)
return error;
data->completed_steps++;
report_progress(data, delta->old_file.path);
}
-
- if (actions[i] & CHECKOUT_ACTION__NOTIFY) {
- if (data->checkout_opts->skipped_notify_cb(
- delta->old_file.path, &delta->old_file.oid,
- delta->old_file.mode, data->checkout_opts->notify_payload))
- return GIT_EUSER;
- }
}
return 0;
@@ -403,13 +576,13 @@ static int checkout_create_the_new(
static int checkout_create_submodules(
git_diff_list *diff,
unsigned int *actions,
- struct checkout_diff_data *data)
+ checkout_diff_data *data)
{
git_diff_delta *delta;
size_t i;
git_vector_foreach(&diff->deltas, i, delta) {
- if (actions[i] & CHECKOUT_ACTION__CREATE_SUBMODULE) {
+ if (actions[i] & CHECKOUT_ACTION__UPDATE_SUBMODULE) {
int error = checkout_submodule(data, &delta->old_file);
if (error < 0)
return error;
@@ -427,16 +600,13 @@ int git_checkout_index(
git_checkout_opts *opts)
{
git_diff_list *diff = NULL;
-
git_diff_options diff_opts = {0};
git_checkout_opts checkout_opts;
- struct checkout_diff_data data;
+ checkout_diff_data data;
git_buf workdir = GIT_BUF_INIT;
-
uint32_t *actions = NULL;
size_t *counts = NULL;
-
int error;
assert(repo);
@@ -445,9 +615,8 @@ int git_checkout_index(
return error;
diff_opts.flags =
- GIT_DIFF_INCLUDE_UNTRACKED |
- GIT_DIFF_INCLUDE_TYPECHANGE |
- GIT_DIFF_SKIP_BINARY_CHECK;
+ GIT_DIFF_INCLUDE_UNMODIFIED | GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_SKIP_BINARY_CHECK;
if (opts && opts->paths.count > 0)
diff_opts.pathspec = opts->paths;
@@ -470,33 +639,35 @@ int git_checkout_index(
* 3. Then checkout all submodules in case a new .gitmodules blob was
* checked out during pass #2.
*/
- if ((error = checkout_get_actions(&actions, &counts, diff, &checkout_opts)) < 0)
- goto cleanup;
memset(&data, 0, sizeof(data));
data.path = &workdir;
data.workdir_len = git_buf_len(&workdir);
- data.checkout_opts = &checkout_opts;
- data.owner = repo;
+ data.repo = repo;
+ data.diff = diff;
+ data.opts = &checkout_opts;
+
+ if ((error = checkout_get_actions(&actions, &counts, &data)) < 0)
+ goto cleanup;
+
data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
- counts[CHECKOUT_ACTION__CREATE_BLOB] +
- counts[CHECKOUT_ACTION__CREATE_SUBMODULE];
+ counts[CHECKOUT_ACTION__UPDATE_BLOB] +
+ counts[CHECKOUT_ACTION__UPDATE_SUBMODULE];
- if ((error = retrieve_symlink_capabilities(repo, &data.can_symlink)) < 0)
+ if ((error = retrieve_symlink_caps(repo, &data.can_symlink)) < 0)
goto cleanup;
- report_progress(&data, NULL);
+ report_progress(&data, NULL); /* establish 0 baseline */
if (counts[CHECKOUT_ACTION__REMOVE] > 0 &&
(error = checkout_remove_the_old(diff, actions, &data)) < 0)
goto cleanup;
- if ((counts[CHECKOUT_ACTION__CREATE_BLOB] +
- counts[CHECKOUT_ACTION__NOTIFY]) > 0 &&
+ if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 &&
(error = checkout_create_the_new(diff, actions, &data)) < 0)
goto cleanup;
- if (counts[CHECKOUT_ACTION__CREATE_SUBMODULE] > 0 &&
+ if (counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] > 0 &&
(error = checkout_create_submodules(diff, actions, &data)) < 0)
goto cleanup;
@@ -519,32 +690,28 @@ int git_checkout_tree(
git_object *treeish,
git_checkout_opts *opts)
{
+ int error = 0;
git_index *index = NULL;
git_tree *tree = NULL;
- int error;
-
assert(repo && treeish);
if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
- giterr_set(GITERR_INVALID, "Provided treeish cannot be peeled into a tree.");
- return GIT_ERROR;
+ giterr_set(
+ GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
+ return -1;
}
- if ((error = git_repository_index(&index, repo)) < 0)
- goto cleanup;
+ /* load paths in tree that match pathspec into index */
+ if (!(error = git_repository_index(&index, repo)) &&
+ !(error = git_index_read_tree_match(
+ index, tree, opts ? &opts->paths : NULL)) &&
+ !(error = git_index_write(index)))
+ error = git_checkout_index(repo, opts);
- if ((error = git_index_read_tree(index, tree)) < 0)
- goto cleanup;
-
- if ((error = git_index_write(index)) < 0)
- goto cleanup;
-
- error = git_checkout_index(repo, opts);
-
-cleanup:
git_index_free(index);
git_tree_free(tree);
+
return error;
}
@@ -552,21 +719,16 @@ int git_checkout_head(
git_repository *repo,
git_checkout_opts *opts)
{
- git_reference *head;
int error;
+ git_reference *head = NULL;
git_object *tree = NULL;
assert(repo);
- if ((error = git_repository_head(&head, repo)) < 0)
- return error;
-
- if ((error = git_reference_peel(&tree, head, GIT_OBJ_TREE)) < 0)
- goto cleanup;
+ if (!(error = git_repository_head(&head, repo)) &&
+ !(error = git_reference_peel(&tree, head, GIT_OBJ_TREE)))
+ error = git_checkout_tree(repo, tree, opts);
- error = git_checkout_tree(repo, tree, opts);
-
-cleanup:
git_reference_free(head);
git_object_free(tree);