Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14620224
D21686.id51667.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
D21686.id51667.diff
View Options
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,28 @@
return false;
}
+ /**
+ * Stash uncommitted changes temporarily. Use {@method:restoreStash()} to
+ * bring these changes back.
+ *
+ * Note that saving and restoring changes may not behave as expected if used
+ * in a non-stack manner, i.e. proper use involves only restoring stashes in
+ * the reverse order they were saved.
+ *
+ * @return wild A reference object that refers to the changes which were
+ * saved. When restoring changes this should be passed to
+ * {@method:restoreStash()}.
+ */
protected function saveStash() {
throw new PhutilMethodNotImplementedException();
}
+ /**
+ * Restores changes that were previously stashed by {@method:saveStash()}.
+ *
+ * @param wild A reference object referring to which previously stashed
+ * changes to restore, from invoking {@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
@@ -21,18 +21,118 @@
hg,
i18n,
node,
- registrar,
)
_ = i18n._
cmdtable = {}
-command = registrar.command(cmdtable)
+
+# Older veresions of Mercurial (~4.7) moved the command function and the
+# remoteopts object to different modules. Using try/except here to attempt
+# allowing this module to load properly, despite whether individual commands
+# will work properly on older versions of Mercurial or not.
+# https://phab.mercurial-scm.org/rHG46ba2cdda476ac53a8a8f50e4d9435d88267db60
+# https://phab.mercurial-scm.org/rHG04baab18d60a5c833ab3190506147e01b3c6d12c
+try:
+ from mercurial import registrar
+ command = registrar.command(cmdtable)
+except:
+ command = cmdutil.command(cmdtable)
+
+try:
+ if "remoteopts" in cmdutil:
+ remoteopts = cmdutil.remoteopts
+except:
+ from mercurial import commands
+ remoteopts = commands.remoteopts
+
+@command(
+ b'arc-amend',
+ [
+ (b'l',
+ b'logfile',
+ b'',
+ _(b'read commit message from file'),
+ _(b'FILE')),
+ (b'm',
+ b'message',
+ b'',
+ _(b'use text as commit message'),
+ _(b'TEXT')),
+ (b'u',
+ b'user',
+ b'',
+ _(b'record the specified user as committer'),
+ _(b'USER')),
+ (b'd',
+ b'date',
+ b'',
+ _(b'record the specified date as commit date'),
+ _(b'DATE')),
+ (b'A',
+ b'addremove',
+ False,
+ _(b'mark new/missing files as added/removed before committing')),
+ (b'n',
+ b'note',
+ b'',
+ _(b'store a note on amend'),
+ _(b'TEXT')),
+ ],
+ _(b'[OPTION]'))
+def amend(ui, repo, source=None, **opts):
+ """amend
+
+ Uses Mercurial internal API to amend changes to a non-head commit.
+
+ (This is an Arcanist extension to Mercurial.)
+
+ Returns 0 if amending succeeds, 1 otherwise.
+ """
+
+ # The option keys seem to come in as 'str' type but the cmdutil.amend() code
+ # expects them as binary. To account for both Python 2 and Python 3
+ # compatibility, insert the value under both 'str' and binary type.
+ newopts = {}
+ for key in opts:
+ val = opts.get(key)
+ newopts[key] = val
+ if isinstance(key, str):
+ newkey = key.encode('UTF-8')
+ newopts[newkey] = val
+
+ orig = repo[b'.']
+ extra = {}
+ pats = []
+ cmdutil.amend(ui, repo, orig, extra, pats, newopts)
+
+ """
+ # This will allow running amend on older versions of Mercurial, ~3.5, however
+ # the behavior on those versions will squash child commits of the working
+ # directory into the amended commit which is undesired.
+ try:
+ cmdutil.amend(ui, repo, orig, extra, pats, newopts)
+ except:
+ def commitfunc(ui, repo, message, match, opts):
+ return repo.commit(
+ message,
+ opts.get('user') or orig.user(),
+ opts.get('date') or orig.date(),
+ match,
+ extra=extra)
+ cmdutil.amend(ui, repo, commitfunc, orig, extra, pats, newopts)
+ """
+
+ return 0
@command(
b'arc-ls-markers',
- [(b'', b'output', b'',
- _(b'file to output refs to'), _(b'FILE')),
- ] + cmdutil.remoteopts,
+ [
+ (b'',
+ b'output',
+ b'',
+ _(b'file to output refs to'),
+ _(b'FILE')),
+ ] + remoteopts,
_(b'[--output FILENAME] [SOURCE]'))
def lsmarkers(ui, repo, source=None, **opts):
"""list markers
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jan 11, 1:19 AM (10 h, 1 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6984384
Default Alt Text
D21686.id51667.diff (10 KB)
Attached To
Mode
D21686: Update "arc diff" to amend non-head commits with Mercurial
Attached
Detach File
Event Timeline
Log In to Comment