Failed Conditions
Push — master ( acad7e...bfa4e7 )
by Alexander
02:33
created

CommitMessageBuilder::getCommitMessageHeading()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 1
1
<?php
2
/**
3
 * This file is part of the SVN-Buddy library.
4
 * For the full copyright and license information, please view
5
 * the LICENSE file that was distributed with this source code.
6
 *
7
 * @copyright Alexander Obuhovich <[email protected]>
8
 * @link      https://github.com/console-helpers/svn-buddy
9
 */
10
11
namespace ConsoleHelpers\SVNBuddy\Repository;
12
13
14
use ConsoleHelpers\SVNBuddy\Repository\Connector\Connector;
15
use ConsoleHelpers\SVNBuddy\Repository\Parser\RevisionListParser;
16
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\RevisionLogFactory;
17
18
class CommitMessageBuilder
19
{
20
21
	/**
22
	 * Repository connector.
23
	 *
24
	 * @var Connector
25
	 */
26
	protected $repositoryConnector;
27
28
	/**
29
	 * Revision list parser.
30
	 *
31
	 * @var RevisionListParser
32
	 */
33
	protected $revisionListParser;
34
35
	/**
36
	 * Revision log factory.
37
	 *
38
	 * @var RevisionLogFactory
39
	 */
40
	protected $revisionLogFactory;
41
42
	/**
43
	 * Working copy conflict tracker.
44
	 *
45
	 * @var WorkingCopyConflictTracker
46
	 */
47
	protected $workingCopyConflictTracker;
48
49
	/**
50
	 * Creates commit message builder instance.
51
	 *
52
	 * @param Connector                  $repository_connector          Repository connector.
53
	 * @param RevisionListParser         $revision_list_parser          Revision list parser.
54
	 * @param RevisionLogFactory         $revision_log_factory          Revision log factory.
55
	 * @param WorkingCopyConflictTracker $working_copy_conflict_tracker Working copy conflict tracker.
56
	 */
57 6
	public function __construct(
58
		Connector $repository_connector,
59
		RevisionListParser $revision_list_parser,
60
		RevisionLogFactory $revision_log_factory,
61
		WorkingCopyConflictTracker $working_copy_conflict_tracker
62
	) {
63 6
		$this->repositoryConnector = $repository_connector;
64 6
		$this->revisionListParser = $revision_list_parser;
65 6
		$this->revisionLogFactory = $revision_log_factory;
66 6
		$this->workingCopyConflictTracker = $working_copy_conflict_tracker;
67 6
	}
68
69
	/**
70
	 * Builds a commit message.
71
	 *
72
	 * @param string      $wc_path    Working copy path.
73
	 * @param string|null $changelist Changelist.
74
	 *
75
	 * @return string
76
	 */
77 5
	public function build($wc_path, $changelist = null)
78
	{
79
		/*
80
		 * 3. if it's In-Portal project, then:
81
		 * - create commit message that:
82
		 * -- Merge of "{from_path}@{from_rev}" to "{to_path}@{to_rev}".
83
		 * -- Merge of "in-portal/branches/5.2.x@16189" to "in-portal/branches/5.3.x@16188".
84
		 * - {from_path} to be determined from list of merged revisions
85
		 * - {from_rev} - last changed of {from_path} by looking in repo
86
		 * - {to_path} to be determined from working copy
87
		 * - {to_rev} - last changed of {to_path} by looking in repo
88
		 * 4. open interactive editor with auto-generated message
89
		 */
90
91 5
		$commit_message_parts = array();
92
93 5
		if ( strlen($changelist) ) {
94 2
			$commit_message_parts[] = trim($changelist);
95 2
		}
96
97 5
		$commit_message_parts[] = $this->getFragmentForMergedRevisions($wc_path);
98 5
		$commit_message_parts[] = $this->getFragmentForRecentConflicts($wc_path);
99
100 5
		return implode(PHP_EOL, array_filter($commit_message_parts));
101
	}
102
103
	/**
104
	 * Returns commit message fragment for merged revisions.
105
	 *
106
	 * @param string $wc_path Working copy path.
107
	 *
108
	 * @return string
109
	 */
110 5
	protected function getFragmentForMergedRevisions($wc_path)
111
	{
112 5
		$merged_revisions = $this->getFreshMergedRevisions($wc_path);
113
114 5
		if ( !$merged_revisions ) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $merged_revisions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
115 3
			return '';
116
		}
117
118 2
		$ret = '';
119 2
		$wc_url = $this->repositoryConnector->getWorkingCopyUrl($wc_path);
120 2
		$repository_url = $this->repositoryConnector->getRootUrl($wc_url);
121
122 2
		foreach ( $merged_revisions as $path => $revisions ) {
123 2
			$merged_messages = array();
124 2
			$revision_log = $this->revisionLogFactory->getRevisionLog($repository_url . $path);
125 2
			$ret .= PHP_EOL . $this->getCommitMessageHeading($wc_url, $path) . PHP_EOL;
126
127 2
			$revisions_data = $revision_log->getRevisionsData('summary', $revisions);
128
129 2
			foreach ( $revisions as $revision ) {
130 2
				$merged_messages[] = ' * r' . $revision . ': ' . $revisions_data[$revision]['msg'];
131 2
			}
132
133 2
			$merged_messages = array_unique(array_map('trim', $merged_messages));
134 2
			$ret .= implode(PHP_EOL, $merged_messages) . PHP_EOL;
135 2
		}
136
137 2
		return trim($ret);
138
	}
