Completed
Push — master ( eaaa40...44d567 )
by Alexander
06:13
created

CommitMessageBuilder::getFreshMergedRevisions()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 33
ccs 25
cts 25
cp 1
rs 8.439
cc 5
eloc 18
nc 5
nop 1
crap 5
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
	 * Creates commit message builder instance.
44
	 *
45
	 * @param Connector          $repository_connector Repository connector.
46
	 * @param RevisionListParser $revision_list_parser Revision list parser.
47
	 * @param RevisionLogFactory $revision_log_factory Revision log factory.
48
	 */
49 6
	public function __construct(
50
		Connector $repository_connector,
51
		RevisionListParser $revision_list_parser,
52
		RevisionLogFactory $revision_log_factory
53
	) {
54 6
		$this->repositoryConnector = $repository_connector;
55 6
		$this->revisionListParser = $revision_list_parser;
56 6
		$this->revisionLogFactory = $revision_log_factory;
57 6
	}
58
59
	/**
60
	 * Builds a commit message.
61
	 *
62
	 * @param string      $wc_path          Working copy path.
63
	 * @param string|null $changelist       Changelist.
64
	 * @param array       $recent_conflicts Recent conflicts.
65
	 *
66
	 * @return string
67
	 */
68 5
	public function build($wc_path, $changelist = null, array $recent_conflicts = array())
69
	{
70
		/*
71
		 * 3. if it's In-Portal project, then:
72
		 * - create commit message that:
73
		 * -- Merge of "{from_path}@{from_rev}" to "{to_path}@{to_rev}".
74
		 * -- Merge of "in-portal/branches/5.2.x@16189" to "in-portal/branches/5.3.x@16188".
75
		 * - {from_path} to be determined from list of merged revisions
76
		 * - {from_rev} - last changed of {from_path} by looking in repo
77
		 * - {to_path} to be determined from working copy
78
		 * - {to_rev} - last changed of {to_path} by looking in repo
79
		 * 4. open interactive editor with auto-generated message
80
		 */
81
82 5
		$commit_message_parts = array();
83
84 5
		if ( strlen($changelist) ) {
85 2
			$commit_message_parts[] = trim($changelist);
86 2
		}
87
88 5
		$commit_message_parts[] = $this->getFragmentForMergedRevisions($wc_path);
89 5
		$commit_message_parts[] = $this->getFragmentForRecentConflicts($recent_conflicts);
90
91 5
		return implode(PHP_EOL, array_filter($commit_message_parts));
92
	}
93
94
	/**
95
	 * Returns commit message fragment for merged revisions.
96
	 *
97
	 * @param string $wc_path Working copy path.
98
	 *
99
	 * @return string
100
	 */
101 5
	protected function getFragmentForMergedRevisions($wc_path)
102
	{
103 5
		$merged_revisions = $this->getFreshMergedRevisions($wc_path);
104
105 5
		if ( !$merged_revisions ) {
106 3
			return '';
107
		}
108
109 2
		$ret = '';
110 2
		$wc_url = $this->repositoryConnector->getWorkingCopyUrl($wc_path);
111 2
		$repository_url = $this->repositoryConnector->getRootUrl($wc_url);
112
113 2
		foreach ( $merged_revisions as $path => $revisions ) {
114 2
			$merged_messages = array();
115 2
			$revision_log = $this->revisionLogFactory->getRevisionLog($repository_url . $path);
116 2
			$ret .= PHP_EOL . $this->getCommitMessageHeading($wc_url, $path) . PHP_EOL;
117
118 2
			$revisions_data = $revision_log->getRevisionsData('summary', $revisions);
119
120 2
			foreach ( $revisions as $revision ) {
121 2
				$merged_messages[] = ' * r' . $revision . ': ' . $revisions_data[$revision]['msg'];
122 2
			}
123
124 2
			$merged_messages = array_unique(array_map('trim', $merged_messages));
125 2
			$ret .= implode(PHP_EOL, $merged_messages) . PHP_EOL;
126 2
		}
127
128 2
		return trim($ret);
129
	}
130
131
	/**
132
	 * Returns list of just merged revisions.
133
	 *
134
	 * @param string $wc_path Merge target: working copy path.
135
	 *
136
	 * @return array
137
	 */
138 5
	protected function getFreshMergedRevisions($wc_path)
139
	{
140 5
		$final_paths = array();
141 5
		$old_paths = $this->getMergedRevisions($wc_path, 'BASE');
142 5
		$new_paths = $this->getMergedRevisions($wc_path);
143
144 5
		if ( $old_paths === $new_paths ) {
145 3
			return array();
146
		}
147
148 2
		foreach ( $new_paths as $new_path => $new_merged_revisions ) {
149 2
			if ( !isset($old_paths[$new_path]) ) {
150
				// Merge from new path.
151 2
				$final_paths[$new_path] = $this->revisionListParser->expandRanges(
152 2
					explode(',', $new_merged_revisions)
153 2
				);
154 2
			}
155 2
			elseif ( $new_merged_revisions != $old_paths[$new_path] ) {
156
				// Merge on existing path.
157 2
				$new_merged_revisions_parsed = $this->revisionListParser->expandRanges(
158 2
					explode(',', $new_merged_revisions)
159 2
				);
160 2
				$old_merged_revisions_parsed = $this->revisionListParser->expandRanges(
161 2
					explode(',', $old_paths[$new_path])
162 2
				);
163 2
				$final_paths[$new_path] = array_values(
164 2
					array_diff($new_merged_revisions_parsed, $old_merged_revisions_parsed)
165 2
				);
166 2
			}
167 2
		}
168
169 2
		return $final_paths;
170
	}
171
172
	/**
173
	 * Returns list of merged revisions per path.
174
	 *
175
	 * @param string  $wc_path  Merge target: working copy path.
176
	 * @param integer $revision Revision.
177
	 *
178
	 * @return array
179
	 */
180 5
	protected function getMergedRevisions($wc_path, $revision = null)
181
	{
182 5
		$paths = array();
183
184 5
		$merge_info = $this->repositoryConnector->getProperty('svn:mergeinfo', $wc_path, $revision);
185 5
		$merge_info = array_filter(explode("\n", $merge_info));
186
187 5
		foreach ( $merge_info as $merge_info_line ) {
188 5
			list($path, $revisions) = explode(':', $merge_info_line, 2);
189 5
			$paths[$path] = $revisions;
190 5
		}
191
192 5
		return $paths;
193
	}
194
195
	/**
196
	 * Builds commit message heading.
197
	 *
198
	 * @param string $wc_url Working copy url.
199
	 * @param string $path   Source path for merge operation.
200
	 *
201
	 * @return string
202
	 */
203 2
	protected function getCommitMessageHeading($wc_url, $path)
204
	{
205 2
		return 'Merging from ' . ucfirst(basename($path)) . ' to ' . ucfirst(basename($wc_url));
206
	}
207
208
	/**
209
	 * Returns commit message fragment for recent conflicts.
210
	 *
211
	 * @param array $recent_conflicts Recent conflicts.
212
	 *
213
	 * @return string
214
	 */
215 5
	protected function getFragmentForRecentConflicts(array $recent_conflicts)
216
	{
217 5
		if ( !$recent_conflicts ) {
218 3
			return '';
219
		}
220
221
		// Ensure empty line before.
222 2
		$ret = PHP_EOL . 'Conflicts:';
223
224 2
		foreach ( $recent_conflicts as $conflict_path ) {
225 2
			$ret .= PHP_EOL . ' * ' . $conflict_path;
226 2
		}
227
228 2
		return $ret;
229
	}
230
231
}
232