2011-01-09 15:22:25 -08:00
|
|
|
<?php
|
|
|
|
|
2011-02-19 11:36:08 -08:00
|
|
|
/**
|
|
|
|
* Interfaces with Git working copies.
|
|
|
|
*/
|
2012-01-31 12:07:05 -08:00
|
|
|
final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
2011-01-09 15:22:25 -08:00
|
|
|
|
Improve git behavior in the zero- and one- commit case
Summary:
Git works completely differently for commits zero and one than for 2..N so add
more special casing to handle them. See:
- {T206}
- {T596}
The getCommitRange() block is also fatal land, although I wasn't able to reach
it. I'll follow up with @s on T596.
Test Plan:
- Created a new, empty repository ("mkdir x; cd x; git init").
- Ran "arc lint", "arc unit", "arc diff" against it with no commits (the first
two work, the third fails helpfully).
- Made an initial commit.
- Ran "arc lint", "arc unit", "arc diff" against it (all work correctly).
Reviewers: btrahan, jungejason, aran
Reviewed By: aran
CC: s, aran
Differential Revision: 1142
2011-11-30 09:15:37 -08:00
|
|
|
private $repositoryHasNoCommits = false;
|
2011-01-09 15:22:25 -08:00
|
|
|
const SEARCH_LENGTH_FOR_PARENT_REVISIONS = 16;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For the repository's initial commit, 'git diff HEAD^' and similar do
|
2012-12-17 12:53:28 -08:00
|
|
|
* not work. Using this instead does work; it is the hash of the empty tree.
|
2011-01-09 15:22:25 -08:00
|
|
|
*/
|
|
|
|
const GIT_MAGIC_ROOT_COMMIT = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
|
|
|
|
|
2014-06-11 16:35:50 -07:00
|
|
|
private $symbolicHeadCommit;
|
2014-06-11 14:37:01 -07:00
|
|
|
private $resolvedHeadCommit;
|
|
|
|
|
2012-03-02 16:47:34 -08:00
|
|
|
protected function buildLocalFuture(array $argv) {
|
|
|
|
$argv[0] = 'git '.$argv[0];
|
|
|
|
|
|
|
|
$future = newv('ExecFuture', $argv);
|
|
|
|
$future->setCWD($this->getPath());
|
|
|
|
return $future;
|
|
|
|
}
|
|
|
|
|
2013-05-30 21:03:21 -07:00
|
|
|
public function execPassthru($pattern /* , ... */) {
|
|
|
|
$args = func_get_args();
|
|
|
|
|
|
|
|
static $git = null;
|
|
|
|
if ($git === null) {
|
|
|
|
if (phutil_is_windows()) {
|
|
|
|
// NOTE: On Windows, phutil_passthru() uses 'bypass_shell' because
|
|
|
|
// everything goes to hell if we don't. We must provide an absolute
|
|
|
|
// path to Git for this to work properly.
|
|
|
|
$git = Filesystem::resolveBinary('git');
|
|
|
|
$git = csprintf('%s', $git);
|
|
|
|
} else {
|
|
|
|
$git = 'git';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$args[0] = $git.' '.$args[0];
|
|
|
|
|
|
|
|
return call_user_func_array('phutil_passthru', $args);
|
|
|
|
}
|
|
|
|
|
2012-03-02 16:47:34 -08:00
|
|
|
|
2011-01-09 15:22:25 -08:00
|
|
|
public function getSourceControlSystemName() {
|
|
|
|
return 'git';
|
|
|
|
}
|
|
|
|
|
2012-06-12 12:39:15 -07:00
|
|
|
public function getMetadataPath() {
|
2013-01-17 11:28:13 -08:00
|
|
|
static $path = null;
|
|
|
|
if ($path === null) {
|
|
|
|
list($stdout) = $this->execxLocal('rev-parse --git-dir');
|
|
|
|
$path = rtrim($stdout, "\n");
|
|
|
|
// the output of git rev-parse --git-dir is an absolute path, unless
|
|
|
|
// the cwd is the root of the repository, in which case it uses the
|
|
|
|
// relative path of .git. If we get this relative path, turn it into
|
|
|
|
// an absolute path.
|
|
|
|
if ($path === '.git') {
|
|
|
|
$path = $this->getPath('.git');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $path;
|
2012-06-12 12:39:15 -07:00
|
|
|
}
|
|
|
|
|
Improve git behavior in the zero- and one- commit case
Summary:
Git works completely differently for commits zero and one than for 2..N so add
more special casing to handle them. See:
- {T206}
- {T596}
The getCommitRange() block is also fatal land, although I wasn't able to reach
it. I'll follow up with @s on T596.
Test Plan:
- Created a new, empty repository ("mkdir x; cd x; git init").
- Ran "arc lint", "arc unit", "arc diff" against it with no commits (the first
two work, the third fails helpfully).
- Made an initial commit.
- Ran "arc lint", "arc unit", "arc diff" against it (all work correctly).
Reviewers: btrahan, jungejason, aran
Reviewed By: aran
CC: s, aran
Differential Revision: 1142
2011-11-30 09:15:37 -08:00
|
|
|
public function getHasCommits() {
|
|
|
|
return !$this->repositoryHasNoCommits;
|
|
|
|
}
|
|
|
|
|
2014-06-11 14:37:01 -07:00
|
|
|
/**
|
|
|
|
* Tests if a child commit is descendant of a parent commit.
|
|
|
|
* If child and parent are the same, it returns false.
|
2014-06-11 16:35:50 -07:00
|
|
|
* @param Child commit SHA.
|
|
|
|
* @param Parent commit SHA.
|
|
|
|
* @return bool True if the child is a descendant of the parent.
|
2014-06-11 14:37:01 -07:00
|
|
|
*/
|
|
|
|
private function isDescendant($child, $parent) {
|
2014-06-11 16:35:50 -07:00
|
|
|
list($common_ancestor) = $this->execxLocal(
|
|
|
|
'merge-base %s %s',
|
|
|
|
$child,
|
|
|
|
$parent);
|
2014-06-11 14:37:01 -07:00
|
|
|
$common_ancestor = trim($common_ancestor);
|
|
|
|
|
2014-06-11 16:35:50 -07:00
|
|
|
return ($common_ancestor == $parent) && ($common_ancestor != $child);
|
2014-06-11 14:37:01 -07:00
|
|
|
}
|
|
|
|
|
2011-08-23 18:48:55 -07:00
|
|
|
public function getLocalCommitInformation() {
|
Improve git behavior in the zero- and one- commit case
Summary:
Git works completely differently for commits zero and one than for 2..N so add
more special casing to handle them. See:
- {T206}
- {T596}
The getCommitRange() block is also fatal land, although I wasn't able to reach
it. I'll follow up with @s on T596.
Test Plan:
- Created a new, empty repository ("mkdir x; cd x; git init").
- Ran "arc lint", "arc unit", "arc diff" against it with no commits (the first
two work, the third fails helpfully).
- Made an initial commit.
- Ran "arc lint", "arc unit", "arc diff" against it (all work correctly).
Reviewers: btrahan, jungejason, aran
Reviewed By: aran
CC: s, aran
Differential Revision: 1142
2011-11-30 09:15:37 -08:00
|
|
|
if ($this->repositoryHasNoCommits) {
|
|
|
|
// Zero commits.
|
|
|
|
throw new Exception(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"You can't get local commit information for a repository with no ".
|
|
|
|
"commits."));
|
2012-12-17 12:54:08 -08:00
|
|
|
} else if ($this->getBaseCommit() == self::GIT_MAGIC_ROOT_COMMIT) {
|
Improve git behavior in the zero- and one- commit case
Summary:
Git works completely differently for commits zero and one than for 2..N so add
more special casing to handle them. See:
- {T206}
- {T596}
The getCommitRange() block is also fatal land, although I wasn't able to reach
it. I'll follow up with @s on T596.
Test Plan:
- Created a new, empty repository ("mkdir x; cd x; git init").
- Ran "arc lint", "arc unit", "arc diff" against it with no commits (the first
two work, the third fails helpfully).
- Made an initial commit.
- Ran "arc lint", "arc unit", "arc diff" against it (all work correctly).
Reviewers: btrahan, jungejason, aran
Reviewed By: aran
CC: s, aran
Differential Revision: 1142
2011-11-30 09:15:37 -08:00
|
|
|
// One commit.
|
|
|
|
$against = 'HEAD';
|
|
|
|
} else {
|
2012-05-11 13:52:14 -07:00
|
|
|
|
|
|
|
// 2..N commits. We include commits reachable from HEAD which are
|
2014-06-11 16:35:50 -07:00
|
|
|
// not reachable from the base commit; this is consistent with user
|
|
|
|
// expectations even though it is not actually the diff range.
|
2012-05-11 13:52:14 -07:00
|
|
|
// Particularly:
|
|
|
|
//
|
|
|
|
// |
|
|
|
|
// D <----- master branch
|
|
|
|
// |
|
|
|
|
// C Y <- feature branch
|
|
|
|
// | /|
|
|
|
|
// B X
|
|
|
|
// | /
|
|
|
|
// A
|
|
|
|
// |
|
|
|
|
//
|
|
|
|
// If "A, B, C, D" are master, and the user is at Y, when they run
|
|
|
|
// "arc diff B" they want (and get) a diff of B vs Y, but they think about
|
|
|
|
// this as being the commits X and Y. If we log "B..Y", we only show
|
|
|
|
// Y. With "Y --not B", we show X and Y.
|
|
|
|
|
2014-06-11 16:35:50 -07:00
|
|
|
|
|
|
|
if ($this->symbolicHeadCommit !== null) {
|
|
|
|
$base_commit = $this->getBaseCommit();
|
|
|
|
$resolved_base = $this->resolveCommit($base_commit);
|
|
|
|
|
|
|
|
$head_commit = $this->symbolicHeadCommit;
|
|
|
|
$resolved_head = $this->getHeadCommit();
|
|
|
|
|
|
|
|
if (!$this->isDescendant($resolved_head, $resolved_base)) {
|
|
|
|
// NOTE: Since the base commit will have been resolved as the
|
|
|
|
// merge-base of the specified base and the specified HEAD, we can't
|
|
|
|
// easily tell exactly what's wrong with the range.
|
|
|
|
|
|
|
|
// For example, `arc diff HEAD --head HEAD^^^` is invalid because it
|
|
|
|
// is reversed, but resolving the commit "HEAD" will compute its
|
|
|
|
// merge-base with "HEAD^^^", which is "HEAD^^^", so the range will
|
|
|
|
// appear empty.
|
|
|
|
|
|
|
|
throw new ArcanistUsageException(
|
|
|
|
pht(
|
|
|
|
'The specified commit range is empty, backward or invalid: the '.
|
|
|
|
'base (%s) is not an ancestor of the head (%s). You can not '.
|
|
|
|
'diff an empty or reversed commit range.',
|
|
|
|
$base_commit,
|
|
|
|
$head_commit));
|
|
|
|
}
|
2014-06-11 14:37:01 -07:00
|
|
|
}
|
|
|
|
|
2014-06-11 16:35:50 -07:00
|
|
|
$against = csprintf(
|
|
|
|
'%s --not %s',
|
|
|
|
$this->getHeadCommit(),
|
|
|
|
$this->getBaseCommit());
|
Improve git behavior in the zero- and one- commit case
Summary:
Git works completely differently for commits zero and one than for 2..N so add
more special casing to handle them. See:
- {T206}
- {T596}
The getCommitRange() block is also fatal land, although I wasn't able to reach
it. I'll follow up with @s on T596.
Test Plan:
- Created a new, empty repository ("mkdir x; cd x; git init").
- Ran "arc lint", "arc unit", "arc diff" against it with no commits (the first
two work, the third fails helpfully).
- Made an initial commit.
- Ran "arc lint", "arc unit", "arc diff" against it (all work correctly).
Reviewers: btrahan, jungejason, aran
Reviewed By: aran
CC: s, aran
Differential Revision: 1142
2011-11-30 09:15:37 -08:00
|
|
|
}
|
|
|
|
|
Fix escaping of "git log ---format" command on Windows
Summary:
On Windows, the PHP function escapeshellarg() replaces '%' with ' ' (space). This is apparently because there is no safe way to escape % inside of strings.
cmd.exe does use "^" as an escape character, so I think replacing "xyz" with "^x^y^z" might work for arbitrary strings (maybe?), or at least some subset of strings, but I don't know cmd.exe well enough to make that call without being concerned I'm introducing a security issue.
Although this patch is dumb, it's certinaly safe, and can only do something wrong if the user has environmental variables like H, P, T, or x01, in which case they're sort of asking for it.
cmd.exe also truncates output on \0, so use \1 as a delimiter instead.
Seriously it's like this thing was written in 1982 and never ever changed.
Test Plan: Created D1783 successfully after applying this patch.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T124
Differential Revision: https://secure.phabricator.com/D1785
2012-03-05 13:22:41 -08:00
|
|
|
// NOTE: Windows escaping of "%" symbols apparently is inherently broken;
|
2014-07-13 00:45:38 +10:00
|
|
|
// when passed through escapeshellarg() they are replaced with spaces.
|
Fix escaping of "git log ---format" command on Windows
Summary:
On Windows, the PHP function escapeshellarg() replaces '%' with ' ' (space). This is apparently because there is no safe way to escape % inside of strings.
cmd.exe does use "^" as an escape character, so I think replacing "xyz" with "^x^y^z" might work for arbitrary strings (maybe?), or at least some subset of strings, but I don't know cmd.exe well enough to make that call without being concerned I'm introducing a security issue.
Although this patch is dumb, it's certinaly safe, and can only do something wrong if the user has environmental variables like H, P, T, or x01, in which case they're sort of asking for it.
cmd.exe also truncates output on \0, so use \1 as a delimiter instead.
Seriously it's like this thing was written in 1982 and never ever changed.
Test Plan: Created D1783 successfully after applying this patch.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T124
Differential Revision: https://secure.phabricator.com/D1785
2012-03-05 13:22:41 -08:00
|
|
|
|
|
|
|
// TODO: Learn how cmd.exe works and find some clever workaround?
|
|
|
|
|
|
|
|
// NOTE: If we use "%x00", output is truncated in Windows.
|
|
|
|
|
2012-03-02 16:47:34 -08:00
|
|
|
list($info) = $this->execxLocal(
|
Fix escaping of "git log ---format" command on Windows
Summary:
On Windows, the PHP function escapeshellarg() replaces '%' with ' ' (space). This is apparently because there is no safe way to escape % inside of strings.
cmd.exe does use "^" as an escape character, so I think replacing "xyz" with "^x^y^z" might work for arbitrary strings (maybe?), or at least some subset of strings, but I don't know cmd.exe well enough to make that call without being concerned I'm introducing a security issue.
Although this patch is dumb, it's certinaly safe, and can only do something wrong if the user has environmental variables like H, P, T, or x01, in which case they're sort of asking for it.
cmd.exe also truncates output on \0, so use \1 as a delimiter instead.
Seriously it's like this thing was written in 1982 and never ever changed.
Test Plan: Created D1783 successfully after applying this patch.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T124
Differential Revision: https://secure.phabricator.com/D1785
2012-03-05 13:22:41 -08:00
|
|
|
phutil_is_windows()
|
2012-05-11 13:52:14 -07:00
|
|
|
? 'log %C --format=%C --'
|
|
|
|
: 'log %C --format=%s --',
|
Improve git behavior in the zero- and one- commit case
Summary:
Git works completely differently for commits zero and one than for 2..N so add
more special casing to handle them. See:
- {T206}
- {T596}
The getCommitRange() block is also fatal land, although I wasn't able to reach
it. I'll follow up with @s on T596.
Test Plan:
- Created a new, empty repository ("mkdir x; cd x; git init").
- Ran "arc lint", "arc unit", "arc diff" against it with no commits (the first
two work, the third fails helpfully).
- Made an initial commit.
- Ran "arc lint", "arc unit", "arc diff" against it (all work correctly).
Reviewers: btrahan, jungejason, aran
Reviewed By: aran
CC: s, aran
Differential Revision: 1142
2011-11-30 09:15:37 -08:00
|
|
|
$against,
|
2012-05-09 15:56:16 -07:00
|
|
|
// NOTE: "%B" is somewhat new, use "%s%n%n%b" instead.
|
2013-02-05 20:11:20 -08:00
|
|
|
'%H%x01%T%x01%P%x01%at%x01%an%x01%aE%x01%s%x01%s%n%n%b%x02');
|
2011-08-23 18:48:55 -07:00
|
|
|
|
|
|
|
$commits = array();
|
|
|
|
|
2012-05-09 15:56:16 -07:00
|
|
|
$info = trim($info, " \n\2");
|
2012-05-11 06:07:33 -07:00
|
|
|
if (!strlen($info)) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2012-05-09 15:56:16 -07:00
|
|
|
$info = explode("\2", $info);
|
2011-08-23 18:48:55 -07:00
|
|
|
foreach ($info as $line) {
|
2013-02-05 20:11:20 -08:00
|
|
|
list($commit, $tree, $parents, $time, $author, $author_email,
|
|
|
|
$title, $message) = explode("\1", trim($line), 8);
|
2012-05-09 15:56:16 -07:00
|
|
|
$message = rtrim($message);
|
2011-08-23 18:48:55 -07:00
|
|
|
|
2012-05-09 15:56:16 -07:00
|
|
|
$commits[$commit] = array(
|
2011-08-23 18:48:55 -07:00
|
|
|
'commit' => $commit,
|
|
|
|
'tree' => $tree,
|
|
|
|
'parents' => array_filter(explode(' ', $parents)),
|
|
|
|
'time' => $time,
|
|
|
|
'author' => $author,
|
|
|
|
'summary' => $title,
|
2012-05-09 15:56:16 -07:00
|
|
|
'message' => $message,
|
2013-02-05 20:11:20 -08:00
|
|
|
'authorEmail' => $author_email,
|
2011-08-23 18:48:55 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $commits;
|
|
|
|
}
|
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
protected function buildBaseCommit($symbolic_commit) {
|
|
|
|
if ($symbolic_commit !== null) {
|
2015-04-14 06:29:07 +10:00
|
|
|
if ($symbolic_commit == self::GIT_MAGIC_ROOT_COMMIT) {
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht('you explicitly specified the empty tree.'));
|
2012-12-17 12:54:08 -08:00
|
|
|
return $symbolic_commit;
|
|
|
|
}
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
list($err, $merge_base) = $this->execManualLocal(
|
2014-06-11 14:37:01 -07:00
|
|
|
'merge-base %s %s',
|
|
|
|
$symbolic_commit,
|
|
|
|
$this->getHeadCommit());
|
2011-01-09 15:22:25 -08:00
|
|
|
if ($err) {
|
2012-12-17 12:54:08 -08:00
|
|
|
throw new ArcanistUsageException(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"Unable to find any git commit named '%s' in this repository.",
|
|
|
|
$symbolic_commit));
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
}
|
|
|
|
|
2014-06-11 16:35:50 -07:00
|
|
|
if ($this->symbolicHeadCommit === null) {
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the merge-base of the explicitly specified base commit ".
|
|
|
|
"'%s' and HEAD.",
|
|
|
|
$symbolic_commit));
|
2014-06-11 16:35:50 -07:00
|
|
|
} else {
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the merge-base of the explicitly specified base commit ".
|
|
|
|
"'%s' and the explicitly specified head commit '%s'.",
|
|
|
|
$symbolic_commit,
|
|
|
|
$this->symbolicHeadCommit));
|
2014-06-11 16:35:50 -07:00
|
|
|
}
|
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
return trim($merge_base);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect zero-commit or one-commit repositories. There is only one
|
|
|
|
// relative-commit value that makes any sense in these repositories: the
|
|
|
|
// empty tree.
|
|
|
|
list($err) = $this->execManualLocal('rev-parse --verify HEAD^');
|
|
|
|
if ($err) {
|
|
|
|
list($err) = $this->execManualLocal('rev-parse --verify HEAD');
|
|
|
|
if ($err) {
|
|
|
|
$this->repositoryHasNoCommits = true;
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
}
|
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
if ($this->repositoryHasNoCommits) {
|
2015-05-13 18:05:15 +10:00
|
|
|
$this->setBaseCommitExplanation(pht('the repository has no commits.'));
|
2012-12-17 12:54:08 -08:00
|
|
|
} else {
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht('the repository has only one commit.'));
|
2012-05-06 17:03:32 -07:00
|
|
|
}
|
2012-04-10 15:33:31 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
return self::GIT_MAGIC_ROOT_COMMIT;
|
|
|
|
}
|
2012-04-10 15:33:31 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
if ($this->getBaseCommitArgumentRules() ||
|
2013-10-18 16:10:06 -07:00
|
|
|
$this->getConfigurationManager()->getConfigFromAnySource('base')) {
|
2012-12-17 12:54:08 -08:00
|
|
|
$base = $this->resolveBaseCommit();
|
|
|
|
if (!$base) {
|
|
|
|
throw new ArcanistUsageException(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"None of the rules in your 'base' configuration matched a valid ".
|
|
|
|
"commit. Adjust rules or specify which commit you want to use ".
|
|
|
|
"explicitly."));
|
2012-04-10 15:33:31 -07:00
|
|
|
}
|
2012-12-17 12:54:08 -08:00
|
|
|
return $base;
|
|
|
|
}
|
2012-04-10 15:33:31 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
$do_write = false;
|
|
|
|
$default_relative = null;
|
|
|
|
$working_copy = $this->getWorkingCopyIdentity();
|
|
|
|
if ($working_copy) {
|
2013-10-22 15:34:06 -07:00
|
|
|
$default_relative = $working_copy->getProjectConfig(
|
2012-12-17 12:54:08 -08:00
|
|
|
'git.default-relative-commit');
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the merge-base of '%s' and HEAD, as specified in '%s' in ".
|
|
|
|
"'%s'. This setting overrides other settings.",
|
|
|
|
$default_relative,
|
|
|
|
'git.default-relative-commit',
|
|
|
|
'.arcconfig'));
|
2012-12-17 12:54:08 -08:00
|
|
|
}
|
2012-04-03 16:06:43 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
if (!$default_relative) {
|
|
|
|
list($err, $upstream) = $this->execManualLocal(
|
2014-07-10 07:56:35 -07:00
|
|
|
'rev-parse --abbrev-ref --symbolic-full-name %s',
|
|
|
|
'@{upstream}');
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
if (!$err) {
|
|
|
|
$default_relative = trim($upstream);
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the merge-base of '%s' (the Git upstream ".
|
|
|
|
"of the current branch) HEAD.",
|
|
|
|
$default_relative));
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
}
|
2012-12-17 12:54:08 -08:00
|
|
|
}
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
if (!$default_relative) {
|
|
|
|
$default_relative = $this->readScratchFile('default-relative-commit');
|
|
|
|
$default_relative = trim($default_relative);
|
|
|
|
if ($default_relative) {
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the merge-base of '%s' and HEAD, as specified in '%s'.",
|
|
|
|
$default_relative,
|
|
|
|
'.git/arc/default-relative-commit'));
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
}
|
2012-12-17 12:54:08 -08:00
|
|
|
}
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
if (!$default_relative) {
|
|
|
|
|
|
|
|
// TODO: Remove the history lesson soon.
|
|
|
|
|
|
|
|
echo phutil_console_format(
|
2015-05-22 17:09:55 +10:00
|
|
|
"<bg:green>** %s **</bg>\n\n",
|
|
|
|
pht('Select a Default Commit Range'));
|
2012-12-17 12:54:08 -08:00
|
|
|
echo phutil_console_wrap(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"You're running a command which operates on a range of revisions ".
|
|
|
|
"(usually, from some revision to HEAD) but have not specified the ".
|
|
|
|
"revision that should determine the start of the range.\n\n".
|
|
|
|
"Previously, arc assumed you meant '%s' when you did not specify ".
|
|
|
|
"a start revision, but this behavior does not make much sense in ".
|
|
|
|
"most workflows outside of Facebook's historic %s workflow.\n\n".
|
|
|
|
"arc no longer assumes '%s'. You must specify a relative commit ".
|
|
|
|
"explicitly when you invoke a command (e.g., `%s`, not just `%s`) ".
|
|
|
|
"or select a default for this working copy.\n\nIn most cases, the ".
|
|
|
|
"best default is '%s'. You can also select '%s' to preserve the ".
|
|
|
|
"old behavior, or some other remote or branch. But you almost ".
|
|
|
|
"certainly want to select 'origin/master'.\n\n".
|
|
|
|
"(Technically: the merge-base of the selected revision and HEAD is ".
|
|
|
|
"used to determine the start of the commit range.)",
|
|
|
|
'HEAD^',
|
|
|
|
'git-svn',
|
|
|
|
'HEAD^',
|
|
|
|
'arc diff HEAD^',
|
|
|
|
'arc diff',
|
|
|
|
'origin/master',
|
|
|
|
'HEAD^'));
|
|
|
|
|
|
|
|
$prompt = pht('What default do you want to use? [origin/master]');
|
2012-12-17 12:54:08 -08:00
|
|
|
$default = phutil_console_prompt($prompt);
|
|
|
|
|
|
|
|
if (!strlen(trim($default))) {
|
|
|
|
$default = 'origin/master';
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
$default_relative = $default;
|
|
|
|
$do_write = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
list($object_type) = $this->execxLocal(
|
|
|
|
'cat-file -t %s',
|
|
|
|
$default_relative);
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
if (trim($object_type) !== 'commit') {
|
|
|
|
throw new Exception(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"Relative commit '%s' is not the name of a commit!",
|
|
|
|
$default_relative));
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
Revenge of the git relative commit default
Summary:
The default of "arc diff" to "arc diff HEAD^" in git is universally confusing to everyone not at Facebook.
Drive the default with configuration instead. Even at Facebook, "origin/trunk" (or whatever) is probably a better default than "HEAD^".
See D863 for the last attempt at this.
NOTE: This is contentious!
Test Plan: Ran "arc diff", got prompted to set a default. Ran "arc diff" from a zero-commit repo, got sensible behavior
Reviewers: btrahan, vrana, nh, jungejason, tuomaspelkonen
Reviewed By: btrahan
CC: aran, epriestley, zeeg, davidreuss
Maniphest Tasks: T651
Differential Revision: https://secure.phabricator.com/D1861
2012-04-02 16:52:20 -07:00
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
if ($do_write) {
|
|
|
|
// Don't perform this write until we've verified that the object is a
|
|
|
|
// valid commit name.
|
|
|
|
$this->writeScratchFile('default-relative-commit', $default_relative);
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the merge-base of '%s' and HEAD, as you just specified.",
|
|
|
|
$default_relative));
|
2012-12-17 12:54:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
list($merge_base) = $this->execxLocal(
|
|
|
|
'merge-base %s HEAD',
|
|
|
|
$default_relative);
|
|
|
|
|
|
|
|
return trim($merge_base);
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
2014-06-11 14:37:01 -07:00
|
|
|
public function getHeadCommit() {
|
|
|
|
if ($this->resolvedHeadCommit === null) {
|
2014-06-11 16:35:50 -07:00
|
|
|
$this->resolvedHeadCommit = $this->resolveCommit(
|
|
|
|
coalesce($this->symbolicHeadCommit, 'HEAD'));
|
2014-06-11 14:37:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->resolvedHeadCommit;
|
|
|
|
}
|
|
|
|
|
2015-04-14 06:29:07 +10:00
|
|
|
public function setHeadCommit($symbolic_commit) {
|
2014-06-11 14:37:01 -07:00
|
|
|
$this->symbolicHeadCommit = $symbolic_commit;
|
|
|
|
$this->reloadCommitRange();
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Translates a symbolic commit (like "HEAD^") to a commit identifier.
|
|
|
|
* @param string_symbol commit.
|
|
|
|
* @return string the commit SHA.
|
|
|
|
*/
|
|
|
|
private function resolveCommit($symbolic_commit) {
|
|
|
|
list($err, $commit_hash) = $this->execManualLocal(
|
|
|
|
'rev-parse %s',
|
|
|
|
$symbolic_commit);
|
|
|
|
|
|
|
|
if ($err) {
|
|
|
|
throw new ArcanistUsageException(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"Unable to find any git commit named '%s' in this repository.",
|
|
|
|
$symbolic_commit));
|
2014-06-11 14:37:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return trim($commit_hash);
|
|
|
|
}
|
|
|
|
|
2012-11-15 15:47:43 -08:00
|
|
|
private function getDiffFullOptions($detect_moves_and_renames = true) {
|
|
|
|
$options = array(
|
2011-05-29 10:45:18 -07:00
|
|
|
self::getDiffBaseOptions(),
|
2011-01-09 15:22:25 -08:00
|
|
|
'--no-color',
|
|
|
|
'--src-prefix=a/',
|
|
|
|
'--dst-prefix=b/',
|
|
|
|
'-U'.$this->getDiffLinesOfContext(),
|
|
|
|
);
|
2012-04-30 16:47:12 -07:00
|
|
|
|
|
|
|
if ($detect_moves_and_renames) {
|
2012-11-15 15:47:43 -08:00
|
|
|
$options[] = '-M';
|
|
|
|
$options[] = '-C';
|
2012-04-30 16:47:12 -07:00
|
|
|
}
|
|
|
|
|
2012-11-15 15:47:43 -08:00
|
|
|
return implode(' ', $options);
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
2011-05-29 10:45:18 -07:00
|
|
|
private function getDiffBaseOptions() {
|
|
|
|
$options = array(
|
|
|
|
// Disable external diff drivers, like graphical differs, since Arcanist
|
|
|
|
// needs to capture the diff text.
|
|
|
|
'--no-ext-diff',
|
|
|
|
// Disable textconv so we treat binary files as binary, even if they have
|
|
|
|
// an alternative textual representation. TODO: Ideally, Differential
|
|
|
|
// would ship up the binaries for 'arc patch' but display the textconv
|
|
|
|
// output in the visual diff.
|
|
|
|
'--no-textconv',
|
|
|
|
);
|
|
|
|
return implode(' ', $options);
|
|
|
|
}
|
|
|
|
|
2014-06-11 14:37:01 -07:00
|
|
|
/**
|
|
|
|
* @param the base revision
|
|
|
|
* @param head revision. If this is null, the generated diff will include the
|
|
|
|
* working copy
|
|
|
|
*/
|
2014-07-11 10:45:11 -07:00
|
|
|
public function getFullGitDiff($base, $head = null) {
|
2012-11-15 15:47:43 -08:00
|
|
|
$options = $this->getDiffFullOptions();
|
2014-06-11 14:37:01 -07:00
|
|
|
|
2014-07-11 10:45:11 -07:00
|
|
|
if ($head !== null) {
|
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
"diff {$options} %s %s --",
|
|
|
|
$base,
|
|
|
|
$head);
|
|
|
|
} else {
|
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
"diff {$options} %s --",
|
|
|
|
$base);
|
2014-06-11 14:37:01 -07:00
|
|
|
}
|
|
|
|
|
2011-01-09 15:22:25 -08:00
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
2012-04-30 16:47:12 -07:00
|
|
|
/**
|
|
|
|
* @param string Path to generate a diff for.
|
|
|
|
* @param bool If true, detect moves and renames. Otherwise, ignore
|
|
|
|
* moves/renames; this is useful because it prompts git to
|
|
|
|
* generate real diff text.
|
|
|
|
*/
|
|
|
|
public function getRawDiffText($path, $detect_moves_and_renames = true) {
|
2012-11-15 15:47:43 -08:00
|
|
|
$options = $this->getDiffFullOptions($detect_moves_and_renames);
|
2012-03-02 16:47:34 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
2012-11-15 15:47:43 -08:00
|
|
|
"diff {$options} %s -- %s",
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->getBaseCommit(),
|
2011-01-09 15:22:25 -08:00
|
|
|
$path);
|
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
private function getBranchNameFromRef($ref) {
|
|
|
|
$count = 0;
|
|
|
|
$branch = preg_replace('/^refs\/heads\//', '', $ref, 1, $count);
|
|
|
|
if ($count !== 1) {
|
|
|
|
return null;
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
Allow `arc` to identify repositories without "project_id"
Summary:
Ref T4343. Continues the process of reducing the prominence of Arcanist Projects. Primarily:
- Query Phabricator to identify the working copy based on explicit configuration, or guess based on heuristics.
- Enhance `arc which` to explain the process to the user.
- The `project_id` key is no longer required in `.arcconfig`.
Minor/cleanup changes:
- Rename `project_id` to `project.name` (consistency, clarity).
- Rename `conduit_uri` to `phabricator.uri` (consistency, clairty).
- These both need documentation updates.
- Add `repository.callsign` to explicitly bind to a repository.
- Updated `.arcconfig` for the new values.
- Fix a unit test which broke a while ago when we fixed a rare definition of "unstaged".
- Make `getRepositoryUUID()` generic so we can get rid of one `instanceof`.
Test Plan:
- Ran `arc which`.
- Ran `arc diff`.
- This doesn't really change anything, so the only real risk is version compatibility breaks. This //does// introduce such a break, but the window is very narrow: if you upgrade `arc` after this commit, and try to diff against a Phabricator which was updated after yesterday (D8068) but before D8072 lands, the lookup will work so we'll add `repositoryPHID` to the `differential.creatediff` call, but it won't exist in Phabricator yet. This window is so narrow that I'm not going to try to fix it, as I'd guess there is a significant chance that no users will be affected. I don't see a clever way to fix it that doesn't involve a lot of work, either.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4343
Differential Revision: https://secure.phabricator.com/D8073
2014-01-26 15:31:30 -08:00
|
|
|
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
return $branch;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getBranchName() {
|
|
|
|
list($err, $stdout, $stderr) = $this->execManualLocal(
|
|
|
|
'symbolic-ref --quiet HEAD');
|
|
|
|
|
|
|
|
if ($err === 0) {
|
|
|
|
// We expect the branch name to come qualified with a refs/heads/ prefix.
|
|
|
|
// Verify this, and strip it.
|
|
|
|
$ref = rtrim($stdout);
|
|
|
|
$branch = $this->getBranchNameFromRef($ref);
|
|
|
|
if (!$branch) {
|
|
|
|
throw new Exception(
|
|
|
|
pht('Failed to parse %s output!', 'git symbolic-ref'));
|
|
|
|
}
|
|
|
|
return $branch;
|
|
|
|
} else if ($err === 1) {
|
|
|
|
// Exit status 1 with --quiet indicates that HEAD is detached.
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
throw new Exception(
|
|
|
|
pht('Command %s failed: %s', 'git symbolic-ref', $stderr));
|
|
|
|
}
|
Allow `arc` to identify repositories without "project_id"
Summary:
Ref T4343. Continues the process of reducing the prominence of Arcanist Projects. Primarily:
- Query Phabricator to identify the working copy based on explicit configuration, or guess based on heuristics.
- Enhance `arc which` to explain the process to the user.
- The `project_id` key is no longer required in `.arcconfig`.
Minor/cleanup changes:
- Rename `project_id` to `project.name` (consistency, clarity).
- Rename `conduit_uri` to `phabricator.uri` (consistency, clairty).
- These both need documentation updates.
- Add `repository.callsign` to explicitly bind to a repository.
- Updated `.arcconfig` for the new values.
- Fix a unit test which broke a while ago when we fixed a rare definition of "unstaged".
- Make `getRepositoryUUID()` generic so we can get rid of one `instanceof`.
Test Plan:
- Ran `arc which`.
- Ran `arc diff`.
- This doesn't really change anything, so the only real risk is version compatibility breaks. This //does// introduce such a break, but the window is very narrow: if you upgrade `arc` after this commit, and try to diff against a Phabricator which was updated after yesterday (D8068) but before D8072 lands, the lookup will work so we'll add `repositoryPHID` to the `differential.creatediff` call, but it won't exist in Phabricator yet. This window is so narrow that I'm not going to try to fix it, as I'd guess there is a significant chance that no users will be affected. I don't see a clever way to fix it that doesn't involve a lot of work, either.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4343
Differential Revision: https://secure.phabricator.com/D8073
2014-01-26 15:31:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getRemoteURI() {
|
2015-08-24 04:51:03 -07:00
|
|
|
list($stdout) = $this->execxLocal('ls-remote --get-url origin');
|
Allow `arc` to identify repositories without "project_id"
Summary:
Ref T4343. Continues the process of reducing the prominence of Arcanist Projects. Primarily:
- Query Phabricator to identify the working copy based on explicit configuration, or guess based on heuristics.
- Enhance `arc which` to explain the process to the user.
- The `project_id` key is no longer required in `.arcconfig`.
Minor/cleanup changes:
- Rename `project_id` to `project.name` (consistency, clarity).
- Rename `conduit_uri` to `phabricator.uri` (consistency, clairty).
- These both need documentation updates.
- Add `repository.callsign` to explicitly bind to a repository.
- Updated `.arcconfig` for the new values.
- Fix a unit test which broke a while ago when we fixed a rare definition of "unstaged".
- Make `getRepositoryUUID()` generic so we can get rid of one `instanceof`.
Test Plan:
- Ran `arc which`.
- Ran `arc diff`.
- This doesn't really change anything, so the only real risk is version compatibility breaks. This //does// introduce such a break, but the window is very narrow: if you upgrade `arc` after this commit, and try to diff against a Phabricator which was updated after yesterday (D8068) but before D8072 lands, the lookup will work so we'll add `repositoryPHID` to the `differential.creatediff` call, but it won't exist in Phabricator yet. This window is so narrow that I'm not going to try to fix it, as I'd guess there is a significant chance that no users will be affected. I don't see a clever way to fix it that doesn't involve a lot of work, either.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4343
Differential Revision: https://secure.phabricator.com/D8073
2014-01-26 15:31:30 -08:00
|
|
|
|
2015-08-24 04:51:03 -07:00
|
|
|
$uri = rtrim($stdout);
|
|
|
|
if ($uri === 'origin') {
|
|
|
|
return null;
|
Allow `arc` to identify repositories without "project_id"
Summary:
Ref T4343. Continues the process of reducing the prominence of Arcanist Projects. Primarily:
- Query Phabricator to identify the working copy based on explicit configuration, or guess based on heuristics.
- Enhance `arc which` to explain the process to the user.
- The `project_id` key is no longer required in `.arcconfig`.
Minor/cleanup changes:
- Rename `project_id` to `project.name` (consistency, clarity).
- Rename `conduit_uri` to `phabricator.uri` (consistency, clairty).
- These both need documentation updates.
- Add `repository.callsign` to explicitly bind to a repository.
- Updated `.arcconfig` for the new values.
- Fix a unit test which broke a while ago when we fixed a rare definition of "unstaged".
- Make `getRepositoryUUID()` generic so we can get rid of one `instanceof`.
Test Plan:
- Ran `arc which`.
- Ran `arc diff`.
- This doesn't really change anything, so the only real risk is version compatibility breaks. This //does// introduce such a break, but the window is very narrow: if you upgrade `arc` after this commit, and try to diff against a Phabricator which was updated after yesterday (D8068) but before D8072 lands, the lookup will work so we'll add `repositoryPHID` to the `differential.creatediff` call, but it won't exist in Phabricator yet. This window is so narrow that I'm not going to try to fix it, as I'd guess there is a significant chance that no users will be affected. I don't see a clever way to fix it that doesn't involve a lot of work, either.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4343
Differential Revision: https://secure.phabricator.com/D8073
2014-01-26 15:31:30 -08:00
|
|
|
}
|
|
|
|
|
2015-08-24 04:51:03 -07:00
|
|
|
return $uri;
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getSourceControlPath() {
|
|
|
|
// TODO: Try to get something useful here.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getGitCommitLog() {
|
2012-12-17 12:54:08 -08:00
|
|
|
$relative = $this->getBaseCommit();
|
Improve git behavior in the zero- and one- commit case
Summary:
Git works completely differently for commits zero and one than for 2..N so add
more special casing to handle them. See:
- {T206}
- {T596}
The getCommitRange() block is also fatal land, although I wasn't able to reach
it. I'll follow up with @s on T596.
Test Plan:
- Created a new, empty repository ("mkdir x; cd x; git init").
- Ran "arc lint", "arc unit", "arc diff" against it with no commits (the first
two work, the third fails helpfully).
- Made an initial commit.
- Ran "arc lint", "arc unit", "arc diff" against it (all work correctly).
Reviewers: btrahan, jungejason, aran
Reviewed By: aran
CC: s, aran
Differential Revision: 1142
2011-11-30 09:15:37 -08:00
|
|
|
if ($this->repositoryHasNoCommits) {
|
|
|
|
// No commits yet.
|
|
|
|
return '';
|
|
|
|
} else if ($relative == self::GIT_MAGIC_ROOT_COMMIT) {
|
|
|
|
// First commit.
|
2012-04-30 14:14:23 -07:00
|
|
|
list($stdout) = $this->execxLocal(
|
2012-04-30 17:28:04 -07:00
|
|
|
'log --format=medium HEAD');
|
2011-01-09 15:22:25 -08:00
|
|
|
} else {
|
Improve git behavior in the zero- and one- commit case
Summary:
Git works completely differently for commits zero and one than for 2..N so add
more special casing to handle them. See:
- {T206}
- {T596}
The getCommitRange() block is also fatal land, although I wasn't able to reach
it. I'll follow up with @s on T596.
Test Plan:
- Created a new, empty repository ("mkdir x; cd x; git init").
- Ran "arc lint", "arc unit", "arc diff" against it with no commits (the first
two work, the third fails helpfully).
- Made an initial commit.
- Ran "arc lint", "arc unit", "arc diff" against it (all work correctly).
Reviewers: btrahan, jungejason, aran
Reviewed By: aran
CC: s, aran
Differential Revision: 1142
2011-11-30 09:15:37 -08:00
|
|
|
// 2..N commits.
|
2012-03-02 16:47:34 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
2014-06-11 14:37:01 -07:00
|
|
|
'log --first-parent --format=medium %s..%s',
|
|
|
|
$this->getBaseCommit(),
|
|
|
|
$this->getHeadCommit());
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getGitHistoryLog() {
|
2012-03-02 16:47:34 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
'log --format=medium -n%d %s',
|
2011-01-09 15:22:25 -08:00
|
|
|
self::SEARCH_LENGTH_FOR_PARENT_REVISIONS,
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->getBaseCommit());
|
2011-01-09 15:22:25 -08:00
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getSourceControlBaseRevision() {
|
2012-03-02 16:47:34 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
'rev-parse %s',
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->getBaseCommit());
|
2011-01-09 15:22:25 -08:00
|
|
|
return rtrim($stdout, "\n");
|
|
|
|
}
|
|
|
|
|
2012-03-09 14:40:47 -08:00
|
|
|
public function getCanonicalRevisionName($string) {
|
2012-09-21 14:02:27 -07:00
|
|
|
$match = null;
|
|
|
|
if (preg_match('/@([0-9]+)$/', $string, $match)) {
|
2013-05-14 11:00:56 -07:00
|
|
|
$stdout = $this->getHashFromFromSVNRevisionNumber($match[1]);
|
2012-09-21 14:02:27 -07:00
|
|
|
} else {
|
|
|
|
list($stdout) = $this->execxLocal(
|
2014-08-21 16:07:00 -07:00
|
|
|
phutil_is_windows()
|
|
|
|
? 'show -s --format=%C %s --'
|
|
|
|
: 'show -s --format=%s %s --',
|
2012-09-21 14:02:27 -07:00
|
|
|
'%H',
|
|
|
|
$string);
|
|
|
|
}
|
2012-03-09 14:40:47 -08:00
|
|
|
return rtrim($stdout);
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
2013-05-14 11:00:56 -07:00
|
|
|
private function executeSVNFindRev($input, $vcs) {
|
|
|
|
$match = array();
|
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
'svn find-rev %s',
|
|
|
|
$input);
|
|
|
|
if (!$stdout) {
|
2014-07-09 09:12:13 +10:00
|
|
|
throw new ArcanistUsageException(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
'Cannot find the %s equivalent of %s.',
|
|
|
|
$vcs,
|
|
|
|
$input));
|
2013-05-14 11:00:56 -07:00
|
|
|
}
|
|
|
|
// When git performs a partial-rebuild during svn
|
|
|
|
// look-up, we need to parse the final line
|
|
|
|
$lines = explode("\n", $stdout);
|
|
|
|
$stdout = $lines[count($lines) - 2];
|
|
|
|
return rtrim($stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert svn revision number to git hash
|
|
|
|
public function getHashFromFromSVNRevisionNumber($revision_id) {
|
2014-05-23 13:53:05 -07:00
|
|
|
return $this->executeSVNFindRev('r'.$revision_id, 'Git');
|
2013-05-14 11:00:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Convert a git hash to svn revision number
|
|
|
|
public function getSVNRevisionNumberFromHash($hash) {
|
2014-05-23 13:53:05 -07:00
|
|
|
return $this->executeSVNFindRev($hash, 'SVN');
|
2013-05-14 11:00:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-17 12:53:28 -08:00
|
|
|
protected function buildUncommittedStatus() {
|
|
|
|
$diff_options = $this->getDiffBaseOptions();
|
2011-07-05 11:06:46 -07:00
|
|
|
|
2012-12-17 12:53:28 -08:00
|
|
|
if ($this->repositoryHasNoCommits) {
|
|
|
|
$diff_base = self::GIT_MAGIC_ROOT_COMMIT;
|
|
|
|
} else {
|
|
|
|
$diff_base = 'HEAD';
|
|
|
|
}
|
2011-07-05 11:06:46 -07:00
|
|
|
|
2012-12-17 12:53:28 -08:00
|
|
|
// Find uncommitted changes.
|
|
|
|
$uncommitted_future = $this->buildLocalFuture(
|
|
|
|
array(
|
|
|
|
'diff %C --raw %s --',
|
|
|
|
$diff_options,
|
|
|
|
$diff_base,
|
|
|
|
));
|
|
|
|
|
|
|
|
$untracked_future = $this->buildLocalFuture(
|
|
|
|
array(
|
|
|
|
'ls-files --others --exclude-standard',
|
|
|
|
));
|
|
|
|
|
|
|
|
// Unstaged changes
|
|
|
|
$unstaged_future = $this->buildLocalFuture(
|
|
|
|
array(
|
2013-11-22 21:47:15 +05:30
|
|
|
'diff-files --name-only',
|
2012-12-17 12:53:28 -08:00
|
|
|
));
|
|
|
|
|
|
|
|
$futures = array(
|
|
|
|
$uncommitted_future,
|
|
|
|
$untracked_future,
|
2014-06-18 05:33:05 -07:00
|
|
|
// NOTE: `git diff-files` races with each of these other commands
|
|
|
|
// internally, and resolves with inconsistent results if executed
|
|
|
|
// in parallel. To work around this, DO NOT run it at the same time.
|
|
|
|
// After the other commands exit, we can start the `diff-files` command.
|
2012-12-17 12:53:28 -08:00
|
|
|
);
|
2011-07-05 11:06:46 -07:00
|
|
|
|
2014-12-30 23:14:32 +11:00
|
|
|
id(new FutureIterator($futures))->resolveAll();
|
2011-07-05 11:06:46 -07:00
|
|
|
|
2014-06-18 05:33:05 -07:00
|
|
|
// We're clear to start the `git diff-files` now.
|
|
|
|
$unstaged_future->start();
|
|
|
|
|
2012-12-17 12:53:28 -08:00
|
|
|
$result = new PhutilArrayWithDefaultValue();
|
2011-01-09 15:22:25 -08:00
|
|
|
|
2012-12-17 12:53:28 -08:00
|
|
|
list($stdout) = $uncommitted_future->resolvex();
|
|
|
|
$uncommitted_files = $this->parseGitStatus($stdout);
|
|
|
|
foreach ($uncommitted_files as $path => $mask) {
|
|
|
|
$result[$path] |= ($mask | self::FLAG_UNCOMMITTED);
|
|
|
|
}
|
2011-01-09 15:22:25 -08:00
|
|
|
|
2012-12-17 12:53:28 -08:00
|
|
|
list($stdout) = $untracked_future->resolvex();
|
|
|
|
$stdout = rtrim($stdout, "\n");
|
|
|
|
if (strlen($stdout)) {
|
|
|
|
$stdout = explode("\n", $stdout);
|
|
|
|
foreach ($stdout as $path) {
|
|
|
|
$result[$path] |= self::FLAG_UNTRACKED;
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
2012-12-17 12:53:28 -08:00
|
|
|
}
|
2011-01-09 15:22:25 -08:00
|
|
|
|
2012-12-17 12:53:28 -08:00
|
|
|
list($stdout, $stderr) = $unstaged_future->resolvex();
|
|
|
|
$stdout = rtrim($stdout, "\n");
|
|
|
|
if (strlen($stdout)) {
|
|
|
|
$stdout = explode("\n", $stdout);
|
|
|
|
foreach ($stdout as $path) {
|
|
|
|
$result[$path] |= self::FLAG_UNSTAGED;
|
|
|
|
}
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
2012-12-17 12:53:28 -08:00
|
|
|
return $result->toArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildCommitRangeStatus() {
|
|
|
|
list($stdout, $stderr) = $this->execxLocal(
|
|
|
|
'diff %C --raw %s --',
|
|
|
|
$this->getDiffBaseOptions(),
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->getBaseCommit());
|
2012-12-17 12:53:28 -08:00
|
|
|
|
|
|
|
return $this->parseGitStatus($stdout);
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
2012-11-20 10:43:19 -08:00
|
|
|
public function getGitConfig($key, $default = null) {
|
2012-11-29 15:35:57 -08:00
|
|
|
list($err, $stdout) = $this->execManualLocal('config %s', $key);
|
|
|
|
if ($err) {
|
2012-11-20 10:43:19 -08:00
|
|
|
return $default;
|
|
|
|
}
|
|
|
|
return rtrim($stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAuthor() {
|
2012-11-29 15:35:57 -08:00
|
|
|
list($stdout) = $this->execxLocal('var GIT_AUTHOR_IDENT');
|
|
|
|
return preg_replace('/\s+<.*/', '', rtrim($stdout, "\n"));
|
2012-11-20 10:43:19 -08:00
|
|
|
}
|
|
|
|
|
2012-11-15 12:33:36 -08:00
|
|
|
public function addToCommit(array $paths) {
|
|
|
|
$this->execxLocal(
|
2013-01-23 13:53:30 -08:00
|
|
|
'add -A -- %Ls',
|
2012-11-15 12:33:36 -08:00
|
|
|
$paths);
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->reloadWorkingCopy();
|
|
|
|
return $this;
|
2012-11-15 12:33:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public function doCommit($message) {
|
|
|
|
$tmp_file = new TempFile();
|
|
|
|
Filesystem::writeFile($tmp_file, $message);
|
2013-05-14 13:35:42 -07:00
|
|
|
|
|
|
|
// NOTE: "--allow-empty-message" was introduced some time after 1.7.0.4,
|
|
|
|
// so we do not provide it and thus require a message.
|
|
|
|
|
2012-11-15 12:33:36 -08:00
|
|
|
$this->execxLocal(
|
2013-05-14 13:35:42 -07:00
|
|
|
'commit -F %s',
|
2012-11-15 12:33:36 -08:00
|
|
|
$tmp_file);
|
2012-12-17 12:53:28 -08:00
|
|
|
|
|
|
|
$this->reloadWorkingCopy();
|
|
|
|
|
|
|
|
return $this;
|
2012-11-15 12:33:36 -08:00
|
|
|
}
|
|
|
|
|
2013-04-16 13:32:12 -07:00
|
|
|
public function amendCommit($message = null) {
|
|
|
|
if ($message === null) {
|
|
|
|
$this->execxLocal('commit --amend --allow-empty -C HEAD');
|
|
|
|
} else {
|
|
|
|
$tmp_file = new TempFile();
|
|
|
|
Filesystem::writeFile($tmp_file, $message);
|
|
|
|
$this->execxLocal(
|
|
|
|
'commit --amend --allow-empty -F %s',
|
|
|
|
$tmp_file);
|
|
|
|
}
|
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->reloadWorkingCopy();
|
|
|
|
return $this;
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private function parseGitStatus($status, $full = false) {
|
|
|
|
static $flags = array(
|
|
|
|
'A' => self::FLAG_ADDED,
|
|
|
|
'M' => self::FLAG_MODIFIED,
|
|
|
|
'D' => self::FLAG_DELETED,
|
|
|
|
);
|
|
|
|
|
|
|
|
$status = trim($status);
|
|
|
|
$lines = array();
|
|
|
|
foreach (explode("\n", $status) as $line) {
|
|
|
|
if ($line) {
|
2012-11-08 19:29:40 -08:00
|
|
|
$lines[] = preg_split("/[ \t]/", $line, 6);
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$files = array();
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
$mask = 0;
|
|
|
|
$flag = $line[4];
|
|
|
|
$file = $line[5];
|
|
|
|
foreach ($flags as $key => $bits) {
|
|
|
|
if ($flag == $key) {
|
|
|
|
$mask |= $bits;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($full) {
|
|
|
|
$files[$file] = array(
|
|
|
|
'mask' => $mask,
|
|
|
|
'ref' => rtrim($line[3], '.'),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$files[$file] = $mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $files;
|
|
|
|
}
|
|
|
|
|
2012-11-08 23:22:29 -08:00
|
|
|
public function getAllFiles() {
|
|
|
|
$future = $this->buildLocalFuture(array('ls-files -z'));
|
|
|
|
return id(new LinesOfALargeExecFuture($future))
|
|
|
|
->setDelimiter("\0");
|
|
|
|
}
|
|
|
|
|
2012-11-08 23:26:27 -08:00
|
|
|
public function getChangedFiles($since_commit) {
|
|
|
|
list($stdout) = $this->execxLocal(
|
2012-11-21 15:57:06 -08:00
|
|
|
'diff --raw %s',
|
2012-11-08 23:26:27 -08:00
|
|
|
$since_commit);
|
2012-11-08 19:29:40 -08:00
|
|
|
return $this->parseGitStatus($stdout);
|
2012-11-08 23:26:27 -08:00
|
|
|
}
|
|
|
|
|
2011-01-09 15:22:25 -08:00
|
|
|
public function getBlame($path) {
|
2012-03-02 16:47:34 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
2015-08-25 09:36:15 -07:00
|
|
|
'blame --porcelain -w -M %s -- %s',
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->getBaseCommit(),
|
2011-01-09 15:22:25 -08:00
|
|
|
$path);
|
|
|
|
|
2015-08-25 09:36:15 -07:00
|
|
|
// the --porcelain format prints at least one header line per source line,
|
|
|
|
// then the source line prefixed by a tab character
|
|
|
|
$blame_info = preg_split('/^\t.*\n/m', rtrim($stdout));
|
2011-01-09 15:22:25 -08:00
|
|
|
|
2015-08-25 09:36:15 -07:00
|
|
|
// commit info is not repeated in these headers, so cache it
|
|
|
|
$revision_data = array();
|
2011-01-09 15:22:25 -08:00
|
|
|
|
2015-08-25 09:36:15 -07:00
|
|
|
$blame = array();
|
|
|
|
foreach ($blame_info as $line_info) {
|
|
|
|
$revision = substr($line_info, 0, 40);
|
|
|
|
$data = idx($revision_data, $revision, array());
|
|
|
|
|
|
|
|
if (empty($data)) {
|
|
|
|
$matches = array();
|
|
|
|
if (!preg_match('/^author (.*)$/m', $line_info, $matches)) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Unexpected output from %s: no author for commit %s',
|
|
|
|
'git blame',
|
|
|
|
$revision));
|
|
|
|
}
|
|
|
|
$data['author'] = $matches[1];
|
|
|
|
$data['from_first_commit'] = preg_match('/^boundary$/m', $line_info);
|
|
|
|
$revision_data[$revision] = $data;
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
2015-08-25 09:36:15 -07:00
|
|
|
// Ignore lines predating the git repository (on a boundary commit)
|
|
|
|
// rather than blaming them on the oldest diff's unfortunate author
|
|
|
|
if (!$data['from_first_commit']) {
|
|
|
|
$blame[] = array($data['author'], $revision);
|
|
|
|
}
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return $blame;
|
|
|
|
}
|
|
|
|
|
2011-01-11 13:02:38 -08:00
|
|
|
public function getOriginalFileData($path) {
|
2012-12-17 12:54:08 -08:00
|
|
|
return $this->getFileDataAtRevision($path, $this->getBaseCommit());
|
2011-01-11 13:02:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getCurrentFileData($path) {
|
|
|
|
return $this->getFileDataAtRevision($path, 'HEAD');
|
|
|
|
}
|
|
|
|
|
|
|
|
private function parseGitTree($stdout) {
|
|
|
|
$result = array();
|
|
|
|
|
|
|
|
$stdout = trim($stdout);
|
|
|
|
if (!strlen($stdout)) {
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
$lines = explode("\n", $stdout);
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
$matches = array();
|
|
|
|
$ok = preg_match(
|
2013-09-11 10:36:23 -07:00
|
|
|
'/^(\d{6}) (blob|tree|commit) ([a-z0-9]{40})[\t](.*)$/',
|
2011-01-11 13:02:38 -08:00
|
|
|
$line,
|
|
|
|
$matches);
|
|
|
|
if (!$ok) {
|
2015-05-13 18:05:15 +10:00
|
|
|
throw new Exception(pht('Failed to parse %s output!', 'git ls-tree'));
|
2011-01-11 13:02:38 -08:00
|
|
|
}
|
|
|
|
$result[$matches[4]] = array(
|
|
|
|
'mode' => $matches[1],
|
|
|
|
'type' => $matches[2],
|
|
|
|
'ref' => $matches[3],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getFileDataAtRevision($path, $revision) {
|
|
|
|
// NOTE: We don't want to just "git show {$revision}:{$path}" since if the
|
|
|
|
// path was a directory at the given revision we'll get a list of its files
|
|
|
|
// and treat it as though it as a file containing a list of other files,
|
|
|
|
// which is silly.
|
|
|
|
|
2012-03-02 16:47:34 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
'ls-tree %s -- %s',
|
2011-01-11 13:02:38 -08:00
|
|
|
$revision,
|
|
|
|
$path);
|
|
|
|
|
|
|
|
$info = $this->parseGitTree($stdout);
|
|
|
|
if (empty($info[$path])) {
|
|
|
|
// No such path, or the path is a directory and we executed 'ls-tree dir/'
|
|
|
|
// and got a list of its contents back.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($info[$path]['type'] != 'blob') {
|
|
|
|
// Path is or was a directory, not a file.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-03-02 16:47:34 -08:00
|
|
|
list($stdout) = $this->execxLocal(
|
|
|
|
'cat-file blob %s',
|
2011-01-11 13:02:38 -08:00
|
|
|
$info[$path]['ref']);
|
|
|
|
return $stdout;
|
|
|
|
}
|
|
|
|
|
2011-06-23 12:10:59 -07:00
|
|
|
/**
|
|
|
|
* Returns names of all the branches in the current repository.
|
|
|
|
*
|
2012-06-26 10:50:43 -07:00
|
|
|
* @return list<dict<string, string>> Dictionary of branch information.
|
2011-06-23 12:10:59 -07:00
|
|
|
*/
|
|
|
|
public function getAllBranches() {
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
list($ref_list) = $this->execxLocal(
|
|
|
|
'for-each-ref --format=%s refs/heads',
|
|
|
|
'%(refname)');
|
|
|
|
$refs = explode("\n", rtrim($ref_list));
|
2012-06-26 10:50:43 -07:00
|
|
|
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
$current = $this->getBranchName();
|
2011-06-23 12:10:59 -07:00
|
|
|
$result = array();
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
foreach ($refs as $ref) {
|
|
|
|
$branch = $this->getBranchNameFromRef($ref);
|
|
|
|
if ($branch) {
|
|
|
|
$result[] = array(
|
|
|
|
'current' => ($branch === $current),
|
|
|
|
'name' => $branch,
|
|
|
|
);
|
2011-06-29 13:21:30 -07:00
|
|
|
}
|
2011-06-23 12:10:59 -07:00
|
|
|
}
|
2012-03-02 16:47:34 -08:00
|
|
|
|
2012-06-26 10:50:43 -07:00
|
|
|
return $result;
|
2011-06-23 12:10:59 -07:00
|
|
|
}
|
|
|
|
|
2011-12-02 16:21:14 -08:00
|
|
|
public function getWorkingCopyRevision() {
|
2012-03-02 16:47:34 -08:00
|
|
|
list($stdout) = $this->execxLocal('rev-parse HEAD');
|
2011-12-02 16:21:14 -08:00
|
|
|
return rtrim($stdout, "\n");
|
|
|
|
}
|
|
|
|
|
2012-11-29 17:40:07 -08:00
|
|
|
public function getUnderlyingWorkingCopyRevision() {
|
|
|
|
list($err, $stdout) = $this->execManualLocal('svn find-rev HEAD');
|
|
|
|
if (!$err && $stdout) {
|
|
|
|
return rtrim($stdout, "\n");
|
|
|
|
}
|
|
|
|
return $this->getWorkingCopyRevision();
|
|
|
|
}
|
|
|
|
|
2012-06-04 15:30:50 -04:00
|
|
|
public function isHistoryDefaultImmutable() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function supportsAmend() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-12-17 12:54:08 -08:00
|
|
|
public function supportsCommitRanges() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function supportsLocalCommits() {
|
2011-09-14 18:44:54 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-07 13:02:53 -08:00
|
|
|
public function hasLocalCommit($commit) {
|
2012-03-09 14:40:47 -08:00
|
|
|
try {
|
2012-09-21 14:02:27 -07:00
|
|
|
if (!$this->getCanonicalRevisionName($commit)) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-03-09 14:40:47 -08:00
|
|
|
} catch (CommandException $exception) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2012-03-07 13:02:53 -08:00
|
|
|
}
|
|
|
|
|
2011-09-14 18:44:54 -07:00
|
|
|
public function getAllLocalChanges() {
|
2014-06-11 14:37:01 -07:00
|
|
|
$diff = $this->getFullGitDiff($this->getBaseCommit());
|
2012-02-29 12:18:46 -08:00
|
|
|
if (!strlen(trim($diff))) {
|
|
|
|
return array();
|
|
|
|
}
|
2011-09-14 18:44:54 -07:00
|
|
|
$parser = new ArcanistDiffParser();
|
|
|
|
return $parser->parseDiff($diff);
|
|
|
|
}
|
|
|
|
|
Add an "arc merge" workflow
Summary:
This should support conservative rewrite policies in git fairly well, under an
assumed workflow of:
- Develop in local branches, never rewrite history.
- Commit with "-m" or by typing a brief, non-template commit message
describing the checkpoint.
- Provide rich information in the web console (reviewers, etc.)
- Finalize with "git checkout master && arc merge branch && git push" or some
flavor thereof.
This supports Mercurial somewhat. The major problem is that "hg merge" fails if
the local is a fastforward of the remote, at which point there's nowhere we can
throw the commit message. Oh well. Just push it and we'll do our best to link
them up based on local commit info.
I am increasingly forming an opinion that Mercurial is "saftey-scissors git".
But also maybe I have no clue what I'm doing. I just don't understand why anyone
would think it's a good idea to have a trunk consisting of ~50% known-broken
revisions, random checkpoint parts, whitespace changes, typo fixes, etc. If you
use git with branching you can avoid this by making a trunk out of merges or
with rebase/amend, but there seems to be no way to have "one commit = one idea"
in any real sense in Mercurial.
Test Plan: Execute "arc merge" in git and mercurial.
Reviewers: fratrik, Makinde, aran, jungejason, tuomaspelkonen
Reviewed By: Makinde
CC: aran, epriestley, Makinde
Differential Revision: 860
2011-08-25 16:02:03 -07:00
|
|
|
public function supportsLocalBranchMerge() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function performLocalBranchMerge($branch, $message) {
|
|
|
|
if (!$branch) {
|
|
|
|
throw new ArcanistUsageException(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht('Under git, you must specify the branch you want to merge.'));
|
Add an "arc merge" workflow
Summary:
This should support conservative rewrite policies in git fairly well, under an
assumed workflow of:
- Develop in local branches, never rewrite history.
- Commit with "-m" or by typing a brief, non-template commit message
describing the checkpoint.
- Provide rich information in the web console (reviewers, etc.)
- Finalize with "git checkout master && arc merge branch && git push" or some
flavor thereof.
This supports Mercurial somewhat. The major problem is that "hg merge" fails if
the local is a fastforward of the remote, at which point there's nowhere we can
throw the commit message. Oh well. Just push it and we'll do our best to link
them up based on local commit info.
I am increasingly forming an opinion that Mercurial is "saftey-scissors git".
But also maybe I have no clue what I'm doing. I just don't understand why anyone
would think it's a good idea to have a trunk consisting of ~50% known-broken
revisions, random checkpoint parts, whitespace changes, typo fixes, etc. If you
use git with branching you can avoid this by making a trunk out of merges or
with rebase/amend, but there seems to be no way to have "one commit = one idea"
in any real sense in Mercurial.
Test Plan: Execute "arc merge" in git and mercurial.
Reviewers: fratrik, Makinde, aran, jungejason, tuomaspelkonen
Reviewed By: Makinde
CC: aran, epriestley, Makinde
Differential Revision: 860
2011-08-25 16:02:03 -07:00
|
|
|
}
|
|
|
|
$err = phutil_passthru(
|
|
|
|
'(cd %s && git merge --no-ff -m %s %s)',
|
|
|
|
$this->getPath(),
|
|
|
|
$message,
|
|
|
|
$branch);
|
|
|
|
|
|
|
|
if ($err) {
|
2015-05-22 17:09:55 +10:00
|
|
|
throw new ArcanistUsageException(pht('Merge failed!'));
|
Add an "arc merge" workflow
Summary:
This should support conservative rewrite policies in git fairly well, under an
assumed workflow of:
- Develop in local branches, never rewrite history.
- Commit with "-m" or by typing a brief, non-template commit message
describing the checkpoint.
- Provide rich information in the web console (reviewers, etc.)
- Finalize with "git checkout master && arc merge branch && git push" or some
flavor thereof.
This supports Mercurial somewhat. The major problem is that "hg merge" fails if
the local is a fastforward of the remote, at which point there's nowhere we can
throw the commit message. Oh well. Just push it and we'll do our best to link
them up based on local commit info.
I am increasingly forming an opinion that Mercurial is "saftey-scissors git".
But also maybe I have no clue what I'm doing. I just don't understand why anyone
would think it's a good idea to have a trunk consisting of ~50% known-broken
revisions, random checkpoint parts, whitespace changes, typo fixes, etc. If you
use git with branching you can avoid this by making a trunk out of merges or
with rebase/amend, but there seems to be no way to have "one commit = one idea"
in any real sense in Mercurial.
Test Plan: Execute "arc merge" in git and mercurial.
Reviewers: fratrik, Makinde, aran, jungejason, tuomaspelkonen
Reviewed By: Makinde
CC: aran, epriestley, Makinde
Differential Revision: 860
2011-08-25 16:02:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getFinalizedRevisionMessage() {
|
2015-05-13 18:05:15 +10:00
|
|
|
return pht(
|
|
|
|
"You may now push this commit upstream, as appropriate (e.g. with ".
|
|
|
|
"'%s', or '%s', or by printing and faxing it).",
|
|
|
|
'git push',
|
|
|
|
'git svn dcommit');
|
Add an "arc merge" workflow
Summary:
This should support conservative rewrite policies in git fairly well, under an
assumed workflow of:
- Develop in local branches, never rewrite history.
- Commit with "-m" or by typing a brief, non-template commit message
describing the checkpoint.
- Provide rich information in the web console (reviewers, etc.)
- Finalize with "git checkout master && arc merge branch && git push" or some
flavor thereof.
This supports Mercurial somewhat. The major problem is that "hg merge" fails if
the local is a fastforward of the remote, at which point there's nowhere we can
throw the commit message. Oh well. Just push it and we'll do our best to link
them up based on local commit info.
I am increasingly forming an opinion that Mercurial is "saftey-scissors git".
But also maybe I have no clue what I'm doing. I just don't understand why anyone
would think it's a good idea to have a trunk consisting of ~50% known-broken
revisions, random checkpoint parts, whitespace changes, typo fixes, etc. If you
use git with branching you can avoid this by making a trunk out of merges or
with rebase/amend, but there seems to be no way to have "one commit = one idea"
in any real sense in Mercurial.
Test Plan: Execute "arc merge" in git and mercurial.
Reviewers: fratrik, Makinde, aran, jungejason, tuomaspelkonen
Reviewed By: Makinde
CC: aran, epriestley, Makinde
Differential Revision: 860
2011-08-25 16:02:03 -07:00
|
|
|
}
|
|
|
|
|
2012-07-01 11:06:05 -07:00
|
|
|
public function getCommitMessage($commit) {
|
2012-03-02 16:47:34 -08:00
|
|
|
list($message) = $this->execxLocal(
|
2012-07-01 11:06:05 -07:00
|
|
|
'log -n1 --format=%C %s --',
|
2012-09-06 08:56:44 -06:00
|
|
|
'%s%n%n%b',
|
2012-07-01 11:06:05 -07:00
|
|
|
$commit);
|
|
|
|
return $message;
|
2012-01-12 19:03:11 -08:00
|
|
|
}
|
|
|
|
|
2012-01-24 08:07:38 -08:00
|
|
|
public function loadWorkingCopyDifferentialRevisions(
|
|
|
|
ConduitClient $conduit,
|
|
|
|
array $query) {
|
|
|
|
|
|
|
|
$messages = $this->getGitCommitLog();
|
|
|
|
if (!strlen($messages)) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$parser = new ArcanistDiffParser();
|
|
|
|
$messages = $parser->parseDiff($messages);
|
|
|
|
|
|
|
|
// First, try to find revisions by explicit revision IDs in commit messages.
|
2012-05-11 06:07:33 -07:00
|
|
|
$reason_map = array();
|
2012-01-24 08:07:38 -08:00
|
|
|
$revision_ids = array();
|
|
|
|
foreach ($messages as $message) {
|
|
|
|
$object = ArcanistDifferentialCommitMessage::newFromRawCorpus(
|
|
|
|
$message->getMetadata('message'));
|
|
|
|
if ($object->getRevisionID()) {
|
|
|
|
$revision_ids[] = $object->getRevisionID();
|
2012-05-11 06:07:33 -07:00
|
|
|
$reason_map[$object->getRevisionID()] = $message->getCommitHash();
|
2012-01-24 08:07:38 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($revision_ids) {
|
|
|
|
$results = $conduit->callMethodSynchronous(
|
|
|
|
'differential.query',
|
|
|
|
$query + array(
|
|
|
|
'ids' => $revision_ids,
|
|
|
|
));
|
2012-05-11 06:07:33 -07:00
|
|
|
|
|
|
|
foreach ($results as $key => $result) {
|
|
|
|
$hash = substr($reason_map[$result['id']], 0, 16);
|
2015-05-13 18:05:15 +10:00
|
|
|
$results[$key]['why'] = pht(
|
|
|
|
"Commit message for '%s' has explicit 'Differential Revision'.",
|
|
|
|
$hash);
|
2012-05-11 06:07:33 -07:00
|
|
|
}
|
|
|
|
|
2012-01-24 08:07:38 -08:00
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we didn't succeed, try to find revisions by hash.
|
|
|
|
$hashes = array();
|
|
|
|
foreach ($this->getLocalCommitInformation() as $commit) {
|
|
|
|
$hashes[] = array('gtcm', $commit['commit']);
|
|
|
|
$hashes[] = array('gttr', $commit['tree']);
|
|
|
|
}
|
|
|
|
|
|
|
|
$results = $conduit->callMethodSynchronous(
|
|
|
|
'differential.query',
|
|
|
|
$query + array(
|
|
|
|
'commitHashes' => $hashes,
|
|
|
|
));
|
|
|
|
|
2012-05-11 06:07:33 -07:00
|
|
|
foreach ($results as $key => $result) {
|
2015-05-13 18:05:15 +10:00
|
|
|
$results[$key]['why'] = pht(
|
2014-05-23 13:53:05 -07:00
|
|
|
'A git commit or tree hash in the commit range is already attached '.
|
2015-05-13 18:05:15 +10:00
|
|
|
'to the Differential revision.');
|
2012-05-11 06:07:33 -07:00
|
|
|
}
|
|
|
|
|
2012-01-24 08:07:38 -08:00
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
2012-03-16 13:40:33 -07:00
|
|
|
public function updateWorkingCopy() {
|
|
|
|
$this->execxLocal('pull');
|
2012-12-17 12:54:08 -08:00
|
|
|
$this->reloadWorkingCopy();
|
2012-03-16 13:40:33 -07:00
|
|
|
}
|
|
|
|
|
2012-05-11 06:07:33 -07:00
|
|
|
public function getCommitSummary($commit) {
|
|
|
|
if ($commit == self::GIT_MAGIC_ROOT_COMMIT) {
|
2015-05-13 18:05:15 +10:00
|
|
|
return pht('(The Empty Tree)');
|
2012-05-11 06:07:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
list($summary) = $this->execxLocal(
|
|
|
|
'log -n 1 --format=%C %s',
|
|
|
|
'%s',
|
|
|
|
$commit);
|
|
|
|
|
|
|
|
return trim($summary);
|
|
|
|
}
|
|
|
|
|
2013-05-14 11:00:56 -07:00
|
|
|
public function backoutCommit($commit_hash) {
|
2015-05-13 18:05:15 +10:00
|
|
|
$this->execxLocal('revert %s -n --no-edit', $commit_hash);
|
2013-05-14 11:00:56 -07:00
|
|
|
$this->reloadWorkingCopy();
|
|
|
|
if (!$this->getUncommittedStatus()) {
|
|
|
|
throw new ArcanistUsageException(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht('%s has already been reverted.', $commit_hash));
|
2013-05-14 11:00:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getBackoutMessage($commit_hash) {
|
2015-05-13 18:05:15 +10:00
|
|
|
return pht('This reverts commit %s.', $commit_hash);
|
2013-05-14 11:00:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public function isGitSubversionRepo() {
|
|
|
|
return Filesystem::pathExists($this->getPath('.git/svn'));
|
|
|
|
}
|
|
|
|
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
public function resolveBaseCommitRule($rule, $source) {
|
|
|
|
list($type, $name) = explode(':', $rule, 2);
|
|
|
|
|
|
|
|
switch ($type) {
|
|
|
|
case 'git':
|
|
|
|
$matches = null;
|
|
|
|
if (preg_match('/^merge-base\((.+)\)$/', $name, $matches)) {
|
|
|
|
list($err, $merge_base) = $this->execManualLocal(
|
|
|
|
'merge-base %s HEAD',
|
|
|
|
$matches[1]);
|
|
|
|
if (!$err) {
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the merge-base of '%s' and HEAD, as specified by ".
|
|
|
|
"'%s' in your %s 'base' configuration.",
|
|
|
|
$matches[1],
|
|
|
|
$rule,
|
|
|
|
$source));
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
return trim($merge_base);
|
|
|
|
}
|
2012-06-27 12:12:39 -07:00
|
|
|
} else if (preg_match('/^branch-unique\((.+)\)$/', $name, $matches)) {
|
|
|
|
list($err, $merge_base) = $this->execManualLocal(
|
|
|
|
'merge-base %s HEAD',
|
|
|
|
$matches[1]);
|
|
|
|
if ($err) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$merge_base = trim($merge_base);
|
|
|
|
|
|
|
|
list($commits) = $this->execxLocal(
|
|
|
|
'log --format=%C %s..HEAD --',
|
|
|
|
'%H',
|
|
|
|
$merge_base);
|
|
|
|
$commits = array_filter(explode("\n", $commits));
|
|
|
|
|
|
|
|
if (!$commits) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$commits[] = $merge_base;
|
|
|
|
|
|
|
|
$head_branch_count = null;
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
$all_branch_names = ipull($this->getAllBranches(), 'name');
|
2012-06-27 12:12:39 -07:00
|
|
|
foreach ($commits as $commit) {
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
// Ideally, we would use something like "for-each-ref --contains"
|
|
|
|
// to get a filtered list of branches ready for script consumption.
|
|
|
|
// Instead, try to get predictable output from "branch --contains".
|
2012-06-27 12:12:39 -07:00
|
|
|
list($branches) = $this->execxLocal(
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
'-c column.ui=never -c color.ui=never branch --contains %s',
|
2012-06-27 12:12:39 -07:00
|
|
|
$commit);
|
|
|
|
$branches = array_filter(explode("\n", $branches));
|
Minimize reliance on 'git branch' output format
Summary:
Ref T5554. Both the current branch name (if on a branch), as well as the
list of all local branches, can be retrieved without having to parse the
output from "git branch".
Unfortunately, there seems to be no git plumbing for "get list of
branches containing this commit" yet.
(see http://marc.info/?l=git&m=141408477614635&w=2)
For that case, this commit whitelists the output from "git branch" using
the known valid branch names from "git for-each-ref".
Test Plan:
Set up a test repo with this structure:
```
| * Commit B1, on branch "subfeature"
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
```
In `subfeature`, I tried:
* `arc which --base 'git:branch-unique(master)'`
* `arc feature`
After that, I detached my HEAD (don't worry, I got better) and tried again.
Nothing looked broken.
(Tested with git 1.7.2.5 and 2.5.0.)
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin
Maniphest Tasks: T5554
Differential Revision: https://secure.phabricator.com/D13989
2015-08-24 17:57:41 -07:00
|
|
|
|
|
|
|
// Filter the list, removing the "current" marker (*) and ignoring
|
|
|
|
// anything other than known branch names (mainly, any possible
|
|
|
|
// "detached HEAD" or "no branch" line).
|
|
|
|
foreach ($branches as $key => $branch) {
|
|
|
|
$branch = trim($branch, ' *');
|
|
|
|
if (in_array($branch, $all_branch_names)) {
|
|
|
|
$branches[$key] = $branch;
|
|
|
|
} else {
|
|
|
|
unset($branches[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-27 12:12:39 -07:00
|
|
|
if ($head_branch_count === null) {
|
|
|
|
// If this is the first commit, it's HEAD. Count how many
|
|
|
|
// branches it is on; we want to include commits on the same
|
|
|
|
// number of branches. This covers a case where this branch
|
|
|
|
// has sub-branches and we're running "arc diff" here again
|
|
|
|
// for whatever reason.
|
|
|
|
$head_branch_count = count($branches);
|
|
|
|
} else if (count($branches) > $head_branch_count) {
|
|
|
|
$branches = implode(', ', $branches);
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the first commit between '%s' (the merge-base of ".
|
|
|
|
"'%s' and HEAD) which is also contained by another branch ".
|
|
|
|
"(%s).",
|
|
|
|
$merge_base,
|
|
|
|
$matches[1],
|
|
|
|
$branches));
|
2012-06-27 12:12:39 -07:00
|
|
|
return $commit;
|
|
|
|
}
|
|
|
|
}
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
} else {
|
|
|
|
list($err) = $this->execManualLocal(
|
|
|
|
'cat-file -t %s',
|
|
|
|
$name);
|
|
|
|
if (!$err) {
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is specified by '%s' in your %s 'base' configuration.",
|
|
|
|
$rule,
|
|
|
|
$source));
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
return $name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'arc':
|
|
|
|
switch ($name) {
|
|
|
|
case 'empty':
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"you specified '%s' in your %s 'base' configuration.",
|
|
|
|
$rule,
|
|
|
|
$source));
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
return self::GIT_MAGIC_ROOT_COMMIT;
|
2012-07-01 11:06:05 -07:00
|
|
|
case 'amended':
|
|
|
|
$text = $this->getCommitMessage('HEAD');
|
|
|
|
$message = ArcanistDifferentialCommitMessage::newFromRawCorpus(
|
|
|
|
$text);
|
|
|
|
if ($message->getRevisionID()) {
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"HEAD has been amended with 'Differential Revision:', ".
|
|
|
|
"as specified by '%s' in your %s 'base' configuration.",
|
|
|
|
$rule,
|
|
|
|
$source));
|
2012-07-01 11:06:05 -07:00
|
|
|
return 'HEAD^';
|
|
|
|
}
|
|
|
|
break;
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
case 'upstream':
|
|
|
|
list($err, $upstream) = $this->execManualLocal(
|
2014-07-10 07:56:35 -07:00
|
|
|
'rev-parse --abbrev-ref --symbolic-full-name %s',
|
|
|
|
'@{upstream}');
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
if (!$err) {
|
2013-02-11 15:58:47 -08:00
|
|
|
$upstream = rtrim($upstream);
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
list($upstream_merge_base) = $this->execxLocal(
|
|
|
|
'merge-base %s HEAD',
|
|
|
|
$upstream);
|
2013-02-11 15:58:47 -08:00
|
|
|
$upstream_merge_base = rtrim($upstream_merge_base);
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"it is the merge-base of the upstream of the current branch ".
|
|
|
|
"and HEAD, and matched the rule '%s' in your %s ".
|
|
|
|
"'base' configuration.",
|
|
|
|
$rule,
|
|
|
|
$source));
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
return $upstream_merge_base;
|
|
|
|
}
|
|
|
|
break;
|
2012-12-17 12:53:38 -08:00
|
|
|
case 'this':
|
|
|
|
$this->setBaseCommitExplanation(
|
2015-05-13 18:05:15 +10:00
|
|
|
pht(
|
|
|
|
"you specified '%s' in your %s 'base' configuration.",
|
|
|
|
$rule,
|
|
|
|
$source));
|
2012-12-17 12:53:38 -08:00
|
|
|
return 'HEAD^';
|
Add a DSL for selecting base commits
Summary:
New optional mode. If you set 'base' in local, project or global config or pass '--base' to 'arc diff' or 'arc which', it switches to DSL mode.
In DSL mode, lists of rules from args, local, project and global config are resolved, in that order. Rules can manipulate the rule machine or resolve into actual commits. Provides support for some 'arc' rules (mostly machine manipulation) and 'git' rules (symbolic ref and merge-base).
Test Plan:
Ran unit tests. Also:
```$ arc which --show-base --base 'arc:prompt'
Against which commit? HEAD
HEAD
$ arc which --show-base --base 'git:HEAD'
HEAD
$ arc which --show-base --base 'git:fake'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'git:origin/master'
origin/master
$ arc which --show-base --base 'git:upstream'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc which --show-base --base 'literal:derp'
derp
$ arc which --show-base --base 'arc:halt'
Usage Exception: None of the rules in your 'base' configuration matched a valid commit. Adjust rules or specify which commit you want to use explicitly.
$ arc set-config --local base git:origin/master
Set key 'base' = 'git:origin/master' in local config.
$ arc which --show-base
origin/master
$ arc which --show-base --base 'git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:yield, git:HEAD^'
origin/master
$ arc which --show-base --base 'arc:global, git:HEAD^'
HEAD^
$ arc which --show-base --base 'arc:global, git:merge-base(origin/master)'
3f4f8992fba8d1f142974da36a82bae900e247c0```
Reviewers: dschleimer, vrana
Reviewed By: dschleimer
CC: aran
Maniphest Tasks: T1233
Differential Revision: https://secure.phabricator.com/D2748
2012-06-15 14:01:28 -07:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2013-03-21 15:51:39 -07:00
|
|
|
public function canStashChanges() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function stashChanges() {
|
|
|
|
$this->execxLocal('stash');
|
|
|
|
$this->reloadWorkingCopy();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function unstashChanges() {
|
|
|
|
$this->execxLocal('stash pop');
|
|
|
|
}
|
|
|
|
|
Dirty cache of HEAD commit after an amend/range reload
Summary:
Introducing `--head` caused us to run `git diff base..head` explicitly.
However, we can now hit this workflow:
- We resolve `HEAD` as commit `aaaa1`.
- This is cached.
- We notice dirty working copy changes and prompt the user to amend them to HEAD.
- The user accepts the amend.
- We amend, creating commit `bbbb2`.
- We dirty the commit range and reload the working copy. This //does not// dirty the cache of HEAD.
- We run `git diff`, but it uses the old cached HEAD: `git diff base..aaaa1`.
- This works fine (`aaaa1` still exists, it's just not on any branch) but produces the wrong diff (without amended changes).
To resolve this, implement the "dirty the cache when the range reloads" hook.
Also never try to amend if the user provides `--head`.
Test Plan:
Ran `arc diff --only --trace` in a working copy with a new commit and some uncommitted changes.
- Prior to this change, saw a `git diff base..aaaa1` command and the wrong diff.
- After this change, saw a `git diff base..bbbb2` command and the correct diff.
Reviewers: chad, csilvers, talshiri
Reviewed By: talshiri
Subscribers: epriestley, spicyj
Differential Revision: https://secure.phabricator.com/D9506
2014-06-12 15:46:15 -07:00
|
|
|
protected function didReloadCommitRange() {
|
|
|
|
// After an amend, the symbolic head may resolve to a different commit.
|
|
|
|
$this->resolvedHeadCommit = null;
|
|
|
|
}
|
|
|
|
|
2011-01-09 15:22:25 -08:00
|
|
|
}
|