139
140
	/**
141
	 * Returns list of just merged revisions.
142
	 *
143
	 * @param string $wc_path Merge target: working copy path.
144
	 *
145
	 * @return array
146
	 */
147 5
	protected function getFreshMergedRevisions($wc_path)
148
	{
149 5
		$final_paths = array();
150 5
		$old_paths = $this->getMergedRevisions($wc_path, 'BASE');
151 5
		$new_paths = $this->getMergedRevisions($wc_path);
152
153 5
		if ( $old_paths === $new_paths ) {
154 3
			return array();
155
		}
156
157 2
		foreach ( $new_paths as $new_path => $new_merged_revisions ) {
158 2
			if ( !isset($old_paths[$new_path]) ) {
159
				// Merge from new path.
160 2
				$final_paths[$new_path] = $this->revisionListParser->expandRanges(
161 2
					explode(',', $new_merged_revisions)
162 2
				);
163 2
			}
164 2
			elseif ( $new_merged_revisions != $old_paths[$new_path] ) {
165
				// Merge on existing path.
166 2
				$new_merged_revisions_parsed = $this->revisionListParser->expandRanges(
167 2
					explode(',', $new_merged_revisions)
168 2
				);
169 2
				$old_merged_revisions_parsed = $this->revisionListParser->expandRanges(
170 2
					explode(',', $old_paths[$new_path])
171 2
				);
172 2
				$final_paths[$new_path] = array_values(
173 2
					array_diff($new_merged_revisions_parsed, $old_merged_revisions_parsed)
174 2
				);
175 2
			}
176 2
		}
177
178 2
		return $final_paths;
179
	}
180
181
	/**
182
	 * Returns list of merged revisions per path.
183
	 *
184
	 * @param string  $wc_path  Merge target: working copy path.
185
	 * @param integer $revision Revision.
186
	 *
187
	 * @return array
188
	 */
189 5
	protected function getMergedRevisions($wc_path, $revision = null)
190
	{
191 5
		$paths = array();
192
193 5
		$merge_info = $this->repositoryConnector->getProperty('svn:mergeinfo', $wc_path, $revision);
194 5
		$merge_info = array_filter(explode("\n", $merge_info));
195
196 5
		foreach ( $merge_info as $merge_info_line ) {
197 5
			list($path, $revisions) = explode(':', $merge_info_line, 2);
198 5
			$paths[$path] = $revisions;
199 5
		}
200
201 5
		return $paths;
202
	}
203
204
	/**
205
	 * Builds commit message heading.
206
	 *
207
	 * @param string $wc_url Working copy url.
208
	 * @param string $path   Source path for merge operation.
209
	 *
210
	 * @return string
211
	 */
212 2
	protected function getCommitMessageHeading($wc_url, $path)
213
	{
214 2
		return 'Merging from ' . ucfirst(basename($path)) . ' to ' . ucfirst(basename($wc_url));
215
	}
216
217
	/**
218
	 * Returns commit message fragment for recent conflicts.
219
	 *
220
	 * @param string $wc_path Working copy path.
221
	 *
222
	 * @return string
223
	 */
224 5
	protected function getFragmentForRecentConflicts($wc_path)
225
	{
226 5
		$recorded_conflicts = $this->workingCopyConflictTracker->getRecordedConflicts($wc_path);
227
228 5
		if ( !$recorded_conflicts ) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $recorded_conflicts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
229 3
			return '';
230
		}
231
232
		// Ensure empty line before.
233 2
		$ret = PHP_EOL . 'Conflicts:';
234
235 2
		foreach ( $recorded_conflicts as $conflict_path ) {
236 2
			$ret .= PHP_EOL . ' * ' . $conflict_path;
237 2
		}
238
239 2
		return $ret;
240
	}
241
242
}
243