8 use Test
::More
qw(no_plan);
11 if (! -e
"/tmp/git-rehi/test-repo") {
12 !system("mkdir -p /tmp/git-rehi/test-repo && ( cd /tmp/git-rehi/test-repo && git init && git fast-import && git config user.email test.author\@example.com && git config user.name TestAuthor ) <itest-repo.data")
13 or die("Test repo initialization failed: $? ($!)");
15 my $SOURCE_DIR = getcwd
;
16 my $STACK_ROOT = `stack path --local-install-root`;
17 $STACK_ROOT =~ s/[\n\r]+$//;
18 $STACK_ROOT =~ s/\\/\//g
;
19 chdir("/tmp/git-rehi/test-repo") or die("Unable to chdir to /tmp/git-rehi/test-repo: $!");
20 my $testee = $STACK_ROOT . "/bin/git-rehi";
25 sub t
(&*) { my ($block, $name) = @_;
26 push @Tests, [$name, $block];
30 sub cmd
($;$$) { my ($cmd) = @_;
31 my $goal_status = do { if (scalar @_ > 1) { $_[1]; } else { 0; }; };
32 my $output_dest = do { if (scalar @_ > 2) { $_[2]; } else { undef; }; };
33 my $output = `$cmd 2>&1 </dev/null`;
37 if ($goal_status eq "!= 0" && $status != 0
38 || $goal_status =~ /^-?\d+$/ && $status == $goal_status)
40 if ($output =~ /Internal error: |Unexpected happened: |IO error: /) {
41 diag
("Command failed with unexpected error\nCommand: $cmd\nOutput:\n$output\n");
47 diag
("Command status does not match: $status vs $goal_status\nCommand: $cmd\nOutput:\n$output\n");
50 if (defined $output_dest) {
51 ${$output_dest} = $output;
55 my $SUBTEST_COMMIT_COUNT;
58 `$testee --abort 2>&1`;
59 `git checkout -f --no-track -B master origin/master1 2>&1`;
60 die("reset checkout failed") if ($?
!= 0);
61 `git clean -f -x -d 2>&1`;
62 die("reset clean failed") if ($?
!= 0);
63 $SUBTEST_COMMIT_COUNT = 0;
67 sub tc
($;$) { my ($files, $tag) = @_;
68 foreach my $filename (keys %{$files}) {
69 if (defined $files->{$filename}) {
70 write_file
($filename, $files->{$filename});
71 cmd
("git add -- $filename");
73 cmd
("git rm -- $filename");
76 my $msg = "$TEST_NAME$SUBTEST_COMMIT_COUNT";
77 $SUBTEST_COMMIT_COUNT++;
78 cmd
("git commit -m $msg -q");
80 cmd
("git tag -f $tag");
86 sub new
($$) { my ($class, $name, $new_value) = @_;
87 my $res = { name
=> $name, prev_exists
=> exists $ENV{$name}, prev_value
=> $ENV{$name} };
88 $ENV{$name} = $new_value;
89 return (bless $res, $class);
92 sub DESTROY
($) { my ($instance) = @_;
93 my $name = $instance->{name
};
94 if ($instance->{prev_exists
}) {
95 #print "return $name <-- $instance->{prev_value}";
96 $ENV{$name} = $instance->{prev_value
};
98 #print "remove $name";
106 sub write_file
($$) { my ($file,$content) = @_;
107 open(my $fh, ">", $file);
112 sub seq
($$;$) { my ($from,$to,$prefix) = @_;
113 if (!defined $prefix) { $prefix = ""; }
115 for (my $i = $from; $i < $to; ++$i) {
116 $buf = $buf . $prefix . $i . "\n";
123 cmd
("git reset --hard origin/b2");
124 cmd
("$testee origin/b1");
125 cmd
("git diff --quiet origin/master1");
129 my $g = env_guard
->new("GIT_EDITOR", "/bin/true");
130 cmd
("git reset --hard origin/b2");
131 cmd
("$testee -i origin/b1");
132 cmd
("git diff --quiet origin/master1");
136 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
137 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "set-comment");
138 cmd
("git reset --hard origin/b2");
139 cmd
("$testee -i origin/b1");
140 is
(`git log --pretty=format:%B -1`, "test-comment\n");
144 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
145 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "edit-noop");
146 cmd
("git reset --hard origin/b2");
147 cmd
("$testee -i origin/base");
148 cmd
("$testee --continue");
149 is
(`git rev-parse origin/b2`, `git rev-parse HEAD`);
153 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
154 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "edit-noop");
155 cmd
("git reset --hard origin/b2");
156 cmd
("$testee -i origin/base");
157 is
(`git symbolic-ref --quiet HEAD`, "");
161 cmd
("git reset --hard origin/b1");
162 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
163 cmd
("$testee -i HEAD");
164 cmd
("git diff --quiet origin/master1");
168 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
169 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "empty");
170 cmd
("$testee -i origin/b4 ..origin/base");
171 cmd
("git diff --quiet origin/master1");
175 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
176 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "empty-with-comment");
177 cmd
("$testee -i origin/b4 ..origin/base");
178 cmd
("git diff --quiet origin/master1");
179 } edit_empty_with_comment
;
182 cmd
("git reset --hard origin/b1");
183 cmd
("git branch -f tmp origin/b2");
184 cmd
("$testee HEAD tmp");
185 is
(`git symbolic-ref HEAD`, "refs/heads/tmp\n");
186 cmd
("git diff --quiet origin/master1");
190 cmd
("git reset --hard origin/b1");
191 cmd
("git branch -f tmp origin/b2");
192 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
193 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
194 my $old_master = `git show --quiet --pretty=format:%h master`;
195 cmd
("$testee -i HEAD tmp", "!= 0");
196 cmd
("$testee --abort");
197 is
(`git symbolic-ref HEAD`, "refs/heads/master\n");
198 is
(`git show --quiet --pretty=format:%h master`, $old_master);
202 cmd
("$testee origin/base ..origin/b1..");
203 cmd
("git diff --quiet origin/master1");
207 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
208 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-c");
209 cmd
("$testee -i origin/b2");
210 cmd
("git diff --quiet origin/master1");
211 is
(`git log --pretty=format:%B -1`, "merge\n");
215 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
216 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-no-ff");
217 cmd
("$testee -i origin/b4");
218 is_deeply
([split(/[ \n]/,`git show --quiet --pretty=format:%p HEAD`)],
219 [split(/\n/, `git show --quiet --pretty=format:%h origin/b4 origin/b2`)]);
223 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
224 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-no-ff-reuse");
225 cmd
("git reset --hard origin/b2");
226 cmd
("$testee -i origin/b4");
227 is_deeply
([split(/[ \n]/,`git show --quiet --pretty=format:%p HEAD`)],
228 [split(/\n/, `git show --quiet --pretty=format:%h origin/b4 origin/b2`)]);
229 } edit_merge_no_ff_reuse
;
232 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
233 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-no-c");
234 cmd
("$testee -i origin/b2");
235 cmd
("git diff --quiet origin/master1");
239 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
240 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-c");
241 cmd
("$testee -i origin/b1", "!= 0");
242 } fastforward_merge_fails
;
245 cmd
("git reset --hard origin/b4");
246 cmd
("git commit --allow-empty -m UPDATE");
247 cmd
("$testee HEAD origin/b4..origin/b3..origin/b2b3", "!= 0"); # conflict
248 cmd
("$testee --continue", "!= 0"); # no continue without resolving
249 cmd
("git checkout origin/b2b3 -- file1");
250 cmd
("git add file1");
251 my $ge = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
252 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-resolved");
253 cmd
("$testee --continue");
254 cmd
("git diff --quiet origin/b2b3");
255 is
(`git log --pretty=format:%B -1`, "Merge origin/b2 with resolving conflict (test)\n");
259 cmd
("$testee origin/b1");
260 like
(`cat .git/rehi_todo.backup`, qr
/^merge
-c
[0-9a
-z
]+ [0-9a
-z
]+,HEAD merge
$/);
261 is
(`git show --quiet --pretty=format:%h HEAD`,
262 `git show --quiet --pretty=format:%h origin/master1`);
263 } merge_second_parent
;
266 cmd
("git reset --hard origin/base");
267 cmd
("git commit --allow-empty -m UPDATE");
268 cmd
("$testee HEAD origin/base..origin/b3..origin/b2b3", "!= 0");
269 ok
(-f
".git/rehi/todo.backup");
273 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
274 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-inner");
275 cmd
("$testee -i origin/base~1 ..origin/base");
276 cmd
("git diff --quiet origin/master1");
277 is
(`git branch -r --merged=HEAD origin/master1` . `git branch -r --merged=HEAD origin/b1` . `git branch -r --merged=HEAD origin/b2`,
279 "source branches are not merged");
280 } inner_merge_handmade
;
283 cmd
("git reset --hard origin/b4");
284 cmd
("git commit --allow-empty -m 'LOCAL'");
285 cmd
("$testee HEAD ..origin/master1");
286 cmd
("git diff --quiet origin/master1");
287 is
(`git branch -r --merged=HEAD origin/master1` . `git branch -r --merged=HEAD origin/b1` . `git branch -r --merged=HEAD origin/b2`,
289 "source branches are not merged");
290 } inner_merge_detected
;
293 cmd
("git reset --hard origin/b2b3");
294 cmd
("$testee origin/b4");
295 is
(`git show --quiet --pretty=format:%h HEAD`,
296 `git show --quiet --pretty=format:%h origin/b2b3`);
297 } fastforward_over_merges
;
300 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
301 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-inner");
302 cmd
("$testee -i origin/b4");
304 my $gc2 = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "merge-inner-broken");
305 if (-f
"save_todo") { cmd
("rm save_todo"); }
306 cmd
("$testee -i origin/b4", "!= 0"); # unknown refs - should fail
307 ok
(-f
"save_todo"); # make sure it reached editor
312 cmd
("git reset --hard origin/b_noffmerge");
313 cmd
("$testee origin/b4 ..origin/base..");
314 is
(`git show --quiet --pretty=format:%h HEAD`,
315 `git show --quiet --pretty=format:%h origin/b_noffmerge`);
316 } handle_noff_merges
;
319 cmd
("git reset --hard origin/b_diamond_after_merge");
320 cmd
("$testee origin/b2");
321 } inner_merge_after_merge
;
324 cmd
("git reset --hard origin/b_merge_of_merges");
325 cmd
("$testee origin/b5");
329 cmd
("git reset --hard origin/base");
330 write_file
("file", seq
(1, 100, "line"));
331 cmd
("git reset --hard origin/base");
333 cmd
("git commit -m base file");
334 cmd
("git tag -f base");
335 cmd
("git commit -m dummy --allow-empty");
336 cmd
("git tag -f rehiBase");
337 write_file
("file", seq
(1, 33, "line") . "line33edit\n" . seq
(34,100,"line"));
338 cmd
("git commit -m b2 file");
339 cmd
("git tag -f b2");
340 cmd
("git reset --hard base");
341 write_file
("file", seq
(1, 73, "line") . "line73edit\n" . seq
(74,100,"line"));
342 cmd
("git commit -m b1 file");
344 cmd
("git tag -f b1");
345 cmd
("$testee base rehiBase..");
346 cmd
("git diff --exit-code b1..HEAD");
350 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
351 my $gc2 = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
352 cmd
("$testee -i origin/base", "!= 0");
353 cmd
("grep -q 'pick.*change2\$' save_todo");
354 } optimal_first_parent
;
357 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
358 my $gc2 = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
359 cmd
("$testee -i origin/base origin/b1~1..", "!= 0");
360 cmd
("grep -q 'pick.*change1\$' save_todo");
361 } optimal_include_start_from_sourcefrom
;
364 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
365 my $gc2 = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
366 cmd
("$testee -i origin/b1~1", "!= 0");
367 cmd
("grep -q 'pick.*change1\$' save_todo");
368 } optimal_include_start_from_base
;
371 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
372 my $gc2 = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "fail");
373 cmd
("$testee -i HEAD", "!= 0");
374 is
(`$testee --current`, "Current: exec false\n");
378 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
379 my $gc2 = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "fail-pick");
380 my $hash = `git rev-parse --short origin/b2`;
381 $hash =~ s/[\n\r]*$//s;
382 my $gc3 = env_guard
->new("GIT_SEQUENCE_EDITOR_STEP_HASH", $hash);
383 cmd
("$testee -i origin/b3 ..origin/b2b3", "!= 0");
384 is
(`$testee --current`, "Current: pick $hash change2\n");
388 cmd
("$testee --current", "!= 0");
392 tc
({ "f1" => "l1\nl2\nl3\n" }, "base");
393 tc
({ "f1" => "l1\nl2r1\nl3\n" });
394 tc
({ "f1" => "l1\nl2r2\nl3\n" }, "src_dest");
395 cmd
("git reset --hard base");
396 tc
({ "f1" => "l1\nl2l1\nl3\n" });
398 my $g = env_guard
->new("GIT_EDITOR", "$SOURCE_DIR/itest-edit.sh");
399 my $gc = env_guard
->new("GIT_SEQUENCE_EDITOR_CASE", "pass");
400 cmd
("$testee -i HEAD base..src_dest", "!= 0");
401 like
(`$testee --current`, qr/pick [0-9a-f]+ current_failed_twice1\n/);
402 write_file
("f1", "l1\nl2l2\nl3\n");
403 cmd
("git add f1 && git commit --no-edit");
404 cmd
("$testee --continue", "!= 0");
405 like
(`$testee --current`, qr/pick [0-9a-f]+ current_failed_twice2\n/);
406 } current_failed_twice
;
409 tc
({ "failed_pick_prints_conflicts_filename" => "l1\nl2\nl3\n",
410 "failed_pick_prints_noconflict_filename" => "l1\nl2\n" }, "base");
411 tc
({ "failed_pick_prints_conflicts_filename" => "l1\nl2r\nl3\n",
412 "failed_pick_prints_noconflict_filename" => "l1\nl2r\n" }, "src_dest");
413 cmd
("git reset --hard base");
414 tc
({ "failed_pick_prints_conflicts_filename" => "l1\nl2l\nl3\n" });
417 cmd
("$testee HEAD base..src_dest", "!= 0", \
$out);
418 like
($out, qr/\bfailed_pick_prints_conflicts_filename\b/);
419 unlike
($out, qr/\bfailed_pick_prints_noconflict_filename\b/);
420 } failed_pick_prints_conflicts
;
424 my $g = env_guard
->new("GIT_EDITOR", "/bin/true");
425 cmd
("git reset --hard origin/b3");
426 cmd
("git merge -sours -m ours_with_ref_comment origin/b2");
427 cmd
("git branch -f t_b3b2_ours");
428 cmd
("git reset --hard origin/b3");
429 cmd
("git commit --allow-empty -m dummy");
430 cmd
("$testee -i HEAD t_b3b2_ours~1..t_b3b2_ours");
431 is
("ours_with_ref_comment\n", `git log --pretty=format:%B -1`);
435 cmd
("$testee origin/b5 origin/b5..", "!= 0");
436 cmd
("$testee origin/b2");
440 cmd
("git reset --hard origin/b2");
441 cmd
("git tag -a -f -m origin_base origin_base origin/base");
442 cmd
("$testee origin/b1 origin_base..");
443 cmd
("git diff --quiet origin/master1");
448 @argv_idx{@ARGV} = ();
450 foreach my $test (@Tests) {
451 if (!scalar %argv_idx || exists $argv_idx{$test->[0]}) {
453 $TEST_NAME = $test->[0];
454 if($] >= "5.012000") {
455 subtest
$test->[0] => $test->[1];
457 print "test: " . $test->[0] . "\n";
459 print "end: " . $test->[0] . "\n";
464 # vim: foldmethod=marker