Permalink
Browse files

Make arc patch create a bookmark by default in hg

Summary:
Previously, arc patch would create a new commit under the existing
current bookmark in mercurial. There have been two discussions about what the
right behavior should be (D3334 and D3658). One side wants no commit at all,
and one side wants a commit under a new bookmark. The current implementation is
the worst of both worlds :(

This change makes it create a new bookmark at the revision's base before commiting,
same as the --bookmark flag used to do (which is now obsolete). That way the
existing bookmark doesn't move (in mercurial >=1.8). This is the same behavior
git has, which is convienent for groups migrating between the two.

Also makes hg's getCanonicalRevision handle svn revisions just like git. This way
arc patch will try to apply the patch to the appropriate revision in the history.

Test Plan:
Ran:
arc patch - Verified it created a new bookmark and commited on top of the
revision's base commit.

arc patch --nobranch - Verified it put the new commit on top of the current
bookmark without a new bookmark.

arc patch --nocommit - Verified it left all the changes in the working copy.

Also verified arc patch still works with git.

Reviewers: epriestley

Reviewed By: epriestley

CC: sid0, bos, dschleimer, aran, Korvin

Differential Revision: https://secure.phabricator.com/D5408
  • Loading branch information...
1 parent 2900ab6 commit ba78000b385d737ca805345a578b5a16ca8baaa5 @DurhamG DurhamG committed Mar 22, 2013
Showing with 100 additions and 80 deletions.
  1. +6 −0 src/repository/api/ArcanistMercurialAPI.php
  2. +94 −80 src/workflow/ArcanistPatchWorkflow.php
@@ -61,6 +61,12 @@ public function getSourceControlBaseRevision() {
}
public function getCanonicalRevisionName($string) {
+ $match = null;
+ if ($this->isHgSubversionRepo() &&
+ preg_match('/@([0-9]+)$/', $string, $match)) {
+ $string = hgsprintf('svnrev(%s)', $match[1]);
+ }
+
list($stdout) = $this->execxLocal(
'log -l 1 --template %s -r %s --',
'{node}',
@@ -95,26 +95,13 @@ public function getArguments() {
),
'nobranch' => array(
'supports' => array(
- 'git'
- ),
- 'help' =>
- "Normally under git, a new branch is created and then the patch ".
- "is applied and committed in the new branch. This flag ".
- "cherry-picks the resultant commit onto the original branch and ".
- "deletes the temporary branch.",
- 'conflicts' => array(
- 'update' => true,
- ),
- ),
- 'bookmark' => array(
- 'supports' => array(
- 'hg'
+ 'git', 'hg'
),
'help' =>
- "Normally under hg, a new bookmark is not created and the patch ".
- "is applied and committed in the current bookmark. With this flag, ".
- "a new bookmark is created and the patch is applied and committed ".
- "in the new bookmark.",
+ "Normally, a new branch (git) or bookmark (hg) is created and then ".
+ "the patch is applied and committed in the new branch/bookmark. ".
+ "This flag cherry-picks the resultant commit onto the original ".
+ "branch and deletes the temporary branch.",
'conflicts' => array(
'update' => true,
),
@@ -208,12 +195,9 @@ private function shouldCommit() {
}
private function canBranch() {
- // git only for now
$repository_api = $this->getRepositoryAPI();
- if (!($repository_api instanceof ArcanistGitAPI)) {
- return false;
- }
- return true;
+ return ($repository_api instanceof ArcanistGitAPI) ||
+ ($repository_api instanceof ArcanistMercurialAPI);
}
private function shouldBranch() {
@@ -224,22 +208,6 @@ private function shouldBranch() {
return true;
}
- private function shouldBookmark() {
- // specific to hg
- $repository_api = $this->getRepositoryAPI();
- if (!($repository_api instanceof ArcanistMercurialAPI)) {
- return false;
- }
-
- $bookmark = $this->getArgument('bookmark', false);
- if ($bookmark) {
- return true;
- }
-
- return false;
- }
-
-
private function getBranchName(ArcanistBundle $bundle) {
$branch_name = null;
$repository_api = $this->getRepositoryAPI();
@@ -322,52 +290,68 @@ private function hasBaseRevision(ArcanistBundle $bundle) {
$repository_api = $this->getRepositoryAPI();
// verify the base revision is valid
- // in a working copy that uses the git-svn bridge, the base revision might
- // be a svn uri instead of a git ref
-
- // NOTE: Use 'cat-file', not 'rev-parse --verify', because 'rev-parse'
- // always "verifies" any properly-formatted commit even if it does not
- // exist.
- list($err) = $repository_api->execManualLocal(
- 'cat-file -t %s',
- $base_revision);
- return !$err;
+ if ($repository_api instanceof ArcanistGitAPI) {
+ // in a working copy that uses the git-svn bridge, the base revision might
+ // be a svn uri instead of a git ref
+
+ // NOTE: Use 'cat-file', not 'rev-parse --verify', because 'rev-parse'
+ // always "verifies" any properly-formatted commit even if it does not
+ // exist.
+ list($err) = $repository_api->execManualLocal(
+ 'cat-file -t %s',
+ $base_revision);
+ return !$err;
+ } else if ($repository_api instanceof ArcanistMercurialAPI) {
+ return $repository_api->hasLocalCommit($base_revision);
+ }
+
+ return false;
}
private function createBranch(ArcanistBundle $bundle, $has_base_revision) {
- $branch_name = $this->getBranchName($bundle);
- $base_revision = $bundle->getBaseRevision();
$repository_api = $this->getRepositoryAPI();
+ if ($repository_api instanceof ArcanistGitAPI) {
+ $branch_name = $this->getBranchName($bundle);
+ $base_revision = $bundle->getBaseRevision();
+
+ if ($base_revision && $has_base_revision) {
+ $repository_api->execxLocal(
+ 'checkout -b %s %s',
+ $branch_name,
+ $base_revision);
+ } else {
+ $repository_api->execxLocal(
+ 'checkout -b %s',
+ $branch_name);
+ }
- if ($base_revision && $has_base_revision) {
- $repository_api->execxLocal(
- 'checkout -b %s %s',
- $branch_name,
- $base_revision);
- } else {
- $repository_api->execxLocal(
- 'checkout -b %s',
+ echo phutil_console_format(
+ "Created and checked out branch %s.\n",
$branch_name);
- }
+ } else if ($repository_api instanceof ArcanistMercurialAPI) {
+ $branch_name = $this->getBookmarkName($bundle);
+ $base_revision = $bundle->getBaseRevision();
- echo phutil_console_format(
- "Created and checked out branch %s.\n",
- $branch_name);
+ if ($base_revision && $has_base_revision) {
+ $base_revision = $repository_api->getCanonicalRevisionName(
+ $base_revision);
- return $branch_name;
- }
+ echo "Updating to the revision's base commit\n";
+ $repository_api->execPassthru(
+ 'update %s',
+ $base_revision);
+ }
- private function createBookmark(ArcanistBundle $bundle) {
- $bookmark_name = $this->getBookmarkName($bundle);
- $repository_api = $this->getRepositoryAPI();
+ $repository_api->execxLocal(
+ 'bookmark %s',
+ $branch_name);
- $repository_api->execxLocal(
- 'bookmark %s',
- $bookmark_name);
+ echo phutil_console_format(
+ "Created and checked out bookmark %s.\n",
+ $branch_name);
+ }
- echo phutil_console_format(
- "Created and applied bookmark %s.\n",
- $bookmark_name);
+ return $branch_name;
}
private function shouldUpdateWorkingCopy() {
@@ -458,17 +442,23 @@ public function run() {
$this->canBranch() &&
($this->shouldBranch() || $has_base_revision)) {
- $original_branch = $repository_api->getBranchName();
+ if ($repository_api instanceof ArcanistGitAPI) {
+ $original_branch = $repository_api->getBranchName();
+ } else if ($repository_api instanceof ArcanistMercurialAPI) {
+ $original_branch = $repository_api->getActiveBookmark();
+ }
+
// If we weren't on a branch, then record the ref we'll return to
// instead.
if ($original_branch === null) {
- $original_branch = $repository_api->getCanonicalRevisionName('HEAD');
+ if ($repository_api instanceof ArcanistGitAPI) {
+ $original_branch = $repository_api->getCanonicalRevisionName('HEAD');
+ } else if ($repository_api instanceof ArcanistMercurialAPI) {
+ $original_branch = $repository_api->getCanonicalRevisionName('.');
+ }
}
- $new_branch = $this->createBranch($bundle, $has_base_revision);
- }
- if ($this->shouldBookmark()) {
- $this->createBookmark($bundle);
+ $new_branch = $this->createBranch($bundle, $has_base_revision);
}
if ($repository_api instanceof ArcanistSubversionAPI) {
@@ -777,10 +767,34 @@ public function run() {
$author_cmd);
$future->write($commit_message);
$future->resolvex();
+
+ if (!$this->shouldBranch() && $has_base_revision) {
+ $original_rev = $repository_api->getCanonicalRevisionName(
+ $original_branch);
+ $current_parent = $repository_api->getCanonicalRevisionName(
+ hgsprintf('%s^', $new_branch));
+
+ $err = 0;
+ if ($original_rev != $current_parent) {
+ list($err) = $repository_api->execManualLocal(
+ 'rebase --dest %s --rev %s',
+ hgsprintf('%s', $original_branch),
+ hgsprintf('%s', $new_branch));
+ }
+
+ $repository_api->execxLocal('bookmark --delete %s', $new_branch);
+ if ($err) {
+ $repository_api->execManualLocal('rebase --abort');
+ throw new ArcanistUsageException(phutil_console_format(
+ "\n<bg:red>** Rebase onto $original_branch failed!**</bg>\n"));
+ }
+ }
+
$verb = 'committed';
} else {
$verb = 'applied';
}
+
echo phutil_console_format(
"<bg:green>** OKAY **</bg> Successfully {$verb} patch.\n");

0 comments on commit ba78000

Please sign in to comment.