hard.c 8.72 KB
Newer Older
1 2 3 4
#include "clar_libgit2.h"
#include "posix.h"
#include "reset_helpers.h"
#include "path.h"
5
#include "futils.h"
6 7 8 9 10 11 12 13 14 15 16 17

static git_repository *repo;
static git_object *target;

void test_reset_hard__initialize(void)
{
	repo = cl_git_sandbox_init("status");
	target = NULL;
}

void test_reset_hard__cleanup(void)
{
18 19 20 21
	if (target != NULL) {
		git_object_free(target);
		target = NULL;
	}
22

23 24 25
	cl_git_sandbox_cleanup();
}

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
static int strequal_ignore_eol(const char *exp, const char *str)
{
	while (*exp && *str) {
		if (*exp != *str) {
			while (*exp == '\r' || *exp == '\n') ++exp;
			while (*str == '\r' || *str == '\n') ++str;
			if (*exp != *str)
				return false;
		} else {
			exp++; str++;
		}
	}
	return (!*exp && !*str);
}

Russell Belfer committed
41
void test_reset_hard__resetting_reverts_modified_files(void)
42
{
Russell Belfer committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
	git_buf path = GIT_BUF_INIT, content = GIT_BUF_INIT;
	int i;
	static const char *files[4] = {
		"current_file",
		"modified_file",
		"staged_new_file",
		"staged_changes_modified_file" };
	static const char *before[4] = {
		"current_file\n",
		"modified_file\nmodified_file\n",
		"staged_new_file\n",
		"staged_changes_modified_file\nstaged_changes_modified_file\nstaged_changes_modified_file\n"
	};
	static const char *after[4] = {
		"current_file\n",
		"modified_file\n",
59
		NULL,
Russell Belfer committed
60 61 62 63 64 65 66 67 68 69 70 71
		"staged_changes_modified_file\n"
	};
	const char *wd = git_repository_workdir(repo);

	cl_assert(wd);

	for (i = 0; i < 4; ++i) {
		cl_git_pass(git_buf_joinpath(&path, wd, files[i]));
		cl_git_pass(git_futils_readbuffer(&content, path.ptr));
		cl_assert_equal_s(before[i], content.ptr);
	}

72
	cl_git_pass(git_revparse_single(&target, repo, "26a125e"));
73

74
	cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
75

Russell Belfer committed
76 77 78 79
	for (i = 0; i < 4; ++i) {
		cl_git_pass(git_buf_joinpath(&path, wd, files[i]));
		if (after[i]) {
			cl_git_pass(git_futils_readbuffer(&content, path.ptr));
80
			cl_assert(strequal_ignore_eol(after[i], content.ptr));
Russell Belfer committed
81 82 83 84 85
		} else {
			cl_assert(!git_path_exists(path.ptr));
		}
	}

86 87
	git_buf_dispose(&content);
	git_buf_dispose(&path);
88
}
89 90 91 92 93 94 95 96

void test_reset_hard__cannot_reset_in_a_bare_repository(void)
{
	git_repository *bare;

	cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git")));
	cl_assert(git_repository_is_bare(bare) == true);

97
	cl_git_pass(git_revparse_single(&target, bare, KNOWN_COMMIT_IN_BARE_REPO));
98

99
	cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_HARD, NULL));
100 101 102

	git_repository_free(bare);
}
Edward Thomson committed
103

104 105 106
static void index_entry_init(git_index *index, int side, git_oid *oid)
{
	git_index_entry entry;
nulltoken committed
107

108
	memset(&entry, 0x0, sizeof(git_index_entry));
nulltoken committed
109

110
	entry.path = "conflicting_file";
111
	GIT_INDEX_ENTRY_STAGE_SET(&entry, side);
112
	entry.mode = 0100644;
113
	git_oid_cpy(&entry.id, oid);
nulltoken committed
114

115 116 117 118 119 120 121 122 123
	cl_git_pass(git_index_add(index, &entry));
}

static void unmerged_index_init(git_index *index, int entries)
{
	int write_ancestor = 1;
	int write_ours = 2;
	int write_theirs = 4;
	git_oid ancestor, ours, theirs;
nulltoken committed
124

125 126 127
	git_oid_fromstr(&ancestor, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
	git_oid_fromstr(&ours, "32504b727382542f9f089e24fddac5e78533e96c");
	git_oid_fromstr(&theirs, "061d42a44cacde5726057b67558821d95db96f19");
nulltoken committed
128

129
	cl_git_rewritefile("status/conflicting_file", "conflicting file\n");
nulltoken committed
130

131 132
	if (entries & write_ancestor)
		index_entry_init(index, 1, &ancestor);
nulltoken committed
133

134 135
	if (entries & write_ours)
		index_entry_init(index, 2, &ours);
nulltoken committed
136

137 138 139 140 141 142 143 144
	if (entries & write_theirs)
		index_entry_init(index, 3, &theirs);
}

void test_reset_hard__resetting_reverts_unmerged(void)
{
	git_index *index;
	int entries;
nulltoken committed
145

146 147 148 149
	/* Ensure every permutation of non-zero stage entries results in the
	 * path being cleaned up. */
	for (entries = 1; entries < 8; entries++) {
		cl_git_pass(git_repository_index(&index, repo));
nulltoken committed
150

151 152
		unmerged_index_init(index, entries);
		cl_git_pass(git_index_write(index));
nulltoken committed
153

154
		cl_git_pass(git_revparse_single(&target, repo, "26a125e"));
155
		cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
nulltoken committed
156

157
		cl_assert(git_path_exists("status/conflicting_file") == 0);
nulltoken committed
158

159 160
		git_object_free(target);
		target = NULL;
nulltoken committed
161 162

		git_index_free(index);
163 164 165
	}
}

Edward Thomson committed
166 167 168 169 170 171 172 173 174 175 176
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"));
177
	cl_git_mkfile(git_buf_cstr(&merge_msg_path), "Merge commit 0017bd4ab1ec30440b17bae1680cff124ab5f1f6\n");
Edward Thomson committed
178

179 180
	cl_git_pass(git_buf_joinpath(&merge_mode_path, git_repository_path(repo), "MERGE_MODE"));
	cl_git_mkfile(git_buf_cstr(&merge_mode_path), "");
Edward Thomson committed
181 182 183 184

	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");

185
	cl_git_pass(git_revparse_single(&target, repo, "0017bd4"));
186
	cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
Edward Thomson committed
187 188 189 190 191 192 193 194

	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)));

