Completed
Push — master ( e36323...eaaa40 )
by Alexander
08:03
created

CommitMessageBuilder::getCommitMessageConflicts()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
ccs 0
cts 8
cp 0
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
crap 12

1 Method

Rating   Name   Duplication   Size   Complexity  
A CommitMessageBuilder::getCommitMessageHeading() 0 4 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
	 * 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 1
	public function __construct(
50
		Connector $repository_connector,
51
		RevisionListParser $revision_list_parser,
52
		RevisionLogFactory $revision_log_factory
53
	) {
54 1
		$this->repositoryConnector = $repository_connector;
55 1
		$this->revisionListParser = $revision_list_parser;
56 1
		$this->revisionLogFactory = $revision_log_factory;
57 1
	}
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
	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
		$commit_message_parts = array();
83
84
		if ( strlen($changelist) ) {
85
			$commit_message_parts[] = trim($changelist);
86
		}
87
88
		$commit_message_parts[] = $this->getFragmentForMergedRevisions($wc_path);
89
		$commit_message_parts[] = $this->getFragmentForRecentConflicts($recent_conflicts);
90
91
		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
	protected function getFragmentForMergedRevisions($wc_path)
102
	{
103
		$merged_revisions = $this->getFreshMergedRevisions($wc_path);
104
105
		if ( !$merged_revisions ) {
106
			return '';
107
		}
108
109
		$ret = '';
110
		$wc_url = $this->repositoryConnector->getWorkingCopyUrl($wc_path);
111
		$repository_url = $this->repositoryConnector->getRootUrl($wc_url);
112
113
		foreach ( $merged_revisions as $path => $revisions ) {
114
			$merged_messages = array();
115
			$revision_log = $this->revisionLogFactory->getRevisionLog($repository_url . $path);
116
			$ret .= PHP_EOL . $this->getCommitMessageHeading($wc_url, $path) . PHP_EOL;
117
118
			$revisions_data = $revision_log->getRevisionsData('summary', $revisions);
119
120
			foreach ( $revisions as $revision ) {
121
				$merged_messages[] = ' * r' . $revision . ': ' . $revisions_data[$revision]['msg'];
122
			}
123
124
			$merged_messages = array_unique(array_map('trim', $merged_messages));
125
			$ret .= implode(PHP_EOL, $merged_messages) . PHP_EOL;
126
		}
127
128
		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
	protected function getFreshMergedRevisions($wc_path)
139
	{
140
		$final_paths = array();
141
		$old_paths = $this->getMergedRevisions($wc_path, 'BASE');
142
		$new_paths = $this->getMergedRevisions($wc_path);
143
144
		if ( $old_paths === $new_paths ) {
145
			return array();
146
		}
147
148
		foreach ( $new_paths as $new_path => $new_merged_revisions ) {
149
			if ( !isset($old_paths[$new_path]) ) {
150
				// Merge from new path.
151
				$final_paths[$new_path] = $this->revisionListParser->expandRanges(
152
					explode(',', $new_merged_revisions)
153
				);
154
			}
155
			elseif ( $new_merged_revisions != $old_paths[$new_path] ) {
156
				// Merge on existing path.
157
				$new_merged_revisions_parsed = $this->revisionListParser->expandRanges(
158
					explode(',', $new_merged_revisions)
159
				);
160
				$old_merged_revisions_parsed = $this->revisionListParser->expandRanges(
161
					explode(',', $old_paths[$new_path])
162
				);
163
				$final_paths[$new_path] = array_values(
164
					array_diff($new_merged_revisions_parsed, $old_merged_revisions_parsed)
165
				);
166
			}
167
		}
168
169
		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
	protected function getMergedRevisions($wc_path, $revision = null)
181
	{
182
		$paths = array();
183
184
		$merge_info = $this->repositoryConnector->getProperty('svn:mergeinfo', $wc_path, $revision);
185
		$merge_info = array_filter(explode("\n", $merge_info));
186
187
		foreach ( $merge_info as $merge_info_line ) {
188
			list($path, $revisions) = explode(':', $merge_info_line, 2);
189
			$paths[$path] = $revisions;
190
		}
191
192
		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
	protected function getCommitMessageHeading($wc_url, $path)
204
	{
205
		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
	protected function getFragmentForRecentConflicts(array $recent_conflicts)
216
	{
217
		if ( !$recent_conflicts ) {
218
			return '';
219
		}
220
221
		// Ensure empty line before.
222
		$ret = PHP_EOL . 'Conflicts:';
223
224
		foreach ( $recent_conflicts as $conflict_path ) {
225
			$ret .= PHP_EOL . ' * ' . $conflict_path;
226
		}
227
228
		return $ret;
229
	}
230
231
}
232