diff --git a/src/repository/api/ArcanistGitAPI.php b/src/repository/api/ArcanistGitAPI.php --- a/src/repository/api/ArcanistGitAPI.php +++ b/src/repository/api/ArcanistGitAPI.php @@ -483,21 +483,14 @@ } public function getBranchName() { - // TODO: consider: - // - // $ git rev-parse --abbrev-ref `git symbolic-ref HEAD` - // - // But that may fail if you're not on a branch. - list($stdout) = $this->execxLocal('branch --no-color'); - - // Assume that any branch beginning with '(' means 'no branch', or whatever - // 'no branch' is in the current locale. - $matches = null; - if (preg_match('/^\* ([^\(].*)$/m', $stdout, $matches)) { - return $matches[1]; + list($stdout) = $this->execxLocal('rev-parse --abbrev-ref HEAD'); + + $branch = rtrim($stdout); + if ($branch === 'HEAD') { + return null; } - return null; + return $branch; } public function getRemoteURI() { @@ -887,22 +880,16 @@ */ public function getAllBranches() { list($branch_info) = $this->execxLocal( - 'branch --no-color'); - $lines = explode("\n", rtrim($branch_info)); + 'for-each-ref --format=%s refs/heads', + '%(refname:short)'); + $branches = explode("\n", rtrim($branch_info)); + $current = $this->getBranchName(); $result = array(); - foreach ($lines as $line) { - - if (preg_match('@^[* ]+\(no branch|detached from \w+/\w+\)@', $line)) { - // This is indicating that the working copy is in a detached state; - // just ignore it. - continue; - } - - list($current, $name) = preg_split('/\s+/', $line, 2); + foreach ($branches as $branch) { $result[] = array( - 'current' => !empty($current), - 'name' => $name, + 'current' => ($branch === $current), + 'name' => $branch, ); } @@ -1134,11 +1121,28 @@ $commits[] = $merge_base; $head_branch_count = null; + $all_branch_names = ipull($this->getAllBranches(), 'name'); foreach ($commits as $commit) { + // 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". list($branches) = $this->execxLocal( - 'branch --contains %s', + '-c column.ui=never -c color.ui=never branch --contains %s', $commit); $branches = array_filter(explode("\n", $branches)); + + // 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]); + } + } + 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 @@ -1147,9 +1151,6 @@ // for whatever reason. $head_branch_count = count($branches); } else if (count($branches) > $head_branch_count) { - foreach ($branches as $key => $branch) { - $branches[$key] = trim($branch, ' *'); - } $branches = implode(', ', $branches); $this->setBaseCommitExplanation( pht(