Page MenuHomePhabricator

D21686.id51662.diff
No OneTemporary

D21686.id51662.diff

diff --git a/src/land/engine/ArcanistMercurialLandEngine.php b/src/land/engine/ArcanistMercurialLandEngine.php
--- a/src/land/engine/ArcanistMercurialLandEngine.php
+++ b/src/land/engine/ArcanistMercurialLandEngine.php
@@ -803,8 +803,8 @@
// descendants and the min commit has no ancestors. The min/max terms are
// used in a topological sense as chronological terms for commits can be
// misleading or incorrect in certain situations.
- $max_commit = last($commits)->getHash();
$min_commit = head($commits)->getHash();
+ $max_commit = last($commits)->getHash();
$revision_ref = $set->getRevisionRef();
$commit_message = $revision_ref->getCommitMessage();
diff --git a/src/repository/api/ArcanistMercurialAPI.php b/src/repository/api/ArcanistMercurialAPI.php
--- a/src/repository/api/ArcanistMercurialAPI.php
+++ b/src/repository/api/ArcanistMercurialAPI.php
@@ -658,37 +658,120 @@
public function doCommit($message) {
$tmp_file = new TempFile();
Filesystem::writeFile($tmp_file, $message);
- $this->execxLocal('commit -l %s', $tmp_file);
+ $this->execxLocal('commit --logfile %s', $tmp_file);
$this->reloadWorkingCopy();
}
public function amendCommit($message = null) {
+ $path_statuses = $this->buildUncommittedStatus();
+
if ($message === null) {
+ if (empty($path_statuses)) {
+ // If there are no changes to the working directory and the message is
+ // not being changed then there's nothing to amend.
+ return;
+ }
+
$message = $this->getCommitMessage('.');
}
$tmp_file = new TempFile();
Filesystem::writeFile($tmp_file, $message);
- try {
- $this->execxLocal(
- 'commit --amend -l %s',
- $tmp_file);
- } catch (CommandException $ex) {
- if (preg_match('/nothing changed/', $ex->getStdout())) {
- // NOTE: Mercurial considers it an error to make a no-op amend. Although
- // we generally defer to the underlying VCS to dictate behavior, this
- // one seems a little goofy, and we use amend as part of various
- // workflows under the assumption that no-op amends are fine. If this
- // amend failed because it's a no-op, just continue.
- } else {
+ if ($this->getMercurialFeature('evolve')) {
+ $this->execxLocal('amend --logfile %s --', $tmp_file);
+ try {
+ $this->execxLocal('evolve --all --');
+ } catch (CommandException $ex) {
+ $this->execxLocal('evolve --abort --');
throw $ex;
}
+ $this->reloadWorkingCopy();
+ return;
+ }
+
+ // Get the child nodes of the current changeset.
+ list($children) = $this->execxLocal(
+ 'log --template %s --rev %s --',
+ '{node} ',
+ 'children(.)');
+ $child_nodes = array_filter(explode(' ', $children));
+
+ // For a head commit we can simply use `commit --amend` for both new commit
+ // message and amending changes from the working directory.
+ if (empty($child_nodes)) {
+ try {
+ $this->execxLocal('commit --amend --logfile %s --', $tmp_file);
+ } catch (CommandException $ex) {
+ if (preg_match('/nothing changed/', $ex->getStdout())) {
+ // NOTE: Mercurial considers it an error to make a no-op amend.
+ // Although we generally defer to the underlying VCS to dictate
+ // behavior, this one seems a little goofy, and we use amend as part
+ // of various workflows under the assumption that no-op amends are
+ // fine. If this amend failed because it's a no-op, just continue.
+ } else {
+ throw $ex;
+ }
+ }
+ } else {
+ $this->amendNonHeadCommit($child_nodes, $tmp_file);
}
$this->reloadWorkingCopy();
}
+ /**
+ * Amends a non-head commit with a new message and file changes. This
+ * strategy is for Mercurial repositories without the evolve extension.
+ *
+ * 1. Run 'arc-amend' which uses Mercurial internals to amend the current
+ * commit with updated message/file-changes. It results in a new commit
+ * from the right parent
+ * 2. For each branch from the original commit, rebase onto the new commit,
+ * removing the original branch. Note that there is potential for this to
+ * cause a conflict but this is something the user has to address.
+ * 3. Strip the original commit.
+ *
+ * @param array The list of child changesets off the original commit.
+ * @param file The file containing the new commit message.
+ */
+ private function amendNonHeadCommit($child_nodes, $tmp_file) {
+ list($current) = $this->execxLocal(
+ 'log --template %s --rev . --',
+ '{node}');
+
+ $argv = array();
+ foreach ($this->getMercurialExtensionArguments() as $arg) {
+ $argv[] = $arg;
+ }
+ $argv[] = 'arc-amend';
+ $argv[] = '--logfile';
+ $argv[] = $tmp_file;
+ $this->execxLocal('%Ls', $argv);
+
+ list($new_commit) = $this->execxLocal(
+ 'log --rev tip --template %s --',
+ '{node}');
+
+ try {
+ foreach ($child_nodes as $child) {
+ // descendants(rev) will also include rev itself which is why this
+ // can't use a single rebase of descendants($current).
+ $revset = hgsprintf('descendants(%s)', $child);
+ $this->execxLocal(
+ 'rebase --dest %s --rev %s --',
+ $new_commit,
+ $revset);
+ }
+ } catch (CommandException $ex) {
+ $this->execxLocal('rebase --abort --');
+ throw $ex;
+ }
+
+ $this->execxLocal('--config extensions.strip= strip --rev %s --',
+ $current);
+ }
+
public function getCommitSummary($commit) {
if ($commit == 'null') {
return pht('(The Empty Void)');
diff --git a/src/repository/state/ArcanistRepositoryLocalState.php b/src/repository/state/ArcanistRepositoryLocalState.php
--- a/src/repository/state/ArcanistRepositoryLocalState.php
+++ b/src/repository/state/ArcanistRepositoryLocalState.php
@@ -192,10 +192,27 @@
return false;
}
+ /**
+ * Stash uncommitted changes temporarily. Use {@method:restoreStash()} to
+ * bring these changes back.
+ *
+ * Note that on Git repositories the stash acts as a stack, so saving the
+ * stash must match appropriately to restoring the stash.
+ *
+ * @return wild On Git this returns true, on Mercurial this returns a name
+ * (string) which references the stash that was made. This name
+ * should later be passed to {@method:restoreStash()}.
+ */
protected function saveStash() {
throw new PhutilMethodNotImplementedException();
}
+ /**
+ * Restores changes that were previously stashed by {@method:saveStash()}.
+ *
+ * @param wild On Git this parameter is unused, on Mercurial this should be
+ * the name (string) returned from {@method:saveStash()}.
+ */
protected function restoreStash($ref) {
throw new PhutilMethodNotImplementedException();
}
diff --git a/support/hg/arc-hg.py b/support/hg/arc-hg.py
--- a/support/hg/arc-hg.py
+++ b/support/hg/arc-hg.py
@@ -22,12 +22,31 @@
i18n,
node,
registrar,
+ util,
)
_ = i18n._
cmdtable = {}
command = registrar.command(cmdtable)
+@command(
+ b'arc-amend',
+ [
+ (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
+ (b'm', b'message', None, _(b'use text as commit message'), _(b'TEXT')),
+ ],
+ _(b'[--logfile FILE] [--message TEXT]'))
+def amend(ui, repo, source=None, **opts):
+ # The option keys seem to come in as 'str' type but the cmdutil.amend() code
+ # expects them as binary.
+ if opts.get('logfile'):
+ opts[b'logfile'] = opts.get('logfile')
+ if opts.get('message'):
+ opts[b'message'] = opts.get('message')
+
+ cmdutil.amend(ui, repo, repo[b'.'], {}, [], opts)
+ return 0
+
@command(
b'arc-ls-markers',
[(b'', b'output', b'',

File Metadata

Mime Type
text/plain
Expires
Tue, May 21, 7:02 PM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6304985
Default Alt Text
D21686.id51662.diff (7 KB)

Event Timeline