Completed
Push — master ( f2828f...7fcd15 )
by Alexander
02:39
created

CommitCommand::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 0
cts 18
cp 0
rs 9.0856
c 0
b 0
f 0
cc 1
eloc 17
nc 1
nop 0
crap 2
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\Command;
12
13
14
use ConsoleHelpers\ConsoleKit\Exception\CommandException;
15
use ConsoleHelpers\SVNBuddy\Config\AbstractConfigSetting;
16
use ConsoleHelpers\SVNBuddy\Config\ChoiceConfigSetting;
17
use ConsoleHelpers\SVNBuddy\InteractiveEditor;
18
use ConsoleHelpers\SVNBuddy\Repository\CommitMessage\CommitMessageBuilder;
19
use ConsoleHelpers\SVNBuddy\Repository\CommitMessage\MergeTemplateFactory;
20
use ConsoleHelpers\SVNBuddy\Repository\WorkingCopyConflictTracker;
21
use Symfony\Component\Console\Input\InputArgument;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Input\InputOption;
24
use Symfony\Component\Console\Output\OutputInterface;
25
26
class CommitCommand extends AbstractCommand implements IConfigAwareCommand
27
{
28
29
	const SETTING_COMMIT_MERGE_TEMPLATE = 'commit.merge-template';
30
31
	const STOP_LINE = '--This line, and those below, will be ignored--';
32
33
	/**
34
	 * Editor.
35
	 *
36
	 * @var InteractiveEditor
37
	 */
38
	private $_editor;
39
40
	/**
41
	 * Commit message builder.
42
	 *
43
	 * @var CommitMessageBuilder
44
	 */
45
	private $_commitMessageBuilder;
46
47
	/**
48
	 * Merge template factory.
49
	 *
50
	 * @var MergeTemplateFactory
51
	 */
52
	private $_mergeTemplateFactory;
53
54
	/**
55
	 * Working copy conflict tracker.
56
	 *
57
	 * @var WorkingCopyConflictTracker
58
	 */
59
	private $_workingCopyConflictTracker;
60
61
	/**
62
	 * {@inheritdoc}
63
	 */
64
	protected function configure()
65
	{
66
		$this
67
			->setName('commit')
68
			->setDescription(
69
				'Send changes from your working copy to the repository'
70
			)
71
			->setAliases(array('ci'))
72
			->addArgument(
73
				'path',
74
				InputArgument::OPTIONAL,
75
				'Working copy path',
76
				'.'
77
			)
78
			->addOption(
79
				'cl',
80
				null,
81
				InputOption::VALUE_NONE,
82
				'Operate only on members of selected changelist'
83
			);
84
85
		parent::configure();
86
	}
87
88
	/**
89
	 * Prepare dependencies.
90
	 *
91
	 * @return void
92
	 */
93
	protected function prepareDependencies()
94
	{
95
		parent::prepareDependencies();
96
97
		$container = $this->getContainer();
98
99
		$this->_editor = $container['editor'];
100
		$this->_commitMessageBuilder = $container['commit_message_builder'];
101
		$this->_mergeTemplateFactory = $container['merge_template_factory'];
102
		$this->_workingCopyConflictTracker = $container['working_copy_conflict_tracker'];
103
	}
104
105
	/**
106
	 * {@inheritdoc}
107
	 *
108
	 * @throws CommandException When conflicts are detected.
109
	 * @throws CommandException Working copy has no changes.
110
	 * @throws CommandException User decides not to perform a commit.
111
	 */
112
	protected function execute(InputInterface $input, OutputInterface $output)
113
	{
114
		$wc_path = $this->getWorkingCopyPath();
115
		$conflicts = $this->_workingCopyConflictTracker->getNewConflicts($wc_path);
116
117
		if ( $conflicts ) {
118
			throw new CommandException('Conflicts detected. Please resolve them before committing.');
119
		}
120
121
		$changelist = $this->getChangelist($wc_path);
122
		$compact_working_copy_status = $this->repositoryConnector->getCompactWorkingCopyStatus($wc_path, $changelist);
123
124
		if ( !$compact_working_copy_status ) {
125
			throw new CommandException('Nothing to commit.');
126
		}
127
128
		$merge_template = $this->_mergeTemplateFactory->get($this->getSetting(self::SETTING_COMMIT_MERGE_TEMPLATE));
129
		$commit_message = $this->_commitMessageBuilder->build($wc_path, $merge_template, $changelist);
130
		$commit_message .= PHP_EOL . PHP_EOL . self::STOP_LINE . PHP_EOL . PHP_EOL . $compact_working_copy_status;
131
132
		$edited_commit_message = $this->_editor
133
			->setDocumentName('commit_message')
134
			->setContent($commit_message)
135
			->launch();
136
137
		$stop_line_pos = strpos($edited_commit_message, self::STOP_LINE);
138
139
		if ( $stop_line_pos !== false ) {
140
			$edited_commit_message = trim(substr($edited_commit_message, 0, $stop_line_pos));
141
		}
142
143
		$this->io->writeln(array('<fg=white;options=bold>Commit message:</>', $edited_commit_message, ''));
144
145
		if ( !$this->io->askConfirmation('Run "svn commit"', false) ) {
146
			throw new CommandException('Commit aborted by user.');
147
		}
148
149
		$tmp_file = tempnam(sys_get_temp_dir(), 'commit_message_');
150
		file_put_contents($tmp_file, $edited_commit_message);
151
152
		$arguments = array(
153
			'-F {' . $tmp_file . '}',
154
		);
155
156
		if ( strlen($changelist) ) {
157
			$arguments[] = '--depth empty';
158
159
			// Relative path used to make command line shorter.
160
			foreach ( array_keys($this->repositoryConnector->getWorkingCopyStatus($wc_path, $changelist)) as $path ) {
161
				$arguments[] = '{' . $path . '}';
162
			}
163
		}
164
		else {
165
			$arguments[] = '{' . $wc_path . '}';
166
		}
167
168
		$this->repositoryConnector->getCommand('commit', implode(' ', $arguments))->runLive();
169
		$this->_workingCopyConflictTracker->erase($wc_path);
170
		unlink($tmp_file);
171
172
		$this->io->writeln('<info>Done</info>');
173
	}
174
175
	/**
176
	 * Returns user selected changelist.
177
	 *
178
	 * @param string $wc_path Working copy path.
179
	 *
180
	 * @return string|null
181
	 * @throws CommandException When no changelists found.
182
	 */
183
	protected function getChangelist($wc_path)
184
	{
185
		if ( !$this->io->getOption('cl') ) {
186
			return null;
187
		}
188
189
		$changelists = $this->repositoryConnector->getWorkingCopyChangelists($wc_path);
190
191
		if ( !$changelists ) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $changelists 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...
192
			throw new CommandException('No changelists detected.');
193
		}
194
195
		return $this->io->choose(
196
			'Pick changelist by number [0]:',
197
			$changelists,
198
			0,
199
			'Changelist "%s" is invalid.'
200
		);
201
	}
202
203
	/**
204
	 * Returns list of config settings.
205
	 *
206
	 * @return AbstractConfigSetting[]
207
	 */
208
	public function getConfigSettings()
209
	{
210
		$container = $this->getContainer();
211
		$merge_template_names = array();
212
213
		foreach ( $container['merge_template_factory']->getNames() as $merge_template_name ) {
214
			$merge_template_names[$merge_template_name] = str_replace('_', ' ', ucfirst($merge_template_name));
215
		}
216
217
		return array(
218
			new ChoiceConfigSetting(
219
				self::SETTING_COMMIT_MERGE_TEMPLATE,
220
				$merge_template_names,
221
				reset($merge_template_names)
222
			),
223
		);
224
	}
225
226
}
227