Page MenuHomePhabricator

D14137.diff
No OneTemporary

D14137.diff

diff --git a/src/internationalization/ArcanistUSEnglishTranslation.php b/src/internationalization/ArcanistUSEnglishTranslation.php
--- a/src/internationalization/ArcanistUSEnglishTranslation.php
+++ b/src/internationalization/ArcanistUSEnglishTranslation.php
@@ -68,6 +68,16 @@
'Ignore this untracked file and continue?',
'Ignore these untracked files and continue?',
),
+
+ '%s submodule(s) have uncommitted or untracked changes:' => array(
+ 'A submodule has uncommitted or untracked changes:',
+ 'Submodules have uncommitted or untracked changes:',
+ ),
+
+ 'Ignore the changes to these %s submodule(s) and continue?' => array(
+ 'Ignore the changes to this submodule and continue?',
+ 'Ignore the changes to these submodules and continue?',
+ ),
);
}
diff --git a/src/repository/api/ArcanistGitAPI.php b/src/repository/api/ArcanistGitAPI.php
--- a/src/repository/api/ArcanistGitAPI.php
+++ b/src/repository/api/ArcanistGitAPI.php
@@ -672,7 +672,7 @@
$result = new PhutilArrayWithDefaultValue();
list($stdout) = $uncommitted_future->resolvex();
- $uncommitted_files = $this->parseGitStatus($stdout);
+ $uncommitted_files = $this->parseGitRawDiff($stdout);
foreach ($uncommitted_files as $path => $mask) {
$result[$path] |= ($mask | self::FLAG_UNCOMMITTED);
}
@@ -704,7 +704,7 @@
$this->getDiffBaseOptions(),
$this->getBaseCommit());
- return $this->parseGitStatus($stdout);
+ return $this->parseGitRawDiff($stdout);
}
public function getGitConfig($key, $default = null) {
@@ -759,7 +759,7 @@
return $this;
}
- private function parseGitStatus($status, $full = false) {
+ private function parseGitRawDiff($status, $full = false) {
static $flags = array(
'A' => self::FLAG_ADDED,
'M' => self::FLAG_MODIFIED,
@@ -777,17 +777,51 @@
$files = array();
foreach ($lines as $line) {
$mask = 0;
+
+ // "git diff --raw" lines begin with a ":" character.
+ $old_mode = ltrim($line[0], ':');
+ $new_mode = $line[1];
+
+ // The hashes may be padded with "." characters for alignment. Discard
+ // them.
+ $old_hash = rtrim($line[2], '.');
+ $new_hash = rtrim($line[3], '.');
+
$flag = $line[4];
$file = $line[5];
- foreach ($flags as $key => $bits) {
- if ($flag == $key) {
- $mask |= $bits;
- }
+
+ $new_value = intval($new_mode, 8);
+ $is_submodule = (($new_value & 0160000) === 0160000);
+
+ if (($is_submodule) &&
+ ($flag == 'M') &&
+ ($old_hash === $new_hash) &&
+ ($old_mode === $new_mode)) {
+ // See T9455. We see this submodule as "modified", but the old and new
+ // hashes are the same and the old and new modes are the same, so we
+ // don't directly see a modification.
+
+ // We can end up here if we have a submodule which has uncommitted
+ // changes inside of it (for example, the user has added untracked
+ // files or made uncommitted changes to files in the submodule). In
+ // this case, we set a different flag because we can't meaningfully
+ // give users the same prompt.
+
+ // Note that if the submodule has real changes from the parent
+ // perspective (the base commit has changed) and also has uncommitted
+ // changes, we'll only see the real changes and miss the uncommitted
+ // changes. At the time of writing, there is no reasonable porcelain
+ // for finding those changes, and the impact of this error seems small.
+
+ $mask |= self::FLAG_EXTERNALS;
+ } else if (isset($flags[$flag])) {
+ $mask |= $flags[$flag];
}
+
if ($full) {
$files[$file] = array(
'mask' => $mask,
- 'ref' => rtrim($line[3], '.'),
+ 'ref' => $new_hash,
);
} else {
$files[$file] = $mask;
@@ -807,7 +841,7 @@
list($stdout) = $this->execxLocal(
'diff --raw %s',
$since_commit);
- return $this->parseGitStatus($stdout);
+ return $this->parseGitRawDiff($stdout);
}
public function getBlame($path) {
diff --git a/src/repository/api/ArcanistRepositoryAPI.php b/src/repository/api/ArcanistRepositoryAPI.php
--- a/src/repository/api/ArcanistRepositoryAPI.php
+++ b/src/repository/api/ArcanistRepositoryAPI.php
@@ -15,6 +15,9 @@
const FLAG_MISSING = 32;
const FLAG_UNSTAGED = 64;
const FLAG_UNCOMMITTED = 128;
+
+ // Occurs in SVN when you have uncommitted changes to a modified external,
+ // or in Git when you have uncommitted or untracked changes in a submodule.
const FLAG_EXTERNALS = 256;
// Occurs in SVN when you replace a file with a directory without telling
@@ -195,6 +198,14 @@
/**
* @task status
*/
+ final public function getDirtyExternalChanges() {
+ return $this->getUncommittedPathsWithMask(self::FLAG_EXTERNALS);
+ }
+
+
+ /**
+ * @task status
+ */
private function getUncommittedPathsWithMask($mask) {
$match = array();
foreach ($this->getUncommittedStatus() as $path => $flags) {
diff --git a/src/workflow/ArcanistFeatureWorkflow.php b/src/workflow/ArcanistFeatureWorkflow.php
--- a/src/workflow/ArcanistFeatureWorkflow.php
+++ b/src/workflow/ArcanistFeatureWorkflow.php
@@ -356,8 +356,8 @@
foreach ($out as $line) {
$table->addRow(array(
'current' => $line['current'] ? '*' : '',
- 'name' => phutil_console_format('**%s**', $line['name']),
- 'status' => phutil_console_format(
+ 'name' => tsprintf('**%s**', $line['name']),
+ 'status' => tsprintf(
"<fg:{$line['color']}>%s</fg>", $line['status']),
'descr' => $line['desc'],
));
diff --git a/src/workflow/ArcanistListWorkflow.php b/src/workflow/ArcanistListWorkflow.php
--- a/src/workflow/ArcanistListWorkflow.php
+++ b/src/workflow/ArcanistListWorkflow.php
@@ -91,11 +91,11 @@
$revision = $revisions[$key];
$table->addRow(array(
- 'exists' => $spec['exists'] ? phutil_console_format('**%s**', '*') : '',
- 'status' => phutil_console_format(
+ 'exists' => $spec['exists'] ? tsprintf('**%s**', '*') : '',
+ 'status' => tsprintf(
"<fg:{$spec['color']}>%s</fg>",
$spec['statusName']),
- 'title' => phutil_console_format(
+ 'title' => tsprintf(
'**D%d:** %s',
$revision['id'],
$revision['title']),
diff --git a/src/workflow/ArcanistPhrequentWorkflow.php b/src/workflow/ArcanistPhrequentWorkflow.php
--- a/src/workflow/ArcanistPhrequentWorkflow.php
+++ b/src/workflow/ArcanistPhrequentWorkflow.php
@@ -56,7 +56,7 @@
$table->addRow(array(
'type' => '('.$column_type.')',
- 'time' => phutil_format_relative_time($result['time']),
+ 'time' => tsprintf($result['time']),
'name' => $phid_map[$result['phid']],
));
diff --git a/src/workflow/ArcanistTasksWorkflow.php b/src/workflow/ArcanistTasksWorkflow.php
--- a/src/workflow/ArcanistTasksWorkflow.php
+++ b/src/workflow/ArcanistTasksWorkflow.php
@@ -111,7 +111,7 @@
// Render the "T123" column.
$task_id = 'T'.$task['id'];
- $formatted_task_id = phutil_console_format('**%s**', $task_id);
+ $formatted_task_id = tsprintf('**%s**', $task_id);
$output['id'] = $formatted_task_id;
// Render the "Title" column.
@@ -145,7 +145,7 @@
} else {
$color = 'white';
}
- $formatted_priority = phutil_console_format(
+ $formatted_priority = tsprintf(
"<bg:{$color}> </bg> %s",
$task['priority']);
$output['priority'] = $formatted_priority;
@@ -159,7 +159,7 @@
$status_text = $task['statusName'];
$status_color = 'green';
}
- $formatted_status = phutil_console_format(
+ $formatted_status = tsprintf(
"<bg:{$status_color}> </bg> %s",
$status_text);
$output['status'] = $formatted_status;
diff --git a/src/workflow/ArcanistWorkflow.php b/src/workflow/ArcanistWorkflow.php
--- a/src/workflow/ArcanistWorkflow.php
+++ b/src/workflow/ArcanistWorkflow.php
@@ -893,11 +893,47 @@
implode("\n ", $missing)));
}
+ $externals = $api->getDirtyExternalChanges();
+
+ // TODO: This state can exist in Subversion, but it is currently handled
+ // elsewhere. It should probably be handled here, eventually.
+ if ($api instanceof ArcanistSubversionAPI) {
+ $externals = array();
+ }
+
+ if ($externals) {
+ $message = pht(
+ '%s submodule(s) have uncommitted or untracked changes:',
+ new PhutilNumber(count($externals)));
+
+ $prompt = pht(
+ 'Ignore the changes to these %s submodule(s) and continue?',
+ new PhutilNumber(count($externals)));
+
+ $list = id(new PhutilConsoleList())
+ ->setWrap(false)
+ ->addItems($externals);
+
+ id(new PhutilConsoleBlock())
+ ->addParagraph($message)
+ ->addList($list)
+ ->draw();
+
+ $ok = phutil_console_confirm($prompt, $default_no = false);
+ if (!$ok) {
+ throw new ArcanistUserAbortException();
+ }
+ }
+
$uncommitted = $api->getUncommittedChanges();
$unstaged = $api->getUnstagedChanges();
+ // We already dealt with externals.
+ $unstaged = array_diff($unstaged, $externals);
+
// We only want files which are purely uncommitted.
$uncommitted = array_diff($uncommitted, $unstaged);
+ $uncommitted = array_diff($uncommitted, $externals);
$untracked = $api->getUntrackedChanges();
if (!$this->shouldRequireCleanUntrackedFiles()) {

File Metadata

Mime Type
text/plain
Expires
Fri, May 10, 11:32 PM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6284392
Default Alt Text
D14137.diff (9 KB)

Event Timeline