1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-09-20 08:58:55 +02:00

Use $EDITOR to prompt users when creating a new commit out of dirty working copy changes

Summary:
Fixes T7344.
Currently, we use `phutil_console_prompt()`, which isn't a very good editor. Use the real $EDITOR instead.

100% of the logic here was also a gigantic mess. Clean it up.

Test Plan: Will update in a second with console output from this run.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7344

Differential Revision: https://secure.phabricator.com/D11843
This commit is contained in:
epriestley 2015-02-21 02:37:58 -08:00
parent 8f8fe44b05
commit dd59423d56
3 changed files with 138 additions and 89 deletions

View file

@ -40,14 +40,30 @@ final class ArcanistUSEnglishTranslation extends PhutilTranslation {
'Do you want to mark these files as binary and continue?', 'Do you want to mark these files as binary and continue?',
), ),
'Do you want to amend these %s file(s) to the commit?' => array( 'Do you want to amend these %s change(s) to the current commit?' => array(
'Do you want to amend this file to the commit?', 'Do you want to amend this change to the current commit?',
'Do you want to amend these files to the commit?', 'Do you want to amend these changes to the current commit?',
), ),
'Do you want to add these %s file(s) to the commit?' => array( 'Do you want to create a new commit with these %s change(s)?' => array(
'Do you want to add this file to the commit?', 'Do you want to create a new commit with this change?',
'Do you want to add these files to the commit?', 'Do you want to create a new commit with these changes?',
),
'(To ignore these %s change(s), add them to ".git/info/exclude".)' =>
array(
'(To ignore this change, add it to ".git/info/exclude".)',
'(To ignore these changes, add themto ".git/info/exclude".)',
),
'(To ignore these %s change(s), add them to "svn:ignore".)' => array(
'(To ignore this change, add it to "svn:ignore".)',
'(To ignore these changes, add them to "svn:ignore".)',
),
'(To ignore these %s change(s), add them to ".hgignore".)' => array(
'(To ignore this change, add it to ".hgignore".)',
'(To ignore these changes, add them to ".hgignore".)',
), ),
'%s line(s)' => array('line', 'lines'), '%s line(s)' => array('line', 'lines'),

View file

@ -1923,9 +1923,6 @@ EOTEXT
$messages = array(); $messages = array();
foreach ($local as $hash => $info) { foreach ($local as $hash => $info) {
$text = $info['message']; $text = $info['message'];
if (trim($text) == self::AUTO_COMMIT_TITLE) {
continue;
}
$obj = ArcanistDifferentialCommitMessage::newFromRawCorpus($text); $obj = ArcanistDifferentialCommitMessage::newFromRawCorpus($text);
$messages[$hash] = $obj; $messages[$hash] = $obj;
} }
@ -2162,9 +2159,6 @@ EOTEXT
foreach ($usable as $message) { foreach ($usable as $message) {
// Pick the first line out of each message. // Pick the first line out of each message.
$text = trim($message); $text = trim($message);
if ($text == self::AUTO_COMMIT_TITLE) {
continue;
}
$text = head(explode("\n", $text)); $text = head(explode("\n", $text));
$default[] = ' - '.$text."\n"; $default[] = ' - '.$text."\n";
} }

View file

