Completed
Push — master ( ef4f1c...e36323 )
by Alexander
19:43
created

CommitMessageBuilder::build()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 46
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 46
ccs 0
cts 23
cp 0
rs 8.4751
cc 5
eloc 19
nc 4
nop 3
crap 30
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, array $recent_conflicts)
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 = '';
83
84
		if ( strlen($changelist) ) {
85
			$commit_message .= $changelist . PHP_EOL;
86
		}
87
88
		$merged_revisions = $this->getFreshMergedRevisions($wc_path);
89
90
		if ( $merged_revisions ) {
91
			$wc_url = $this->repositoryConnector->getWorkingCopyUrl($wc_path);
92
			$repository_url = $this->repositoryConnector->getRootUrl($wc_url);
93
94
			foreach ( $merged_revisions as $path => $revisions ) {
95
				$merged_messages = array();
96
				$revision_log = $this->revisionLogFactory->getRevisionLog($repository_url . $path);
97
				$commit_message .= $this->getCommitMessageHeading($wc_url, $path) . PHP_EOL;
98
99
				$revisions_data = $revision_log->getRevisionsData('summary', $revisions);
100
101
				foreach ( $revisions as $revision ) {
102
					$merged_messages[] = ' * r' . $revision . ': ' . $revisions_data[$revision]['msg'];
103
				}
104
105
				$merged_messages = array_unique(array_map('trim', $merged_messages));
106
				$commit_message .= implode(PHP_EOL, $merged_messages) . PHP_EOL;
107
			}
108
		}
109
110
		$commit_message .= $this->getCommitMessageConflicts($recent_conflicts);
111
112
		return rtrim($commit_message);
113
	}
114
115
	/**
116
	 * Returns list of just merged revisions.
117
	 *
118
	 * @param string $wc_path Merge target: working copy path.
119
	 *
120
	 * @return array
121
	 */
122
	protected function getFreshMergedRevisions($wc_path)
123
	{
124
		$final_paths = array();
125
		$old_paths = $this->getMergedRevisions($wc_path, 'BASE');
126
		$new_paths = $this->getMergedRevisions($wc_path);
127
128
		if ( $old_paths === $new_paths ) {
129
			return array();
130
		}
131
132
		foreach ( $new_paths as $new_path => $new_merged_revisions ) {
133
			if ( !isset($old_paths[$new_path]) ) {
134
				// Merge from new path.
135
				$final_paths[$new_path] = $this->revisionListParser->expandRanges(
136
					explode(',', $new_merged_revisions)
137
				);
138
			}
139
			elseif ( $new_merged_revisions != $old_paths[$new_path] ) {
140
				// Merge on existing path.
141
				$new_merged_revisions_parsed = $this->revisionListParser->expandRanges(
142
					explode(',', $new_merged_revisions)
143
				);
144
				$old_merged_revisions_parsed = $this->revisionListParser->expandRanges(
145
					explode(',', $old_paths[$new_path])
146
				);
147
				$final_paths[$new_path] = array_values(
148
					array_diff($new_merged_revisions_parsed, $old_merged_revisions_parsed)
149
				);
150
			}
151
		}
152
153
		return $final_paths;
154
	}
155
156
	/**
157
	 * Returns list of merged revisions per path.
158
	 *
159
	 * @param string  $wc_path  Merge target: working copy path.
160
	 * @param integer $revision Revision.
161
	 *
162
	 * @return array
163
	 */
164
	protected function getMergedRevisions($wc_path, $revision = null)
165
	{
166
		$paths = array();
167
168
		$merge_info = $this->repositoryConnector->getProperty('svn:mergeinfo', $wc_path, $revision);
169
		$merge_info = array_filter(explode("\n", $merge_info));
170
171
		foreach ( $merge_info as $merge_info_line ) {
172
			list($path, $revisions) = explode(':', $merge_info_line, 2);
173
			$paths[$path] = $revisions;
174
		}
175
176
		return $paths;
177
	}
178
179
	/**
180
	 * Builds commit message heading.
181
	 *
182
	 * @param string $wc_url Working copy url.
183
	 * @param string $path   Source path for merge operation.
184
	 *
185
	 * @return string
186
	 */
187
	protected function getCommitMessageHeading($wc_url, $path)
188
	{
189
		return 'Merging from ' . ucfirst(basename($path)) . ' to ' . ucfirst(basename($wc_url));
190
	}
191
192
	/**
193
	 * Returns recent merge conflicts.
194
	 *
195
	 * @param array $recent_conflicts Recent conflicts.
196
	 *
197
	 * @return string
198
	 */
199
	protected function getCommitMessageConflicts(array $recent_conflicts)
200
	{
201
		if ( !$recent_conflicts ) {
202
			return '';
203
		}
204
205
		$ret = PHP_EOL . 'Conflicts:' . PHP_EOL;
206
207
		foreach ( $recent_conflicts as $conflict_path ) {
208
			$ret .= ' * ' . $conflict_path . PHP_EOL;
209
		}
210
211
		return $ret;
212
	}
213
214
}
215