<?php
declare(strict_types=1);
namespace Tests\git2;
use git2\git;
use git2\git_delta_t;
use git2\git_diff;
use git2\git_diff_delta;
use git2\git_diff_find_options;
use git2\git_diff_flag_t;
use git2\git_diff_format_t;
use git2\git_diff_hunk;
use git2\git_diff_line;
use git2\git_diff_line_t;
use git2\git_diff_options;
use git2\git_diff_patchid_options;
use git2\git_diff_stats_format_t;
use git2\git_filemode_t;
use git2\git_oid;
use git2\git_submodule_ignore_t;
final class DiffTest extends GitTestCase
{
public function testBlobToBuffer(): void
{
$dir = $this->mkdir('blob_to_buffer');
git::repository_init($repo, $dir, false);
git::blob_create_from_buffer($id, $repo, 'foo');
$this->assertOK(git::blob_lookup($blob, $repo, $id));
$lines = [];
$this->assertOK(git::diff_blob_to_buffer(
$blob,
'foo.old',
'bar',
'foo.new',
null,
function (git_diff_delta $delta, float $progress): int {
$this->assertSame(1., $progress);
$this->assertSame(git_delta_t::MODIFIED, $delta->status);
$this->assertSame(git_diff_flag_t::NOT_BINARY, $delta->flags);
$this->assertSame(0, $delta->similarity);
$this->assertSame(0, $delta->nfiles);
$this->assertSame('19102815663d23f8b75a47e7a01965dcdc96468c', git::oid_tostr_s($delta->old_file->id));
$this->assertSame('foo.old', $delta->old_file->path);
$this->assertSame(3, $delta->old_file->size);
$this->assertSame(git_diff_flag_t::NOT_BINARY | git_diff_flag_t::VALID_ID, $delta->old_file->flags);
$this->assertSame(git_filemode_t::BLOB, $delta->old_file->mode);
$this->assertSame(40, $delta->old_file->id_abbrev);
$this->assertSame('ba0e162e1c47469e3fe4b393a8bf8c569f302116', git::oid_tostr_s($delta->new_file->id));
$this->assertSame('foo.new', $delta->new_file->path);
$this->assertSame(3, $delta->new_file->size);
$this->assertSame(git_diff_flag_t::NOT_BINARY | git_diff_flag_t::VALID_ID, $delta->new_file->flags);
$this->assertSame(git_filemode_t::BLOB, $delta->new_file->mode);
$this->assertSame(40, $delta->new_file->id_abbrev);
return 0;
},
null,
null,
function (git_diff_delta $delta, git_diff_hunk $hunk, git_diff_line $line) use(&$lines): int {
$lines[] = [
$line->num_lines,
$line->new_lineno,
$line->old_lineno,
$line->content_len,
$line->content_offset,
$line->origin,
$line->content,
];
return 0;
},
));
$this->assertSame([
[0, -1, 1, 3, 0, git_diff_line_t::DELETION, 'foo'],
[2, -1, 1, 29, -1, git_diff_line_t::ADD_EOFNL, "\n\\ No newline at end of file\n"],
[0, 1, -1, 3, 0, git_diff_line_t::ADDITION, 'bar'],
[2, 1, -1, 29, -1, git_diff_line_t::DEL_EOFNL, "\n\\ No newline at end of file\n"],
], $lines);
}
public function testBlobs(): void
{
$dir = $this->mkdir('blob_to_buffer');
git::repository_init($repo, $dir, false);
git::blob_create_from_buffer($oid, $repo, 'old');
$this->assertOK(git::blob_lookup($oldBlob, $repo, $oid));
git::blob_create_from_buffer($nid, $repo, 'new');
$this->assertOK(git::blob_lookup($newBlob, $repo, $nid));
$this->assertSame(1, git::diff_blobs(
$oldBlob,
null,
$newBlob,
null,
null,
function (git_diff_delta $delta, float $progress): int {
$this->assertSame(1., $progress);
$this->assertSame(git_delta_t::MODIFIED, $delta->status);
$this->assertSame(git_diff_flag_t::NOT_BINARY, $delta->flags);
$this->assertSame(0, $delta->similarity);
$this->assertSame(0, $delta->nfiles);
$this->assertSame('489ce0f857e7634a0eb9f328265a3e91fad49f61', git::oid_tostr_s($delta->old_file->id));
$this->assertSame('file', $delta->old_file->path);
$this->assertSame(3, $delta->old_file->size);
$this->assertSame(git_diff_flag_t::NOT_BINARY | git_diff_flag_t::VALID_ID, $delta->old_file->flags);
$this->assertSame(git_filemode_t::BLOB, $delta->old_file->mode);
$this->assertSame(40, $delta->old_file->id_abbrev);
$this->assertSame('3e5126c4e761fd09582fc517918a1601b218dff0', git::oid_tostr_s($delta->new_file->id));
$this->assertSame('file', $delta->new_file->path);
$this->assertSame(3, $delta->new_file->size);
$this->assertSame(git_diff_flag_t::NOT_BINARY | git_diff_flag_t::VALID_ID, $delta->new_file->flags);
$this->assertSame(git_filemode_t::BLOB, $delta->new_file->mode);
$this->assertSame(40, $delta->new_file->id_abbrev);
return 1;
},
null,
null,
null,
));
}
public function testBuffers(): void
{
$this->assertSame(1, git::diff_buffers(
null,
null,
'foo',
null,
null,
function (git_diff_delta $delta, float $progress): int {
$this->assertSame(1., $progress);
$this->assertSame(git_delta_t::ADDED, $delta->status);
$this->assertSame(git_diff_flag_t::NOT_BINARY, $delta->flags);
$this->assertSame(0, $delta->similarity);
$this->assertSame(0, $delta->nfiles);
$this->assertSame('0000000000000000000000000000000000000000', git::oid_tostr_s($delta->old_file->id));
$this->assertSame('file', $delta->old_file->path);
$this->assertSame(0, $delta->old_file->size);
$this->assertSame(git_diff_flag_t::NOT_BINARY, $delta->old_file->flags);
$this->assertSame(git_filemode_t::UNREADABLE, $delta->old_file->mode);
$this->assertSame(0, $delta->old_file->id_abbrev);
$this->assertSame('19102815663d23f8b75a47e7a01965dcdc96468c', git::oid_tostr_s($delta->new_file->id));
$this->assertSame('file', $delta->new_file->path);
$this->assertSame(3, $delta->new_file->size);
$this->assertSame(git_diff_flag_t::NOT_BINARY | git_diff_flag_t::VALID_ID, $delta->new_file->flags);
$this->assertSame(git_filemode_t::BLOB, $delta->new_file->mode);
$this->assertSame(40, $delta->new_file->id_abbrev);
return 1;
},
null,
null,
null,
));
}
public function testFindOptionsInit(): void
{
$this->assertOK(git::diff_find_options_init($opts, git_diff_find_options::VERSION));
$this->assertInstanceOf(git_diff_find_options::class, $opts);
$this->assertSame(git_diff_find_options::VERSION, $opts->version);
$this->assertSame(0, $opts->flags);
$this->assertSame(0, $opts->rename_threshold);
$this->assertSame(0, $opts->rename_from_rewrite_threshold);
$this->assertSame(0, $opts->copy_threshold);
$this->assertSame(0, $opts->break_rewrite_threshold);
$this->assertSame(0, $opts->rename_limit);
}
public function testFromBuffer(): void
{
$this->assertOK(git::diff_from_buffer($diff, file_get_contents(__DIR__ . '/fixtures/diff.foo.patch')));
$this->assertInstanceOf(git_diff::class, $diff);
}
public function testFindSimilar(): void
{
$this->assertOK(git::diff_from_buffer($diff, file_get_contents(__DIR__ . '/fixtures/diff.foo.patch')));
$this->assertOK(git::diff_find_similar($diff));
}
public function testForeach(): void
{
$this->assertOK(git::diff_from_buffer($diff, file_get_contents(__DIR__ . '/fixtures/diff.foo.patch')));
$this->assertSame(1, git::diff_foreach(
$diff,
function (git_diff_delta $delta, float $progress): int {
$this->assertSame(0., $progress);
$this->assertSame(git_delta_t::ADDED, $delta->status);
$this->assertSame(git_diff_flag_t::NOT_BINARY, $delta->flags);
$this->assertSame(0, $delta->similarity);
$this->assertSame(1, $delta->nfiles);
$this->assertSame('0000000000000000000000000000000000000000', git::oid_tostr_s($delta->old_file->id));
$this->assertSame('foo', $delta->old_file->path);
$this->assertSame(0, $delta->old_file->size);
$this->assertSame(0, $delta->old_file->flags);
$this->assertSame(git_filemode_t::UNREADABLE, $delta->old_file->mode);
$this->assertSame(0, $delta->old_file->id_abbrev);
$this->assertSame('1910281000000000000000000000000000000000', git::oid_tostr_s($delta->new_file->id));
$this->assertSame('foo', $delta->new_file->path);
$this->assertSame(0, $delta->new_file->size);
$this->assertSame(0, $delta->new_file->flags);
$this->assertSame(git_filemode_t::BLOB, $delta->new_file->mode);
$this->assertSame(7, $delta->new_file->id_abbrev);
return 1;
},
null,
null,
null,
));
}
public function testGetDelta(): void
{
git::diff_from_buffer($diff, file_get_contents(__DIR__ . '/fixtures/diff.foo.patch'));
$this->assertNull(git::diff_get_delta($diff, 1));
$delta = git::diff_get_delta($diff, 0);
$this->assertSame('1910281000000000000000000000000000000000', git::oid_tostr_s($delta->new_file->id));
$this->assertSame('foo', $delta->new_file->path);
$this->assertSame(0, $delta->new_file->size);
$this->assertSame(0, $delta->new_file->flags);
$this->assertSame(git_filemode_t::BLOB, $delta->new_file->mode);
$this->assertSame(7, $delta->new_file->id_abbrev);
}
public function testGetStats(): void
{
git::diff_from_buffer($diff, file_get_contents(__DIR__ . '/fixtures/diff.foo.patch'));
$this->assertOK(git::diff_get_stats($stats, $diff));
$this->assertSame(1, git::diff_stats_files_changed($stats));
$this->assertSame(1, git::diff_stats_insertions($stats));
$this->assertSame(0, git::diff_stats_deletions($stats));
$this->assertOK(git::diff_stats_to_buf($buf, $stats, git_diff_stats_format_t::FULL, 80));
$this->assertSame(" foo | 1 +\n 1 file changed, 1 insertion(+)\n", (string) $buf);
}
public function testStatusChar(): void
{
$this->assertSame('A', git::diff_status_char(git_delta_t::ADDED));
}
public function testToBuf(): void
{
git::diff_from_buffer($diff, file_get_contents(__DIR__ . '/fixtures/diff.foo.patch'));
$this->assertOK(git::diff_to_buf($buf, $diff, git_diff_format_t::NAME_ONLY));
$this->assertSame("foo\n", (string) $buf);
}
public function testIndexToIndex(): void
{
$dir = $this->mkdir('index_to_index');
git::repository_init($repo, $dir, false);
git::index_new($oIndex);
git::index_new($nIndex);
$this->assertOK(git::diff_index_to_index($diff, $repo, $oIndex, $nIndex));
$this->assertInstanceOf(git_diff::class, $diff);
$this->assertOK(git::diff_get_stats($stats, $diff));
$this->assertSame(0, git::diff_stats_files_changed($stats));
}
public function testIndexToWorkdir(): void
{
$dir = $this->mkdir('index_to_workdir');
git::repository_init($repo, $dir, false);
file_put_contents($dir . '/foo', 'foo');
$this->assertOK(git::repository_index($index, $repo));
$this->assertOK(git::index_add_bypath($index, 'foo'));
unlink($dir . '/foo');
$this->assertOK(git::diff_index_to_workdir($diff, $repo, $index));
$this->assertInstanceOf(git_diff::class, $diff);
$this->assertOK(git::diff_get_stats($stats, $diff));
$this->assertSame(1, git::diff_stats_files_changed($stats));
$this->assertSame(stripos(PHP_OS, 'win') === 0, git::diff_is_sorted_icase($diff));
$this->assertSame(1, git::diff_num_deltas($diff));
$this->assertSame(1, git::diff_num_deltas_of_type($diff, git_delta_t::DELETED));
}
public function testPrint(): void
{
git::diff_from_buffer($diff, file_get_contents(__DIR__ . '/fixtures/diff.foo.patch'));
$this->assertSame(1, git::diff_print(
$diff,
git_diff_format_t::NAME_ONLY,
function(git_diff_delta $delta, ?git_diff_hunk $hunk, git_diff_line $line): int {
$this->assertSame(1, $delta->nfiles);
$this->assertNull($hunk);
$this->assertSame("foo\n", $line->content);
return 1;
},
));
}
public function testMerge(): void
{
$dir = $this->mkdir('merge');
git::repository_init($repo, $dir, false);
git::repository_index($index, $repo);
git::index_write_tree($treeId, $index);
git::tree_lookup($tree, $repo, $treeId);
git::signature_now($aut, 'John Doe', 'john.doe@example.com');
git::signature_now($com, 'Jane Doe', 'jane.doe@example.com');
git::commit_create_v($id, $repo, 'HEAD', $aut, $com, null, 'Hello world!', $tree);
git::diff_tree_to_index($diff, $repo, $tree, null);
git::diff_index_to_workdir($diff2, $repo, null);
$this->assertOK(git::diff_merge($diff, $diff2));
$this->assertOK(git::diff_get_stats($stats, $diff));
$this->assertSame(0, git::diff_stats_files_changed($stats));
}
public function testOptions(): void
{
$this->assertOK(git::diff_options_init($opts, git_diff_options::VERSION));
$this->assertInstanceOf(git_diff_options::class, $opts);
$this->assertSame(git_diff_options::VERSION, $opts->version);
$this->assertSame(0, $opts->flags);
$this->assertSame(3, $opts->context_lines);
$this->assertSame(0, $opts->interhunk_lines);
$this->assertSame(0, $opts->id_abbrev);
$this->assertSame(0, $opts->max_size);
$this->assertSame(git_submodule_ignore_t::UNSPECIFIED, $opts->ignore_submodules);
$this->assertNull($opts->old_prefix);
$this->assertNull($opts->new_prefix);
$this->assertSame(0, $opts->pathspec->count);
}
public function testPatchid(): void
{
$this->assertOK(git::diff_patchid_options_init($opts, git_diff_patchid_options::VERSION));
$this->assertInstanceOf(git_diff_patchid_options::class, $opts);
git::diff_from_buffer($diff, file_get_contents(__DIR__ . '/fixtures/diff.foo.patch'));
$this->assertOK(git::diff_patchid($id, $diff, $opts));
$this->assertInstanceOf(git_oid::class, $id);
$this->assertSame('079ff8583aea85d1178a48d5b6b6dd863a7e98c1', git::oid_tostr_s($id));
}
public function testTreeToIndex(): void
{
$dir = $this->mkdir('tree_to_index');
git::repository_init($repo, $dir, false);
git::repository_index($index, $repo);
git::index_write_tree($id, $index);
git::tree_lookup($tree, $repo, $id);
$this->assertOK(git::diff_tree_to_index($diff, $repo, $tree, null));
$this->assertInstanceOf(git_diff::class, $diff);
git::diff_get_stats($stats, $diff);
$this->assertSame(0, git::diff_stats_files_changed($stats));
}
public function testTreeToTree(): void
{
$dir = $this->mkdir('tree_to_tree');
git::repository_init($repo, $dir, false);
$this->assertOK(git::diff_tree_to_tree($diff, $repo, null, null));
$this->assertInstanceOf(git_diff::class, $diff);
git::diff_get_stats($stats, $diff);
$this->assertSame(0, git::diff_stats_files_changed($stats));
}
public function testTreeToWorkdir(): void
{
$dir = $this->mkdir('tree_to_workdir');
git::repository_init($repo, $dir, false);
$this->assertOK(git::diff_tree_to_workdir($diff, $repo, null));
$this->assertInstanceOf(git_diff::class, $diff);
git::diff_get_stats($stats, $diff);
$this->assertSame(0, git::diff_stats_files_changed($stats));
}
public function testTreeToWorkdirWithIndex(): void
{
$dir = $this->mkdir('tree_to_workdir_with_index');
git::repository_init($repo, $dir, false);
$this->assertOK(git::diff_tree_to_workdir_with_index($diff, $repo, null));
$this->assertInstanceOf(git_diff::class, $diff);
git::diff_get_stats($stats, $diff);
$this->assertSame(0, git::diff_stats_files_changed($stats));
}
}