mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-22 06:42:41 +01:00
Support "arc land --pick" to pick specific changes out of a sequence
Summary: Ref T13546. If you have "feature1", "feature2", etc., "arc land feature4" will now land the entire sequence. Provide "arc land --pick feature4" to work more like the old "arc land" did. This cherry-picks the commits associated with "feature4", then cascades onto the ancestor of the range. Test Plan: Ran "arc land --pick land14" to pick a change out of a stack. Maniphest Tasks: T13546 Differential Revision: https://secure.phabricator.com/D21340
This commit is contained in:
parent
7ddaed9aba
commit
ab70626c12
5 changed files with 88 additions and 17 deletions
|
@ -5,6 +5,7 @@ final class ArcanistLandCommitSet
|
|||
|
||||
private $revisionRef;
|
||||
private $commits;
|
||||
private $isPick;
|
||||
|
||||
public function setRevisionRef(ArcanistRevisionRef $revision_ref) {
|
||||
$this->revisionRef = $revision_ref;
|
||||
|
@ -49,4 +50,23 @@ final class ArcanistLandCommitSet
|
|||
return false;
|
||||
}
|
||||
|
||||
public function hasDirectSymbols() {
|
||||
foreach ($this->commits as $commit) {
|
||||
if ($commit->getDirectSymbols()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setIsPick($is_pick) {
|
||||
$this->isPick = $is_pick;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsPick() {
|
||||
return $this->isPick;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@ final class ArcanistGitLandEngine
|
|||
return;
|
||||
}
|
||||
|
||||
$min_commit = head($set->getCommits())->getHash();
|
||||
$old_commit = last($set->getCommits())->getHash();
|
||||
$new_commit = $into_commit;
|
||||
|
||||
|
@ -143,17 +144,38 @@ final class ArcanistGitLandEngine
|
|||
'Rebasing "%s" onto landed state...',
|
||||
$branch_name));
|
||||
|
||||
// If we used "--pick" to select this commit, we want to rebase branches
|
||||
// that descend from it onto its ancestor, not onto the landed change.
|
||||
|
||||
// For example, if the change sequence was "W", "X", "Y", "Z" and we
|
||||
// landed "Y" onto "master" using "--pick", we want to rebase "Z" onto
|
||||
// "X" (so "W" and "X", which it will often depend on, are still
|
||||
// its ancestors), not onto the new "master".
|
||||
|
||||
if ($set->getIsPick()) {
|
||||
$rebase_target = $min_commit.'^';
|
||||
} else {
|
||||
$rebase_target = $new_commit;
|
||||
}
|
||||
|
||||
try {
|
||||
$api->execxLocal(
|
||||
'rebase --onto %s -- %s %s',
|
||||
$new_commit,
|
||||
$rebase_target,
|
||||
$old_commit,
|
||||
$branch_name);
|
||||
} catch (CommandException $ex) {
|
||||
// TODO: If we have a stashed state or are not running in incremental
|
||||
// mode: abort the rebase, restore the local state, and pop the stash.
|
||||
// Otherwise, drop the user out here.
|
||||
throw $ex;
|
||||
$api->execManualLocal('rebase --abort');
|
||||
$api->execManualLocal('reset --hard HEAD --');
|
||||
|
||||
$log->writeWarning(
|
||||
pht('REBASE CONFLICT'),
|
||||
pht(
|
||||
'Branch "%s" does not rebase cleanly from "%s" onto '.
|
||||
'"%s", skipping.',
|
||||
$branch_name,
|
||||
$this->getDisplayHash($old_commit),
|
||||
$this->getDisplayHash($rebase_target)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ abstract class ArcanistLandEngine
|
|||
|
||||
private $localState;
|
||||
private $hasUnpushedChanges;
|
||||
private $pickArgument;
|
||||
|
||||
final public function setOntoRemote($onto_remote) {
|
||||
$this->ontoRemote = $onto_remote;
|
||||
|
@ -75,6 +76,15 @@ abstract class ArcanistLandEngine
|
|||
return $this->intoEmpty;
|
||||
}
|
||||
|
||||
final public function setPickArgument($pick_argument) {
|
||||
$this->pickArgument = $pick_argument;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getPickArgument() {
|
||||
return $this->pickArgument;
|
||||
}
|
||||
|
||||
final public function setIntoLocal($into_local) {
|
||||
$this->intoLocal = $into_local;
|
||||
return $this;
|
||||
|
@ -1360,6 +1370,13 @@ abstract class ArcanistLandEngine
|
|||
$strategy = $this->selectMergeStrategy();
|
||||
$this->setStrategy($strategy);
|
||||
|
||||
$is_pick = $this->getPickArgument();
|
||||
if ($is_pick && !$this->isSquashStrategy()) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'You can not "--pick" changes under the "merge" strategy.'));
|
||||
}
|
||||
|
||||
// Build the symbol ref here (which validates the format of the symbol),
|
||||
// but don't load the object until later on when we're sure we actually
|
||||
// need it, since loading it requires a relatively expensive Conduit call.
|
||||
|
@ -1486,16 +1503,7 @@ abstract class ArcanistLandEngine
|
|||
continue;
|
||||
}
|
||||
|
||||
$symbols = null;
|
||||
foreach ($set->getCommits() as $commit) {
|
||||
$commit_symbols = $commit->getDirectSymbols();
|
||||
if ($commit_symbols) {
|
||||
$symbols = $commit_symbols;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($symbols) {
|
||||
if ($set->hasDirectSymbols()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1524,6 +1532,18 @@ abstract class ArcanistLandEngine
|
|||
// set of commits, and try to confirm that the state we're about to land
|
||||
// is the current state in Differential.
|
||||
|
||||
$is_pick = $this->getPickArgument();
|
||||
if ($is_pick) {
|
||||
foreach ($sets as $key => $set) {
|
||||
if ($set->hasDirectSymbols()) {
|
||||
$set->setIsPick(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($sets[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $sets;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,9 @@ name, a bookmark name, a topic name, a raw commit hash, a symbolic reference,
|
|||
etc.
|
||||
|
||||
When you provide a __ref__, all unpublished changes which are present in
|
||||
ancestors of that __ref__ will be selected for publishing.
|
||||
ancestors of that __ref__ will be selected for publishing. (With the
|
||||
**--pick** flag, only the unpublished changes you directly reference will be
|
||||
selected.)
|
||||
|
||||
For example, if you provide local branch "feature3" as a __ref__ argument, that
|
||||
may also select the changes in "feature1" and "feature2" (if they are ancestors
|
||||
|
@ -222,6 +224,11 @@ EOTEXT
|
|||
'complicated changes by allowing you to make progress one '.
|
||||
'step at a time.'),
|
||||
)),
|
||||
$this->newWorkflowArgument('pick')
|
||||
->setHelp(
|
||||
pht(
|
||||
'Land only the changes directly named by arguments, instead '.
|
||||
'of all reachable ancestors.')),
|
||||
$this->newWorkflowArgument('ref')
|
||||
->setWildcard(true),
|
||||
);
|
||||
|
@ -308,6 +315,7 @@ EOTEXT
|
|||
|
||||
$revision = $this->getArgument('revision');
|
||||
$strategy = $this->getArgument('strategy');
|
||||
$pick = $this->getArgument('pick');
|
||||
|
||||
$land_engine
|
||||
->setViewer($this->getViewer())
|
||||
|
@ -324,6 +332,7 @@ EOTEXT
|
|||
->setIntoEmptyArgument($into_empty)
|
||||
->setIntoLocalArgument($into_local)
|
||||
->setIntoArgument($into)
|
||||
->setPickArgument($pick)
|
||||
->setIsIncremental($is_incremental)
|
||||
->setRevisionSymbol($revision);
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ branch or bookmark that exists in the working copy. If it finds one, it will
|
|||
switch to it. If it does not find one, it will attempt to create a new branch
|
||||
or bookmark.
|
||||
|
||||
When "arc work" creates a branch or bookmark, it will use "--start" as the
|
||||
When "arc work" creates a branch or bookmark, it will use **--start** as the
|
||||
branchpoint if it is provided. Otherwise, the current working copy state will
|
||||
serve as the starting point.
|
||||
EOHELP
|
||||
|
|
Loading…
Reference in a new issue