Commit 629b661c by Edward Thomson

checkout (from index) can write conflicts

parent 3acf44d6
......@@ -131,6 +131,13 @@ typedef enum {
/** Don't refresh index/config/etc before doing checkout */
GIT_CHECKOUT_NO_REFRESH = (1u << 9),
/** Allow checkout to skip unmerged files */
GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
/** For unmerged files, checkout stage 2 from index */
GIT_CHECKOUT_USE_OURS = (1u << 11),
/** For unmerged files, checkout stage 3 from index */
GIT_CHECKOUT_USE_THEIRS = (1u << 12),
/** Treat pathspec as simple list of exact match file paths */
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
......@@ -141,13 +148,6 @@ typedef enum {
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/
/** Allow checkout to skip unmerged files (NOT IMPLEMENTED) */
GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
/** For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED) */
GIT_CHECKOUT_USE_OURS = (1u << 11),
/** For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED) */
GIT_CHECKOUT_USE_THEIRS = (1u << 12),
/** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
/** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
......@@ -238,6 +238,9 @@ typedef struct git_checkout_opts {
git_tree *baseline; /** expected content of workdir, defaults to HEAD */
const char *target_directory; /** alternative checkout path to workdir */
const char *our_label; /** the name of the "our" side of conflicts */
const char *their_label; /** the name of the "their" side of conflicts */
} git_checkout_opts;
#define GIT_CHECKOUT_OPTS_VERSION 1
......
......@@ -41,24 +41,6 @@ enum {
(CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
};
typedef struct {
git_repository *repo;
git_diff_list *diff;
git_checkout_opts opts;
bool opts_free_baseline;
char *pfx;
git_index *index;
git_pool pool;
git_vector removes;
git_buf path;
size_t workdir_len;
unsigned int strategy;
int can_symlink;
bool reload_submodules;
size_t total_steps;
size_t completed_steps;
} checkout_data;
static int checkout_notify(
checkout_data *data,
git_checkout_notify_t why,
......@@ -707,6 +689,7 @@ static int blob_content_to_file(
struct stat *st,
git_blob *blob,
const char *path,
const char * hint_path,
mode_t entry_filemode,
git_checkout_opts *opts)
{
......@@ -715,9 +698,12 @@ static int blob_content_to_file(
git_buf out = GIT_BUF_INIT;
git_filter_list *fl = NULL;
if (hint_path == NULL)
hint_path = path;
if (!opts->disable_filters)
error = git_filter_list_load(
&fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE);
&fl, git_blob_owner(blob), blob, hint_path, GIT_FILTER_TO_WORKTREE);
if (!error)
error = git_filter_list_apply_to_blob(&out, fl, blob);
......@@ -886,34 +872,26 @@ static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
return 0;
}
static int checkout_blob(
int git_checkout__write_content(
checkout_data *data,
const git_diff_file *file)
const git_oid *oid,
const char *full_path,
const char *hint_path,
unsigned int mode,
struct stat *st)
{
int error = 0;
git_blob *blob;
struct stat st;
git_buf_truncate(&data->path, data->workdir_len);
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);
if (rval <= 0)
return rval;
}
if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0)
return error;
if (S_ISLNK(file->mode))
if (S_ISLNK(mode))
error = blob_content_to_link(
&st, blob, git_buf_cstr(&data->path), data->opts.dir_mode, data->can_symlink);
st, blob, full_path, data->opts.dir_mode, data->can_symlink);
else
error = blob_content_to_file(
&st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
st, blob, full_path, hint_path, mode, &data->opts);
git_blob_free(blob);
......@@ -928,6 +906,30 @@ static int checkout_blob(
error = 0;
}
return error;
}
static int checkout_blob(
checkout_data *data,
const git_diff_file *file)
{
int error = 0;
struct stat st;
git_buf_truncate(&data->path, data->workdir_len);
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);
if (rval <= 0)
return rval;
}
error = git_checkout__write_content(
data, &file->oid, git_buf_cstr(&data->path), NULL, file->mode, &st);
/* update the index unless prevented */
if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
error = checkout_update_index(data, file, &st);
......@@ -1172,7 +1174,17 @@ static int checkout_data_init(
(error = git_index_read(data->index)) < 0)
goto cleanup;
/* clear the REUC when doing a tree or commit checkout */
/* cannot checkout if unresolved conflicts exist */
if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) == 0 &&
git_index_has_conflicts(data->index)) {
error = GIT_EMERGECONFLICT;
giterr_set(GITERR_CHECKOUT,
"unresolved conflicts exist in the index");
goto cleanup;
}
/* clean conflict data when doing a tree or commit checkout */
git_index_name_clear(data->index);
git_index_reuc_clear(data->index);
}
}
......@@ -1312,6 +1324,9 @@ int git_checkout_iterator(
assert(data.completed_steps == data.total_steps);
/* Write conflict data to disk */
error = git_checkout__conflicts(&data);
cleanup:
if (error == GIT_EUSER)
giterr_clear();
......
......@@ -9,9 +9,28 @@
#include "git2/checkout.h"
#include "iterator.h"
#include "pool.h"
#define GIT_CHECKOUT__NOTIFY_CONFLICT_TREE (1u << 12)
typedef struct {
git_repository *repo;
git_diff_list *diff;
git_checkout_opts opts;
bool opts_free_baseline;
char *pfx;
git_index *index;
git_pool pool;
git_vector removes;
git_buf path;
size_t workdir_len;
unsigned int strategy;
int can_symlink;
bool reload_submodules;
size_t total_steps;
size_t completed_steps;
} checkout_data;
/**
* Update the working directory to match the target iterator. The
* expected baseline value can be passed in via the checkout options
......@@ -21,4 +40,14 @@ extern int git_checkout_iterator(
git_iterator *target,
const git_checkout_opts *opts);
int git_checkout__write_content(
checkout_data *data,
const git_oid *oid,
const char *path,
const char *hint_path,
unsigned int mode,
struct stat *st);
int git_checkout__conflicts(checkout_data *data);
#endif
......@@ -47,7 +47,7 @@ GIT_INLINE(int) merge_file_best_mode(
* assume executable. Otherwise, if any mode changed from the ancestor,
* use that one.
*/
if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
return GIT_FILEMODE_BLOB_EXECUTABLE;
......
......@@ -135,7 +135,7 @@ int git_reset(
if (reset_type == GIT_RESET_HARD) {
/* overwrite working directory with HEAD */
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_SKIP_UNMERGED;
if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
goto cleanup;
......
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