195 196 197 198
	git_buf_dispose(&merge_head_path);
	git_buf_dispose(&merge_msg_path);
	git_buf_dispose(&merge_mode_path);
	git_buf_dispose(&orig_head_path);
Edward Thomson committed
199
}
Ben Straub committed
200 201 202

void test_reset_hard__reflog_is_correct(void)
{
203
	git_buf buf = GIT_BUF_INIT;
204
	git_annotated_commit *annotated;
Ben Straub committed
205 206 207 208 209 210 211 212
	const char *exp_msg = "commit: Add a file which name should appear before the "
		"\"subdir/\" folder while being dealt with by the treewalker";

	reflog_check(repo, "HEAD", 3, "emeric.fermas@gmail.com", exp_msg);
	reflog_check(repo, "refs/heads/master", 3, "emeric.fermas@gmail.com", exp_msg);

	/* Branch not moving, no reflog entry */
	cl_git_pass(git_revparse_single(&target, repo, "HEAD^{commit}"));
213
	cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
Ben Straub committed
214 215 216
	reflog_check(repo, "HEAD", 3, "emeric.fermas@gmail.com", exp_msg);
	reflog_check(repo, "refs/heads/master", 3, "emeric.fermas@gmail.com", exp_msg);

217 218
	git_object_free(target);

219
	/* Moved branch, expect id in message */
Ben Straub committed
220
	cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}"));
221 222 223 224
	cl_git_pass(git_buf_printf(&buf, "reset: moving to %s", git_oid_tostr_s(git_object_id(target))));
	cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
	reflog_check(repo, "HEAD", 4, NULL, git_buf_cstr(&buf));
	reflog_check(repo, "refs/heads/master", 4, NULL, git_buf_cstr(&buf));
225

226
	git_buf_dispose(&buf);
227 228 229 230 231 232 233 234 235 236

	/* Moved branch, expect revspec in message */
	exp_msg = "reset: moving to HEAD~^{commit}";
	cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "HEAD~^{commit}"));
	cl_git_pass(git_reset_from_annotated(repo, annotated, GIT_RESET_HARD, NULL));
	reflog_check(repo, "HEAD", 5, NULL, exp_msg);
	reflog_check(repo, "refs/heads/master", 5, NULL, exp_msg);

	git_annotated_commit_free(annotated);

Ben Straub committed
237
}
238 239 240

void test_reset_hard__switch_file_to_dir(void)
{
241
	git_index_entry entry = {{ 0 }};
242
	git_index *idx;
243
	git_odb *odb;
244 245 246 247 248 249
	git_object *commit;
	git_tree *tree;
	git_signature *sig;
	git_oid src_tree_id, tgt_tree_id;
	git_oid src_id, tgt_id;

250
	cl_git_pass(git_repository_odb(&odb, repo));
251
	cl_git_pass(git_odb_write(&entry.id, odb, "", 0, GIT_OBJECT_BLOB));
252 253
	git_odb_free(odb);

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
	entry.mode = GIT_FILEMODE_BLOB;
	cl_git_pass(git_index_new(&idx));
	cl_git_pass(git_signature_now(&sig, "foo", "bar"));

	/* Create the old tree */
	entry.path = "README";
	cl_git_pass(git_index_add(idx, &entry));
	entry.path = "dir";
	cl_git_pass(git_index_add(idx, &entry));

	cl_git_pass(git_index_write_tree_to(&src_tree_id, idx, repo));
	cl_git_pass(git_index_clear(idx));

	cl_git_pass(git_tree_lookup(&tree, repo, &src_tree_id));
	cl_git_pass(git_commit_create(&src_id, repo, NULL, sig, sig, NULL, "foo", tree, 0, NULL));
	git_tree_free(tree);

	/* Create the new tree */
	entry.path = "README";
	cl_git_pass(git_index_add(idx, &entry));
	entry.path = "dir/FILE";
	cl_git_pass(git_index_add(idx, &entry));

	cl_git_pass(git_index_write_tree_to(&tgt_tree_id, idx, repo));
	cl_git_pass(git_tree_lookup(&tree, repo, &tgt_tree_id));
	cl_git_pass(git_commit_create(&tgt_id, repo, NULL, sig, sig, NULL, "foo", tree, 0, NULL));
	git_tree_free(tree);
	git_index_free(idx);
	git_signature_free(sig);

	/* Let's go to a known state of the src commit with the file named 'dir' */
285
	cl_git_pass(git_object_lookup(&commit, repo, &src_id, GIT_OBJECT_COMMIT));
286 287 288 289
	cl_git_pass(git_reset(repo, commit, GIT_RESET_HARD, NULL));
	git_object_free(commit);

	/* And now we move over to the commit with the directory named 'dir' */
290
	cl_git_pass(git_object_lookup(&commit, repo, &tgt_id, GIT_OBJECT_COMMIT));
291 292 293
	cl_git_pass(git_reset(repo, commit, GIT_RESET_HARD, NULL));
	git_object_free(commit);
}