From 632d8b230bf38cc61cd70b55a54ae2f52502b4af Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 23 Oct 2012 15:42:09 -0500 Subject: [PATCH] reset changes for merge --- include/git2/repository.h | 16 +++++++++++++ src/merge.c | 48 +++++++++++++++++++++++++++++++++++++++ src/merge.h | 19 ++++++++++++++++ src/refs.h | 3 +++ src/repository.c | 28 +++++++++++++++++++---- src/reset.c | 12 ++++++++++ tests-clar/repo/state.c | 47 ++++++++++++++++++++++++++++++++++++++ tests-clar/reset/hard.c | 35 ++++++++++++++++++++++++++++ tests-clar/reset/soft.c | 13 +++++++++++ 9 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 src/merge.c create mode 100644 src/merge.h create mode 100644 tests-clar/repo/state.c diff --git a/include/git2/repository.h b/include/git2/repository.h index 193ac9523..d72431538 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -569,6 +569,22 @@ GIT_EXTERN(int) git_repository_set_head_detached( GIT_EXTERN(int) git_repository_detach_head( git_repository* repo); +typedef enum { + GIT_REPOSITORY_STATE_NONE, + GIT_REPOSITORY_STATE_MERGE, + GIT_REPOSITORY_STATE_REVERT, + GIT_REPOSITORY_STATE_CHERRY_PICK, +} git_repository_state_t; + +/** + * Determines the status of a git repository - ie, whether an operation + * (merge, cherry-pick, etc) is in progress. + * + * @param repo Repository pointer + * @return The state of the repository + */ +GIT_EXTERN(int) git_repository_state(git_repository *repo); + /** @} */ GIT_END_DECL #endif diff --git a/src/merge.c b/src/merge.c new file mode 100644 index 000000000..135af6a8c --- /dev/null +++ b/src/merge.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "repository.h" +#include "buffer.h" +#include "merge.h" +#include "refs.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "git2/reset.h" + +int git_merge__cleanup(git_repository *repo) +{ + int error = 0; + git_buf merge_head_path = GIT_BUF_INIT, + merge_mode_path = GIT_BUF_INIT, + merge_msg_path = GIT_BUF_INIT; + + assert(repo); + + if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 || + git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 || + git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0) + return -1; + + if (git_path_isfile(merge_head_path.ptr)) { + if ((error = p_unlink(merge_head_path.ptr)) < 0) + goto cleanup; + } + + if (git_path_isfile(merge_mode_path.ptr)) + (void)p_unlink(merge_mode_path.ptr); + + if (git_path_isfile(merge_msg_path.ptr)) + (void)p_unlink(merge_msg_path.ptr); + +cleanup: + git_buf_free(&merge_msg_path); + git_buf_free(&merge_mode_path); + git_buf_free(&merge_head_path); + + return error; +} + diff --git a/src/merge.h b/src/merge.h new file mode 100644 index 000000000..2117d9214 --- /dev/null +++ b/src/merge.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2009-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_merge_h__ +#define INCLUDE_merge_h__ + +#include "git2/types.h" + +#define GIT_MERGE_MSG_FILE "MERGE_MSG" +#define GIT_MERGE_MODE_FILE "MERGE_MODE" + +#define MERGE_CONFIG_FILE_MODE 0666 + +int git_merge__cleanup(git_repository *repo); + +#endif diff --git a/src/refs.h b/src/refs.h index 54359f07b..58e2fd558 100644 --- a/src/refs.h +++ b/src/refs.h @@ -28,8 +28,11 @@ #define GIT_PACKEDREFS_FILE_MODE 0666 #define GIT_HEAD_FILE "HEAD" +#define GIT_ORIG_HEAD_FILE "ORIG_HEAD" #define GIT_FETCH_HEAD_FILE "FETCH_HEAD" #define GIT_MERGE_HEAD_FILE "MERGE_HEAD" +#define GIT_REVERT_HEAD_FILE "REVERT_HEAD" +#define GIT_CHERRY_PICK_HEAD_FILE "CHERRY_PICK_HEAD" #define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master" #define GIT_REFNAME_MAX 1024 diff --git a/src/repository.c b/src/repository.c index 43e0eda8f..fa4604bee 100644 --- a/src/repository.c +++ b/src/repository.c @@ -20,6 +20,7 @@ #include "filter.h" #include "odb.h" #include "remote.h" +#include "merge.h" #define GIT_FILE_CONTENT_PREFIX "gitdir:" @@ -1348,15 +1349,13 @@ int git_repository_head_tree(git_tree **tree, git_repository *repo) return 0; } -#define MERGE_MSG_FILE "MERGE_MSG" - int git_repository_message(char *buffer, size_t len, git_repository *repo) { git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; struct stat st; int error; - if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0) + if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) return -1; if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) { @@ -1382,7 +1381,7 @@ int git_repository_message_remove(git_repository *repo) git_buf path = GIT_BUF_INIT; int error; - if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0) + if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) return -1; error = p_unlink(git_buf_cstr(&path)); @@ -1541,3 +1540,24 @@ cleanup: git_reference_free(new_head); return error; } + +int git_repository_state(git_repository *repo) +{ + git_buf repo_path = GIT_BUF_INIT; + int state = GIT_REPOSITORY_STATE_NONE; + + assert(repo); + + if (git_buf_puts(&repo_path, repo->path_repository) < 0) + return -1; + + if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE)) + state = GIT_REPOSITORY_STATE_MERGE; + else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) + state = GIT_REPOSITORY_STATE_REVERT; + else if(git_path_contains_file(&repo_path, GIT_CHERRY_PICK_HEAD_FILE)) + state = GIT_REPOSITORY_STATE_CHERRY_PICK; + + git_buf_free(&repo_path); + return state; +} diff --git a/src/reset.c b/src/reset.c index 560ae17b1..aff5b9f88 100644 --- a/src/reset.c +++ b/src/reset.c @@ -8,8 +8,10 @@ #include "common.h" #include "commit.h" #include "tag.h" +#include "merge.h" #include "git2/reset.h" #include "git2/checkout.h" +#include "git2/merge.h" #define ERROR_MSG "Cannot perform reset" @@ -88,6 +90,11 @@ int git_reset( goto cleanup; } + if (reset_type == GIT_RESET_SOFT && (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE)) { + giterr_set(GITERR_OBJECT, "%s (soft) while in the middle of a merge.", ERROR_MSG); + goto cleanup; + } + //TODO: Check for unmerged entries if (update_head(repo, commit) < 0) @@ -118,6 +125,11 @@ int git_reset( goto cleanup; } + if ((error = git_merge__cleanup(repo)) < 0) { + giterr_set(GITERR_INDEX, "%s - Failed to clean up merge data.", ERROR_MSG); + goto cleanup; + } + if (reset_type == GIT_RESET_MIXED) { error = 0; goto cleanup; diff --git a/tests-clar/repo/state.c b/tests-clar/repo/state.c new file mode 100644 index 000000000..1ee84374c --- /dev/null +++ b/tests-clar/repo/state.c @@ -0,0 +1,47 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "refs.h" +#include "posix.h" + +static git_repository *_repo; +static git_buf _path; + +void test_repo_state__initialize(void) +{ + _repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_repo_state__cleanup(void) +{ + cl_git_sandbox_cleanup(); + git_buf_free(&_path); +} + +void test_repo_state__none(void) +{ + /* The repo should be at its default state */ + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(_repo)); +} + +void test_repo_state__merge(void) +{ + + /* Then it should recognise that .git/MERGE_HEAD and friends mean their respective states */ + cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), GIT_MERGE_HEAD_FILE)); + cl_git_mkfile(git_buf_cstr(&_path), "dummy"); + cl_assert_equal_i(GIT_REPOSITORY_STATE_MERGE, git_repository_state(_repo)); +} + +void test_repo_state__revert(void) +{ + cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), GIT_REVERT_HEAD_FILE)); + cl_git_mkfile(git_buf_cstr(&_path), "dummy"); + cl_assert_equal_i(GIT_REPOSITORY_STATE_REVERT, git_repository_state(_repo)); +} + +void test_repo_state__cherry_pick(void) +{ + cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), GIT_CHERRY_PICK_HEAD_FILE)); + cl_git_mkfile(git_buf_cstr(&_path), "dummy"); + cl_assert_equal_i(GIT_REPOSITORY_STATE_CHERRY_PICK, git_repository_state(_repo)); +} diff --git a/tests-clar/reset/hard.c b/tests-clar/reset/hard.c index fdab9c536..c3f041817 100644 --- a/tests-clar/reset/hard.c +++ b/tests-clar/reset/hard.c @@ -58,3 +58,38 @@ void test_reset_hard__cannot_reset_in_a_bare_repository(void) git_repository_free(bare); } + +void test_reset_hard__cleans_up_merge(void) +{ + git_buf merge_head_path = GIT_BUF_INIT, + merge_msg_path = GIT_BUF_INIT, + merge_mode_path = GIT_BUF_INIT, + orig_head_path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD")); + cl_git_mkfile(git_buf_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n"); + + cl_git_pass(git_buf_joinpath(&merge_msg_path, git_repository_path(repo), "MERGE_MSG")); + cl_git_mkfile(git_buf_cstr(&merge_head_path), "Merge commit 0017bd4ab1ec30440b17bae1680cff124ab5f1f6\n"); + + cl_git_pass(git_buf_joinpath(&merge_msg_path, git_repository_path(repo), "MERGE_MODE")); + cl_git_mkfile(git_buf_cstr(&merge_head_path), ""); + + cl_git_pass(git_buf_joinpath(&orig_head_path, git_repository_path(repo), "ORIG_HEAD")); + cl_git_mkfile(git_buf_cstr(&orig_head_path), "0017bd4ab1ec30440b17bae1680cff124ab5f1f6"); + + retrieve_target_from_oid(&target, repo, "0017bd4ab1ec30440b17bae1680cff124ab5f1f6"); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD)); + + cl_assert(!git_path_exists(git_buf_cstr(&merge_head_path))); + cl_assert(!git_path_exists(git_buf_cstr(&merge_msg_path))); + cl_assert(!git_path_exists(git_buf_cstr(&merge_mode_path))); + + cl_assert(git_path_exists(git_buf_cstr(&orig_head_path))); + cl_git_pass(p_unlink(git_buf_cstr(&orig_head_path))); + + git_buf_free(&merge_head_path); + git_buf_free(&merge_msg_path); + git_buf_free(&merge_mode_path); + git_buf_free(&orig_head_path); +} diff --git a/tests-clar/reset/soft.c b/tests-clar/reset/soft.c index 1872baf3b..6796c80bc 100644 --- a/tests-clar/reset/soft.c +++ b/tests-clar/reset/soft.c @@ -1,5 +1,7 @@ #include "clar_libgit2.h" +#include "posix.h" #include "reset_helpers.h" +#include "path.h" #include "repo/repo_helpers.h" static git_repository *repo; @@ -110,3 +112,14 @@ void test_reset_soft__resetting_against_an_orphaned_head_repo_makes_the_head_no_ git_reference_free(head); } + +void test_reset_soft__fails_when_merging(void) +{ + git_buf merge_head_path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD")); + cl_git_mkfile(git_buf_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n"); + + cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT)); + cl_git_pass(p_unlink(git_buf_cstr(&merge_head_path))); +}