diff options
Diffstat (limited to 'src/checkout.c')
-rw-r--r-- | src/checkout.c | 466 |
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); |