Commit db0a7e39 by Vicent Martí

Merge pull request #1977 from ethomson/revert

Revert support for a single commit
parents 96fb6a64 bab0b9f2
......@@ -117,6 +117,17 @@ GIT_EXTERN(const char *) git_commit_message(const git_commit *commit);
GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
/**
* Get the short "summary" of the git commit message.
*
* The returned message is the summary of the commit, comprising the
* first paragraph of the message with whitespace trimmed and squashed.
*
* @param commit a previously loaded commit.
* @return the summary of a commit or NULL on error
*/
GIT_EXTERN(const char *) git_commit_summary(git_commit *commit);
/**
* Get the commit time (i.e. committer time) of a commit.
*
* @param commit a previously loaded commit.
......
......@@ -71,6 +71,7 @@ typedef enum {
GITERR_MERGE,
GITERR_SSH,
GITERR_FILTER,
GITERR_REVERT,
} git_error_t;
/**
......
......@@ -488,13 +488,13 @@ GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *re
GIT_EXTERN(int) git_repository_message_remove(git_repository *repo);
/**
* Remove all the metadata associated with an ongoing git merge, including
* MERGE_HEAD, MERGE_MSG, etc.
* Remove all the metadata associated with an ongoing command like merge,
* revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc.
*
* @param repo A repository object
* @return 0 on success, or error
*/
GIT_EXTERN(int) git_repository_merge_cleanup(git_repository *repo);
GIT_EXTERN(int) git_repository_state_cleanup(git_repository *repo);
typedef int (*git_repository_fetchhead_foreach_cb)(const char *ref_name,
const char *remote_url,
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* 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_git_revert_h__
#define INCLUDE_git_revert_h__
#include "common.h"
#include "types.h"
#include "merge.h"
/**
* @file git2/revert.h
* @brief Git revert routines
* @defgroup git_revert Git revert routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
typedef struct {
unsigned int version;
/** For merge commits, the "mainline" is treated as the parent. */
unsigned int mainline;
git_merge_tree_opts merge_tree_opts;
git_checkout_opts checkout_opts;
} git_revert_opts;
#define GIT_REVERT_OPTS_VERSION 1
#define GIT_REVERT_OPTS_INIT {GIT_REVERT_OPTS_VERSION, 0, GIT_MERGE_TREE_OPTS_INIT, GIT_CHECKOUT_OPTS_INIT}
/**
* Reverts the given commits, producing changes in the working directory.
*
* @param repo the repository to revert
* @param commits the commits to revert
* @param commits_len the number of commits to revert
* @param flags merge flags
*/
GIT_EXTERN(int) git_revert(
git_repository *repo,
git_commit *commit,
const git_revert_opts *given_opts);
/** @} */
GIT_END_DECL
#endif
......@@ -31,6 +31,7 @@ void git_commit__free(void *_commit)
git__free(commit->raw_header);
git__free(commit->raw_message);
git__free(commit->message_encoding);
git__free(commit->summary);
git__free(commit);
}
......@@ -286,6 +287,34 @@ const char *git_commit_message(const git_commit *commit)
return message;
}
const char *git_commit_summary(git_commit *commit)
{
git_buf summary = GIT_BUF_INIT;
const char *msg, *space;
assert(commit);
if (!commit->summary) {
for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
break;
else if (msg[0] == '\n')
git_buf_putc(&summary, ' ');
else if (git__isspace(msg[0]))
space = space ? space : msg;
else if (space) {
git_buf_put(&summary, space, (msg - space) + 1);
space = NULL;
} else
git_buf_putc(&summary, *msg);
}
commit->summary = git_buf_detach(&summary);
}
return commit->summary;
}
int git_commit_tree(git_tree **tree_out, const git_commit *commit)
{
assert(commit);
......
......@@ -26,6 +26,8 @@ struct git_commit {
char *message_encoding;
char *raw_message;
char *raw_header;
char *summary;
};
void git_commit__free(void *commit);
......
......@@ -1206,7 +1206,7 @@ static git_merge_diff *merge_diff_from_index_entries(
/* Merge trees */
static int merge_index_insert_conflict(
static int merge_diff_list_insert_conflict(
git_merge_diff_list *diff_list,
struct merge_diff_df_data *merge_df_data,
const git_index_entry *tree_items[3])
......@@ -1222,7 +1222,7 @@ static int merge_index_insert_conflict(
return 0;
}
static int merge_index_insert_unmodified(
static int merge_diff_list_insert_unmodified(
git_merge_diff_list *diff_list,
const git_index_entry *tree_items[3])
{
......@@ -1252,7 +1252,7 @@ int git_merge_diff_list__find_differences(
size_t i, j;
int error = 0;
assert(diff_list && our_tree && their_tree);
assert(diff_list && (our_tree || their_tree));
if ((error = git_iterator_for_tree(&iterators[TREE_IDX_ANCESTOR], (git_tree *)ancestor_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
(error = git_iterator_for_tree(&iterators[TREE_IDX_OURS], (git_tree *)our_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
......@@ -1262,6 +1262,7 @@ int git_merge_diff_list__find_differences(
/* Set up the iterators */
for (i = 0; i < 3; i++) {
error = git_iterator_current(&items[i], iterators[i]);
if (error < 0 && error != GIT_ITEROVER)
goto done;
}
......@@ -1313,9 +1314,9 @@ int git_merge_diff_list__find_differences(
break;
if (cur_item_modified)
error = merge_index_insert_conflict(diff_list, &df_data, cur_items);
error = merge_diff_list_insert_conflict(diff_list, &df_data, cur_items);
else
error = merge_index_insert_unmodified(diff_list, cur_items);
error = merge_diff_list_insert_unmodified(diff_list, cur_items);
if (error < 0)
goto done;
......@@ -1325,6 +1326,7 @@ int git_merge_diff_list__find_differences(
continue;
error = git_iterator_advance(&items[i], iterators[i]);
if (error < 0 && error != GIT_ITEROVER)
goto done;
}
......@@ -1569,7 +1571,7 @@ int git_merge_trees(
size_t i;
int error = 0;
assert(out && repo && our_tree && their_tree);
assert(out && repo && (our_tree || their_tree));
*out = NULL;
......@@ -2268,7 +2270,7 @@ done:
return error;
}
static int merge_indexes(git_repository *repo, git_index *index_new)
int git_merge__indexes(git_repository *repo, git_index *index_new)
{
git_index *index_repo;
unsigned int index_repo_caps;
......@@ -2360,6 +2362,17 @@ done:
return error;
}
static int merge_state_cleanup(git_repository *repo)
{
const char *state_files[] = {
GIT_MERGE_HEAD_FILE,
GIT_MERGE_MODE_FILE,
GIT_MERGE_MSG_FILE,
};
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
int git_merge(
git_merge_result **out,
git_repository *repo,
......@@ -2440,7 +2453,7 @@ int git_merge(
/* TODO: recursive, octopus, etc... */
if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], &opts.merge_tree_opts)) < 0 ||
(error = merge_indexes(repo, index_new)) < 0 ||
(error = git_merge__indexes(repo, index_new)) < 0 ||
(error = git_repository_index(&index_repo, repo)) < 0 ||
(error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
goto on_error;
......@@ -2451,7 +2464,7 @@ int git_merge(
goto done;
on_error:
git_repository_merge_cleanup(repo);
merge_state_cleanup(repo);
git_index_free(index_new);
git__free(result);
......@@ -2495,39 +2508,6 @@ int git_merge__setup(
return error;
}
int git_repository_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_msg_path, repo->path_repository, GIT_MERGE_MSG_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;
}
/* Merge result data */
int git_merge_result_is_uptodate(git_merge_result *merge_result)
......
......@@ -158,4 +158,6 @@ int git_merge__setup(
size_t their_heads_len,
unsigned int flags);
int git_merge__indexes(git_repository *repo, git_index *index_new);
#endif
......@@ -1965,6 +1965,42 @@ int git_repository_state(git_repository *repo)
return state;
}
int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len)
{
git_buf path = GIT_BUF_INIT;
size_t i;
int error = 0;
for (i = 0; i < files_len; ++i) {
git_buf_clear(&path);
if ((error = git_buf_joinpath(&path, repo->path_repository, files[i])) < 0 ||
(git_path_isfile(git_buf_cstr(&path)) &&
(error = p_unlink(git_buf_cstr(&path))) < 0))
goto done;
}
done:
git_buf_free(&path);
return error;
}
static const char *state_files[] = {
GIT_MERGE_HEAD_FILE,
GIT_MERGE_MODE_FILE,
GIT_MERGE_MSG_FILE,
GIT_REVERT_HEAD_FILE,
GIT_CHERRY_PICK_HEAD_FILE,
};
int git_repository_state_cleanup(git_repository *repo)
{
assert(repo);
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
int git_repository_is_shallow(git_repository *repo)
{
git_buf path = GIT_BUF_INIT;
......
......@@ -169,4 +169,6 @@ GIT_INLINE(int) git_repository__ensure_not_bare(
return GIT_EBAREREPO;
}
int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
#endif
......@@ -149,7 +149,7 @@ int git_reset(
(error = git_index_write(index)) < 0)
goto cleanup;
if ((error = git_repository_merge_cleanup(repo)) < 0) {
if ((error = git_repository_state_cleanup(repo)) < 0) {
giterr_set(GITERR_INDEX, "%s - failed to clean up merge data", ERROR_MSG);
goto cleanup;
}
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* 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 "common.h"
#include "repository.h"
#include "filebuf.h"
#include "merge.h"
#include "git2/types.h"
#include "git2/merge.h"
#include "git2/revert.h"
#include "git2/commit.h"
#include "git2/sys/commit.h"
#define GIT_REVERT_FILE_MODE 0666
static int write_revert_head(
git_repository *repo,
const git_commit *commit,
const char *commit_oidstr)
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf file_path = GIT_BUF_INIT;
int error = 0;
assert(repo && commit);
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_REVERT_HEAD_FILE)) >= 0 &&
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) >= 0 &&
(error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
error = git_filebuf_commit(&file);
if (error < 0)
git_filebuf_cleanup(&file);
git_buf_free(&file_path);
return error;
}
static int write_merge_msg(
git_repository *repo,
const git_commit *commit,
const char *commit_oidstr,
const char *commit_msgline)
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf file_path = GIT_BUF_INIT;
int error = 0;
assert(repo && commit);
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) < 0 ||
(error = git_filebuf_printf(&file, "Revert \"%s\"\n\nThis reverts commit %s.\n",
commit_msgline, commit_oidstr)) < 0)
goto cleanup;
error = git_filebuf_commit(&file);
cleanup:
if (error < 0)
git_filebuf_cleanup(&file);
git_buf_free(&file_path);
return error;
}
static int revert_normalize_opts(
git_repository *repo,
git_revert_opts *opts,
const git_revert_opts *given,
const char *their_label)
{
int error = 0;
unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE_CREATE |
GIT_CHECKOUT_ALLOW_CONFLICTS;
GIT_UNUSED(repo);
if (given != NULL)
memcpy(opts, given, sizeof(git_revert_opts));
else {
git_revert_opts default_opts = GIT_REVERT_OPTS_INIT;
memcpy(opts, &default_opts, sizeof(git_revert_opts));
}
if (!opts->checkout_opts.checkout_strategy)
opts->checkout_opts.checkout_strategy = default_checkout_strategy;
if (!opts->checkout_opts.our_label)
opts->checkout_opts.our_label = "HEAD";
if (!opts->checkout_opts.their_label)
opts->checkout_opts.their_label = their_label;
return error;
}
static int revert_state_cleanup(git_repository *repo)
{
const char *state_files[] = { GIT_REVERT_HEAD_FILE, GIT_MERGE_MSG_FILE };
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
int git_revert(
git_repository *repo,
git_commit *commit,
const git_revert_opts *given_opts)
{
git_revert_opts opts;
git_commit *parent_commit = NULL;
git_tree *parent_tree = NULL, *our_tree = NULL, *revert_tree = NULL;
git_index *index_new = NULL, *index_repo = NULL;
char commit_oidstr[GIT_OID_HEXSZ + 1];
const char *commit_msg;
git_buf their_label = GIT_BUF_INIT;
int parent = 0;
int error = 0;
assert(repo && commit);
if ((error = git_repository__ensure_not_bare(repo, "revert")) < 0)
return error;
git_oid_fmt(commit_oidstr, git_commit_id(commit));
commit_oidstr[GIT_OID_HEXSZ] = '\0';
if ((commit_msg = git_commit_summary(commit)) == NULL) {
error = -1;
goto on_error;
}
if ((error = git_buf_printf(&their_label, "parent of %.7s... %s", commit_oidstr, commit_msg)) < 0 ||
(error = revert_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
(error = write_revert_head(repo, commit, commit_oidstr)) < 0 ||
(error = write_merge_msg(repo, commit, commit_oidstr, commit_msg)) < 0 ||
(error = git_repository_head_tree(&our_tree, repo)) < 0 ||
(error = git_commit_tree(&revert_tree, commit)) < 0)
goto on_error;
if (git_commit_parentcount(commit) > 1) {
if (!opts.mainline) {
giterr_set(GITERR_REVERT,
"Mainline branch is not specified but %s is a merge commit",
commit_oidstr);
error = -1;
goto on_error;
}
parent = opts.mainline;
} else {
if (opts.mainline) {
giterr_set(GITERR_REVERT,
"Mainline branch was specified but %s is not a merge",
commit_oidstr);
error = -1;
goto on_error;
}
parent = git_commit_parentcount(commit);
}
if (parent &&
((error = git_commit_parent(&parent_commit, commit, (parent - 1))) < 0 ||
(error = git_commit_tree(&parent_tree, parent_commit)) < 0))
goto on_error;
if ((error = git_merge_trees(&index_new, repo, revert_tree, our_tree, parent_tree, &opts.merge_tree_opts)) < 0 ||
(error = git_merge__indexes(repo, index_new)) < 0 ||
(error = git_repository_index(&index_repo, repo)) < 0 ||
(error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
goto on_error;
goto done;
on_error:
revert_state_cleanup(repo);
done:
git_index_free(index_new);
git_index_free(index_repo);
git_tree_free(parent_tree);
git_tree_free(our_tree);
git_tree_free(revert_tree);
git_commit_free(parent_commit);
git_buf_free(&their_label);
return error;
}
#include "clar_libgit2.h"
#include "commit.h"
#include "git2/commit.h"
static git_repository *_repo;
......@@ -44,3 +46,30 @@ void test_commit_commit__create_unexisting_update_ref(void)
git_signature_free(s);
git_reference_free(ref);
}
void assert_commit_summary(const char *expected, const char *given)
{
git_commit *dummy;
cl_assert(dummy = git__calloc(1, sizeof(struct git_commit)));
dummy->raw_message = git__strdup(given);
cl_assert_equal_s(expected, git_commit_summary(dummy));
git_commit__free(dummy);
}
void test_commit_commit__summary(void)
{
assert_commit_summary("One-liner with no trailing newline", "One-liner with no trailing newline");
assert_commit_summary("One-liner with trailing newline", "One-liner with trailing newline\n");
assert_commit_summary("Trimmed leading&trailing newlines", "\n\nTrimmed leading&trailing newlines\n\n");
assert_commit_summary("First paragraph only", "\nFirst paragraph only\n\n(There are more!)");
assert_commit_summary("First paragraph with unwrapped trailing\tlines", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)");
assert_commit_summary("\tLeading \ttabs", "\tLeading\n\ttabs\n\nis preserved");
assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved");
assert_commit_summary("Trailing tabs\tare removed", "Trailing tabs\tare removed\t\t");
assert_commit_summary("Trailing spaces are removed", "Trailing spaces are removed ");
assert_commit_summary("Trailing tabs", "Trailing tabs\t\n\nare removed");
assert_commit_summary("Trailing spaces", "Trailing spaces \n\nare removed");
}
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
hideDotFiles = dotGitOnly
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
2d440f2b3147d3dc7ad1085813478d6d869d5a4d
5acdc74af27172ec491d213ee36cea7eb9ef2579
13ee9cd5d8e1023c218e0e1ea684ec0c582b5050
52c95c4264245469a0617e289a7d737f156826b4
!File one!
!File one!
File one!
File one
File one
File one
File one
File one
File one
File one
File one!
!File one!
!File one!
!File one!
File two
File two
File two
File two
File two
File two
File two
File two
File two
File two
File two
File two
File two
File two
File two
File two
File three
File three
File three
File three
File three
File three
File three
File three
File three
File three
File three
File three
File three
File three
File three
File three
File six, actually!
File four!
File four!
File four!
File four!
File four!
File four!
File four!
File four!
File four!
File four!
File four!
File four!
File four!
#include "clar.h"
#include "clar_libgit2.h"
#include "buffer.h"
#include "fileops.h"
#include "git2/revert.h"
#include "../merge/merge_helpers.h"
#define TEST_REPO_PATH "revert"
static git_repository *repo;
static git_index *repo_index;
// Fixture setup and teardown
void test_revert_revert__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
git_repository_index(&repo_index, repo);
}
void test_revert_revert__cleanup(void)
{
git_index_free(repo_index);
cl_git_sandbox_cleanup();
}
/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45
* git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */
void test_revert_revert__automerge(void)
{
git_commit *head, *commit;
git_oid head_oid, revert_oid;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
git_oid_fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac");
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
git_commit_free(commit);
git_commit_free(head);
}
/* git revert --no-commit 72333f47d4e83616630ff3b0ffe4c0faebcc3c45 */
void test_revert_revert__conflicts(void)
{
git_reference *head_ref;
git_commit *head, *commit;
git_oid revert_oid;
git_buf conflicting_buf = GIT_BUF_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 1, "file1.txt" },
{ 0100644, "4b8fcff56437e60f58e9a6bc630dd242ebf6ea2c", 2, "file1.txt" },
{ 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 3, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
git_oid_fromstr(&revert_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45");
cl_git_pass(git_repository_head(&head_ref, repo));
cl_git_pass(git_reference_peel((git_object **)&head, head_ref, GIT_OBJ_COMMIT));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
cl_git_pass(git_futils_readbuffer(&conflicting_buf,
TEST_REPO_PATH "/file1.txt"));
cl_assert(strcmp(conflicting_buf.ptr, "!File one!\n" \
"!File one!\n" \
"File one!\n" \
"File one\n" \
"File one\n" \
"File one\n" \
"File one\n" \
"File one\n" \
"File one\n" \
"File one\n" \
"<<<<<<< HEAD\n" \
"File one!\n" \
"!File one!\n" \
"!File one!\n" \
"!File one!\n" \
"=======\n" \
"File one\n" \
"File one\n" \
"File one\n" \
"File one\n" \
">>>>>>> parent of 72333f4... automergeable changes\n") == 0);
git_commit_free(commit);
git_commit_free(head);
git_reference_free(head_ref);
git_buf_free(&conflicting_buf);
}
/* git reset --hard 39467716290f6df775a91cdb9a4eb39295018145
* git revert --no-commit ebb03002cee5d66c7732dd06241119fe72ab96a5
*/
void test_revert_revert__orphan(void)
{
git_commit *head, *commit;
git_oid head_oid, revert_oid;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "296a6d3be1dff05c5d1f631d2459389fa7b619eb", 0, "file-mainline.txt" },
};
git_oid_fromstr(&head_oid, "39467716290f6df775a91cdb9a4eb39295018145");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
git_oid_fromstr(&revert_oid, "ebb03002cee5d66c7732dd06241119fe72ab96a5");
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 1));
git_commit_free(commit);
git_commit_free(head);
}
/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45
* git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */
void test_revert_revert__conflict_use_ours(void)
{
git_commit *head, *commit;
git_oid head_oid, revert_oid;
git_revert_opts opts = GIT_REVERT_OPTS_INIT;
opts.merge_tree_opts.automerge_flags = GIT_MERGE_AUTOMERGE_NONE;
opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 1, "file1.txt" },
{ 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 2, "file1.txt" },
{ 0100644, "747726e021bc5f44b86de60e3032fd6f9f1b8383", 3, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
struct merge_index_entry merge_filesystem_entries[] = {
{ 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 0, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
git_oid_fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac");
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 4));
git_commit_free(commit);
git_commit_free(head);
}
/* git reset --hard cef56612d71a6af8d8015691e4865f7fece905b5
* git revert --no-commit 55568c8de5322ff9a95d72747a239cdb64a19965
*/
void test_revert_revert__rename_1_of_2(void)
{
git_commit *head, *commit;
git_oid head_oid, revert_oid;
git_revert_opts opts = GIT_REVERT_OPTS_INIT;
opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
opts.merge_tree_opts.rename_threshold = 50;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "747726e021bc5f44b86de60e3032fd6f9f1b8383", 0, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 3, "file4.txt" },
{ 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 1, "file5.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 2, "file6.txt" },
};
git_oid_fromstr(&head_oid, "cef56612d71a6af8d8015691e4865f7fece905b5");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
git_oid_fromstr(&revert_oid, "55568c8de5322ff9a95d72747a239cdb64a19965");
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
git_commit_free(commit);
git_commit_free(head);
}
/* git reset --hard 55568c8de5322ff9a95d72747a239cdb64a19965
* git revert --no-commit HEAD~1 */
void test_revert_revert__rename(void)
{
git_commit *head, *commit;
git_oid head_oid, revert_oid;
git_revert_opts opts = GIT_REVERT_OPTS_INIT;
opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
opts.merge_tree_opts.rename_threshold = 50;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 1, "file4.txt" },
{ 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 2, "file5.txt" },
};
struct merge_name_entry merge_name_entries[] = {
{ "file4.txt", "file5.txt", "" },
};
git_oid_fromstr(&head_oid, "55568c8de5322ff9a95d72747a239cdb64a19965");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
git_oid_fromstr(&revert_oid, "0aa8c7e40d342fff78d60b29a4ba8e993ed79c51");
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 2));
cl_assert(merge_test_names(repo_index, merge_name_entries, 1));
git_commit_free(commit);
git_commit_free(head);
}
/* git revert --no-commit HEAD */
void test_revert_revert__head(void)
{
git_reference *head;
git_commit *commit;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 0, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
/* HEAD is 2d440f2b3147d3dc7ad1085813478d6d869d5a4d */
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJ_COMMIT));
cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD));
cl_git_pass(git_revert(repo, commit, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
cl_assert(merge_test_workdir(repo, merge_index_entries, 4));
git_reference_free(head);
git_commit_free(commit);
}
void test_revert_revert__nonmerge_fails_mainline_specified(void)
{
git_reference *head;
git_commit *commit;
git_revert_opts opts = GIT_REVERT_OPTS_INIT;
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJ_COMMIT));
opts.mainline = 1;
cl_must_fail(git_revert(repo, commit, &opts));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/REVERT_HEAD"));
git_reference_free(head);
git_commit_free(commit);
}
/* git reset --hard 5acdc74af27172ec491d213ee36cea7eb9ef2579
* git revert HEAD */
void test_revert_revert__merge_fails_without_mainline_specified(void)
{
git_commit *head;
git_oid head_oid;
git_oid_fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
cl_must_fail(git_revert(repo, head, NULL));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/REVERT_HEAD"));
git_commit_free(head);
}
/* git reset --hard 5acdc74af27172ec491d213ee36cea7eb9ef2579
* git revert HEAD -m1 --no-commit */
void test_revert_revert__merge_first_parent(void)
{
git_commit *head;
git_oid head_oid;
git_revert_opts opts = GIT_REVERT_OPTS_INIT;
opts.mainline = 1;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "296a6d3be1dff05c5d1f631d2459389fa7b619eb", 0, "file-mainline.txt" },
{ 0100644, "0cdb66192ee192f70f891f05a47636057420e871", 0, "file1.txt" },
{ 0100644, "73ec36fa120f8066963a0bc9105bb273dbd903d7", 0, "file2.txt" },
};
git_oid_fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
cl_git_pass(git_revert(repo, head, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
git_commit_free(head);
}
void test_revert_revert__merge_second_parent(void)
{
git_commit *head;
git_oid head_oid;
git_revert_opts opts = GIT_REVERT_OPTS_INIT;
opts.mainline = 2;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "33c6fd981c49a2abf2971482089350bfc5cda8ea", 0, "file-branch.txt" },
{ 0100644, "0cdb66192ee192f70f891f05a47636057420e871", 0, "file1.txt" },
{ 0100644, "73ec36fa120f8066963a0bc9105bb273dbd903d7", 0, "file2.txt" },
};
git_oid_fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
cl_git_pass(git_revert(repo, head, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
git_commit_free(head);
}
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