Skip to content

Commit

Permalink
Fix #7, Fix #8
Browse files Browse the repository at this point in the history
- Renamed option `headSubject` to `headTagName` because the option
  involves a tag name instead of subject.

- Renamed option `nextTagDate` to `headTagDate`.

- Option ~~headSubject~~ headTagName can't be set to an existing tag
  name anymore. Having duplicate tag names is ambiguous.

- Reference to HEAD revision in Gitchangelog::gitTags changed from
  'HEAD' to ''. Any non empty value could result in duplicate tag names.
  • Loading branch information
DigiLive committed Nov 2, 2020
1 parent 5bbf5ef commit d4e352e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 36 deletions.
46 changes: 30 additions & 16 deletions src/GitChangelog.php
Expand Up @@ -82,7 +82,7 @@ class GitChangelog
*/
public $baseFile;
/**
* @var string Path to local git repository. Leave null for repository at current folder.
* @var string Path to local git repository. Set to null for repository at current folder.
*/
public $gitPath;
/**
Expand All @@ -99,8 +99,8 @@ class GitChangelog
*
* <pre>
* logHeader First string of the generated changelog.
* headSubject Subject of the HEAD revision (Implies unreleased commits).
* nextTagDate Date at head subject (Implies date of next release).
* headTagName Name of the HEAD revision (Implies unreleased commits).
* headTagDate Date of head subject (Implies date of next release).
* noChangesMessage Message to show when there are no commit subjects to list for a tag.
* addHashes True includes commit hashes to the listed subjects.
* includeMergeCommits True includes merge commits in the subject lists.
Expand All @@ -112,8 +112,8 @@ class GitChangelog
*/
protected $options = [
'logHeader' => 'Changelog',
'headSubject' => 'Upcoming changes',
'nextTagDate' => 'Undetermined',
'headTagName' => 'Upcoming changes',
'headTagDate' => 'Undetermined',
'noChangesMessage' => 'No changes.',
'addHashes' => true,
'includeMergeCommits' => false,
Expand All @@ -127,12 +127,14 @@ class GitChangelog
*/
private $fromTag;
/**
* @var string Value of the newest tag to include into the generated changelog.
* @var string Value of the newest tag to include into the generated changelog. If the value = an empty string, it
* refers to the HEAD revision.
* @see GitChangelog::setToTag()
*/
private $toTag = 'HEAD';
private $toTag = '';
/**
* @var array Contains the tags which exist in the git repository.
* @var array Contains the tags which exist in the git repository. If the first element's key is an empty string, it
* refers to the HEAD revision.
* @see GitChangelog::fetchTags();
*/
private $gitTags;
Expand Down Expand Up @@ -189,15 +191,20 @@ public function fetchTags($force = false): array
$gitPath .= $this->gitPath ?? './.git';

// Get all git tags.
// TODO: Change shell_exec to exec.
$this->gitTags = explode("\n", shell_exec("git $gitPath tag --sort=-{$this->options['tagOrderBy']}"));
array_pop($this->gitTags); // Remove empty trailing element.

// Add HEAD revision as tag.
if ($this->toTag == 'HEAD') {
array_unshift($this->gitTags, 'HEAD');
if ($this->toTag === '') {
array_unshift($this->gitTags, $this->toTag);
}

$toKey = $this->toTag == 'HEAD' ? 0 : Utilities::arraySearch($this->toTag, $this->gitTags);
$length = $this->fromTag === null ? null : Utilities::arraySearch($this->fromTag, $this->gitTags) - $toKey + 1;
$toKey = Utilities::arraySearch($this->toTag, $this->gitTags);
$length = null;
if ($this->fromTag !== null) {
$length = Utilities::arraySearch($this->fromTag, $this->gitTags) - $toKey + 1;
}

// Cache requested git tags. $this->gitTags = [newest..oldest].
$this->gitTags = array_slice($this->gitTags, $toKey, $length);
Expand Down Expand Up @@ -251,8 +258,9 @@ public function fetchCommitData($force = false): array
// Get tag dates and commit subjects from git log for each tag.
$includeMergeCommits = $this->options['includeMergeCommits'] ? '' : '--no-merges';
foreach ($gitTags as $tag) {
$tagRange = $tag == '' ? $previousTag : "$tag...$previousTag";
$tagRange = $tag == '' ? $previousTag : "$tag..$previousTag";

// TODO: Change shell_exec to exec.
$commitData[$previousTag]['date'] =
shell_exec("git $gitPath log -1 --pretty=format:%ad --date=short $previousTag");
$commitData[$previousTag]['subjects'] =
Expand Down Expand Up @@ -373,15 +381,15 @@ public function get(bool $prepend = false): string
/**
* Set the newest git tag to include in the changelog.
*
* Omit or set to 'HEAD' or null to include the HEAD revision into the changelog.
* Omit or set to '' or null to include the HEAD revision into the changelog.
*
* @param mixed $tag Newest tag to include.
*
* @throws InvalidArgumentException When the tag does not exits in the repository.
*/
public function setToTag($tag = null)
{
$tag = $tag ?? 'HEAD';
$tag = $tag ?? '';
Utilities::arraySearch($tag, $this->gitTags);
$this->toTag = $tag;
}
Expand Down Expand Up @@ -485,7 +493,9 @@ public function addLabel(...$labels)
* @param mixed $name Name of option or array of option names and values.
* @param mixed $value [Optional] Value of option.
*
* @throws Exception If option 'headTag' can't be validated.
* @throws InvalidArgumentException If the option you're trying to set is invalid.
* @throws InvalidArgumentException When setting option 'headTag' to an invalid value.
* @see GitChangelog::$options
*/
public function setOptions($name, $value = null)
Expand All @@ -496,7 +506,11 @@ public function setOptions($name, $value = null)

foreach ($name as $option => $value) {
if (!array_key_exists($option, $this->options)) {
throw new InvalidArgumentException('Attempt to set an invalid option!');
throw new InvalidArgumentException("Attempt to set an invalid option: $option!");
}

if ($option == 'headTagName' && in_array($value, $this->fetchTags())) {
throw new InvalidArgumentException("Attempt to set $option to an already existing tag value!");
}

$this->options[$option] = $value;
Expand Down
6 changes: 3 additions & 3 deletions src/Renderers/Html.php
Expand Up @@ -77,9 +77,9 @@ public function build(): void

foreach ($commitData as $tag => $data) {
// Add tag header and date.
if ($tag == 'HEAD') {
$tag = $this->options['headSubject'];
$data['date'] = $this->options['nextTagDate'];
if ($tag === '') {
$tag = $this->options['headTagName'];
$data['date'] = $this->options['headTagDate'];
}

$logContent .= "<h2>$tag ({$data['date']})</h2><ul>";
Expand Down
4 changes: 2 additions & 2 deletions src/Renderers/MarkDown.php
Expand Up @@ -93,8 +93,8 @@ public function build(): void
$logContent .= "\n";
// Add tag header and date.
$tagData = [$tag, $data['date']];
if ($tag == 'HEAD') {
$tagData = [$this->options['headSubject'], $this->options['nextTagDate']];
if ($tag === '') {
$tagData = [$this->options['headTagName'], $this->options['headTagDate']];
}

$logContent .= str_replace(['{tag}', '{date}'], $tagData, $this->formatTag) . "\n\n";
Expand Down
43 changes: 28 additions & 15 deletions tests/GitChangeLogTest.php
Expand Up @@ -72,16 +72,15 @@ public function testFetchTagsCached()
$changelog = new GitChangelog();

$tags = $changelog->fetchTags();
$this->assertEquals('HEAD', reset($tags));
$this->assertSame('', reset($tags));
}

public function testFetchTagsUncached()
{
$changelog = new GitChangelog();
$changelog->setFromTag('HEAD');
$changelog->setFromTag('');

$tags = $changelog->fetchTags(true);
$this->assertEquals('HEAD', reset($tags));
$this->assertEquals([''], $changelog->fetchTags(true));
}

public function testFetchTagsThrowsExceptionOnInvalidFromTag()
Expand Down Expand Up @@ -206,7 +205,7 @@ public function testFetchCommitData()
$commitData = $changeLog->fetchCommitData();
$firstElement = reset($commitData);

$this->assertEquals('HEAD', key($commitData));
$this->assertSame('', key($commitData));
$this->assertArrayHasKey('date', $firstElement);
$this->assertArrayHasKey('subjects', $firstElement);
$this->assertArrayHasKey('hashes', $firstElement);
Expand All @@ -220,8 +219,8 @@ public function testSetFromTag()
$changeLog = new GitChangelog();

// Test setting tag value.
$changeLog->setFromTag('HEAD');
$this->assertEquals('HEAD', $this->getPrivateProperty($changeLog, 'fromTag'));
$changeLog->setFromTag('');
$this->assertEquals('', $this->getPrivateProperty($changeLog, 'fromTag'));

// Test removing tag value.
$changeLog->setFromTag();
Expand All @@ -234,6 +233,8 @@ public function testSetFromTag()

public function testBuildAscendingCommitOrder()
{
// TODO: Move to renderer tests.
$this->markTestSkipped('GitChangelog does not build anymore. Create tests for added renderers.');
$changeLog = new GitChangelog();
$changeLog->setOptions('tagOrderDesc', false);
$testValues =
Expand Down Expand Up @@ -268,6 +269,8 @@ public function testBuildAscendingCommitOrder()

public function testBuildDescendingCommitOrder()
{
// TODO: Move to renderer tests.
$this->markTestSkipped('GitChangelog does not build anymore. Create tests for added renderers.');
$changeLog = new GitChangelog();
$changeLog->setOptions('commitOrder', 'DESC');
$testValues =
Expand Down Expand Up @@ -350,12 +353,12 @@ public function testSetToTag()
$changeLog = new GitChangelog();

// Test setting tag value.
$changeLog->setToTag('HEAD');
$this->assertEquals('HEAD', $this->getPrivateProperty($changeLog, 'toTag'));
$changeLog->setToTag('');
$this->assertEquals('', $this->getPrivateProperty($changeLog, 'toTag'));

// Test removing tag value.
$changeLog->setToTag();
$this->assertEquals('HEAD', $this->getPrivateProperty($changeLog, 'toTag'));
$this->assertSame('', $this->getPrivateProperty($changeLog, 'toTag'));

// Test exception.
$this->expectException('Exception');
Expand All @@ -365,7 +368,7 @@ public function testSetToTag()
public function testRemoveLabel()
{
$changeLog = new GitChangelog();

// FIXME: Set test labels because default labels might be empty.
$defaultLabels = $this->getPrivateProperty($changeLog, 'labels');

// Test with array parameter.
Expand All @@ -377,6 +380,7 @@ public function testRemoveLabel()
$changeLog->removeLabel('newLabel1', 'newLabel2');
array_shift($defaultLabels);
$this->assertEquals(['newLabel3'], $this->getPrivateProperty($changeLog, 'labels'));
// TODO: Add test for removing non existing label.
}

public function testSave()
Expand Down Expand Up @@ -437,23 +441,32 @@ public function testSetOptions()
// Set multiple options at once.
$changeLog->setOptions(
[
'logHeader' => 'Test1',
'headSubject' => 'Test2',
'logHeader' => 'Test1',
'headTagName' => 'Test2',
]
);
$this->assertEquals('Test1', $this->getPrivateProperty($changeLog, 'options')['logHeader']);
$this->assertEquals('Test2', $this->getPrivateProperty($changeLog, 'options')['headSubject']);
$this->assertEquals('Test2', $this->getPrivateProperty($changeLog, 'options')['headTagName']);

// Set single option.
$changeLog->setOptions('logHeader', 'Test');
$this->assertEquals('Test', $this->getPrivateProperty($changeLog, 'options')['logHeader']);
}

public function testSetOptionsThrowsException()
public function testSetOptionsThrowsExceptionOnInvalidOption()
{
$changeLog = new GitChangelog();

$this->expectException('InvalidArgumentException');
$changeLog->setOptions('NotExistingOption', 'Test');
}

public function testSetOptionsThrowsExceptionOnInvalidHeadTagNameValue()
{
$changeLog = new GitChangelog();
$this->setPrivateProperty($changeLog, 'gitTags', ['Test']);

$this->expectException('InvalidArgumentException');
$changeLog->setOptions('headTagName', 'Test');
}
}

0 comments on commit d4e352e

Please sign in to comment.