@ -41,8 +41,6 @@ abstract class ArcanistWorkflow extends Phobject {
const COMMIT_ALLOW = 1; const COMMIT_ALLOW = 1;
const COMMIT_ENABLE = 2; const COMMIT_ENABLE = 2;
const AUTO_COMMIT_TITLE = 'Automatic commit by arc';
private $commitMode = self::COMMIT_DISABLE; private $commitMode = self::COMMIT_DISABLE;
private $conduit; private $conduit;
@ -837,45 +835,6 @@ abstract class ArcanistWorkflow extends Phobject {
" Working copy: __%s__\n\n", " Working copy: __%s__\n\n",
$api->getPath()); $api->getPath());
$untracked = $api->getUntrackedChanges();
if ($this->shouldRequireCleanUntrackedFiles()) {
if (!empty($untracked)) {
echo "You have untracked files in this working copy.\n\n".
$working_copy_desc.
" Untracked files in working copy:\n".
" ".implode("\n ", $untracked)."\n\n";
if ($api instanceof ArcanistGitAPI) {
echo phutil_console_wrap(
"Since you don't have '.gitignore' rules for these files and have ".
"not listed them in '.git/info/exclude', you may have forgotten ".
"to 'git add' them to your commit.\n");
} else if ($api instanceof ArcanistSubversionAPI) {
echo phutil_console_wrap(
"Since you don't have 'svn:ignore' rules for these files, you may ".
"have forgotten to 'svn add' them.\n");
} else if ($api instanceof ArcanistMercurialAPI) {
echo phutil_console_wrap(
"Since you don't have '.hgignore' rules for these files, you ".
"may have forgotten to 'hg add' them to your commit.\n");
}
if ($this->askForAdd($untracked)) {
$api->addToCommit($untracked);
$must_commit += array_flip($untracked);
} else if ($this->commitMode == self::COMMIT_DISABLE) {
$prompt = $this->getAskForAddPrompt($untracked);
if (phutil_console_confirm($prompt)) {
throw new ArcanistUsageException(pht(
"Add these files and then run 'arc %s' again.",
$this->getWorkflowName()));
}
}
}
}
// NOTE: this is a subversion-only concept. // NOTE: this is a subversion-only concept.
$incomplete = $api->getIncompleteChanges(); $incomplete = $api->getIncompleteChanges();
if ($incomplete) { if ($incomplete) {
@ -910,15 +869,75 @@ abstract class ArcanistWorkflow extends Phobject {
" ".implode("\n ", $missing))); " ".implode("\n ", $missing)));
} }
$uncommitted = $api->getUncommittedChanges();
$unstaged = $api->getUnstagedChanges(); $unstaged = $api->getUnstagedChanges();
// We only want files which are purely uncommitted.
$uncommitted = array_diff($uncommitted, $unstaged);
$untracked = $api->getUntrackedChanges();
if (!$this->shouldRequireCleanUntrackedFiles()) {
$untracked = array();
}
$should_commit = false;
if ($unstaged || $uncommitted) {
// NOTE: We're running this because it builds a cache and can take a
// perceptible amount of time to arrive at an answer, but we don't want
// to pause in the middle of printing the output below.
$this->getShouldAmend();
echo pht(
"You have uncommitted changes in this working copy.\n\n%s",
$working_copy_desc);
$lists = array();
if ($untracked) {
if ($api instanceof ArcanistGitAPI) {
$hint = pht(
'(To ignore these %s change(s), add them to ".git/info/exclude".)',
new PhutilNumber(count($untracked)));
} else if ($api instanceof ArcanistSubversionAPI) {
$hint = pht(
'(To ignore these %s change(s), add them to "svn:ignore".)',
new PhutilNumber(count($untracked)));
} else if ($api instanceof ArcanistMercurialAPI) {
$hint = pht(
'(To ignore these %s change(s), add them to ".hgignore".)',
new PhutilNumber(count($untracked)));
}
$untracked_list = " ".implode("\n ", $untracked);
$lists[] = pht(
" Untracked changes in working copy:\n %s\n%s",
$hint,
$untracked_list);
}
if ($unstaged) {
$unstaged_list = " ".implode("\n ", $unstaged);
$lists[] = pht(
" Unstaged changes in working copy:\n%s",
$unstaged_list);
}
if ($uncommitted) {
$uncommitted_list = " ".implode("\n ", $uncommitted);
$lists[] = pht(
" Uncommitted changes in working copy:\n%s",
$uncommitted_list);
}
echo implode("\n\n", $lists);
$all_uncommitted = array_merge($unstaged, $uncommitted);
if ($this->askForAdd($all_uncommitted)) {
if ($unstaged) { if ($unstaged) {
echo "You have unstaged changes in this working copy.\n\n".
$working_copy_desc.
" Unstaged changes in working copy:\n".
" ".implode("\n ", $unstaged)."\n";
if ($this->askForAdd($unstaged)) {
$api->addToCommit($unstaged); $api->addToCommit($unstaged);
$must_commit += array_flip($unstaged); }
$should_commit = true;
} else { } else {
$permit_autostash = $this->getConfigFromAnySource( $permit_autostash = $this->getConfigFromAnySource(
'arc.autostash', 'arc.autostash',
@ -929,40 +948,59 @@ abstract class ArcanistWorkflow extends Phobject {
$api->stashChanges(); $api->stashChanges();
$this->stashed = true; $this->stashed = true;
} else { } else {
if ($untracked && !$unstaged && !$uncommitted) {
// Give a tailored message if there are only untracked files,
// because the advice to commit files does not make sense in
// Subversion.
throw new ArcanistUsageException( throw new ArcanistUsageException(
'Stage and commit (or revert) them before proceeding.'); pht(
} 'You can not continue with untracked changes. Add them, '.
} 'discard them, or mark them as ignored before proceeding.'));
}
$uncommitted = $api->getUncommittedChanges();
foreach ($uncommitted as $key => $path) {
if (array_key_exists($path, $must_commit)) {
unset($uncommitted[$key]);
}
}
if ($uncommitted) {
echo "You have uncommitted changes in this working copy.\n\n".
$working_copy_desc.
" Uncommitted changes in working copy:\n".
" ".implode("\n ", $uncommitted)."\n";
if ($this->askForAdd($uncommitted)) {
$must_commit += array_flip($uncommitted);
} else { } else {
throw new ArcanistUncommittedChangesException( throw new ArcanistUsageException(
'Commit (or revert) them before proceeding.'); pht(
'You can not continue with uncommitted changes. Commit '.
'or discard them before proceeding.'));
}
}
} }
} }
if ($must_commit) { if ($should_commit) {
if ($this->getShouldAmend()) { if ($this->getShouldAmend()) {
$commit = head($api->getLocalCommitInformation()); $commit = head($api->getLocalCommitInformation());
$api->amendCommit($commit['message']); $api->amendCommit($commit['message']);
} else if ($api->supportsLocalCommits()) { } else if ($api->supportsLocalCommits()) {
$commit_message = phutil_console_prompt('Enter commit message:'); $template =
if ($commit_message == '') { "\n\n".
$commit_message = self::AUTO_COMMIT_TITLE; "# ".pht('Enter a commit message.')."\n#\n".
"# ".pht('Changes:')."\n#\n";
$paths = array_merge($uncommitted, $unstaged);
$paths = array_unique($paths);
sort($paths);
foreach ($paths as $path) {
$template .= "# ".$path."\n";
} }
$commit_message = $this->newInteractiveEditor($template)
->setName(pht('commit-message'))
->editInteractively();
if ($commit_message === $template) {
throw new ArcanistUsageException(
pht('You must provide a commit message.'));
}
$commit_message = ArcanistCommentRemover::removeComments(
$commit_message);
if (!strlen($commit_message)) {
throw new ArcanistUsageException(
pht('You must provide a nonempty commit message.'));
}
$api->doCommit($commit_message); $api->doCommit($commit_message);
} }
} }
@ -1005,6 +1043,7 @@ abstract class ArcanistWorkflow extends Phobject {
// TODO: Check commits since tracking branch. If empty then return false. // TODO: Check commits since tracking branch. If empty then return false.
// Don't amend the current commit if it has already been published.
$repository = $this->loadProjectRepository(); $repository = $this->loadProjectRepository();
if ($repository) { if ($repository) {
$callsign = $repository['callsign']; $callsign = $repository['callsign'];
@ -1013,7 +1052,7 @@ abstract class ArcanistWorkflow extends Phobject {
'diffusion.querycommits', 'diffusion.querycommits',
array('names' => array($commit_name))); array('names' => array($commit_name)));
$known_commit = idx($result['identifierMap'], $commit_name); $known_commit = idx($result['identifierMap'], $commit_name);
if (!$known_commit) { if ($known_commit) {
return false; return false;
} }
} }
@ -1049,11 +1088,11 @@ abstract class ArcanistWorkflow extends Phobject {
private function getAskForAddPrompt(array $files) { private function getAskForAddPrompt(array $files) {
if ($this->getShouldAmend()) { if ($this->getShouldAmend()) {
$prompt = pht( $prompt = pht(
'Do you want to amend these %s file(s) to the commit?', 'Do you want to amend these %s change(s) to the current commit?',
new PhutilNumber(count($files))); new PhutilNumber(count($files)));
} else { } else {
$prompt = pht( $prompt = pht(
'Do you want to add these %s file(s) to the commit?', 'Do you want to create a new commit with these %s change(s)?',
new PhutilNumber(count($files))); new PhutilNumber(count($files)));
} }
return $prompt; return $prompt;