From 98b3dfda3bf460ce3f5bb9af6258ed95405c5633 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 27 Jun 2012 12:12:39 -0700 Subject: [PATCH] Implement "git:branch-unique(*)" in the base commit DSL Summary: Add a DSL command to select the first commit between HEAD and (the merge-base of some target with HEAD) that's on more than one branch. This is similar to @csilvers' suggestion in . A specific problem this address is that, currently, if you use this workflow: $ git checkout -b feature $ git commit $ git checkout -b subfeature $ git commit ..i.e., develop dependent features in sub-branches, "arc diff" will try to send up (feature + subfeature). If you use the rule 'git:branch-unique(origin/master)' instead, diffing from 'subfeature' will correctly select only the commit(s) on 'subfeature'. Test Plan: Constructed feature/subfeature branches, verified that we select the correct base commit. Reviewers: dschleimer, csilvers, btrahan, jungejason Reviewed By: dschleimer CC: aran Maniphest Tasks: T1233 Differential Revision: https://secure.phabricator.com/D2866 --- src/repository/api/ArcanistGitAPI.php | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/repository/api/ArcanistGitAPI.php b/src/repository/api/ArcanistGitAPI.php index c71a0253..4fb18334 100644 --- a/src/repository/api/ArcanistGitAPI.php +++ b/src/repository/api/ArcanistGitAPI.php @@ -856,6 +856,52 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI { "configuration."); return trim($merge_base); } + } 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; + foreach ($commits as $commit) { + list($branches) = $this->execxLocal( + 'branch --contains %s', + $commit); + $branches = array_filter(explode("\n", $branches)); + 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) { + foreach ($branches as $key => $branch) { + $branches[$key] = trim($branch, ' *'); + } + $branches = implode(', ', $branches); + $this->setBaseCommitExplanation( + "it is the first commit between '{$merge_base}' (the ". + "merge-base of '{$matches[1]}' and HEAD) which is also ". + "contained by another branch ({$branches})."); + return $commit; + } + } } else { list($err) = $this->execManualLocal( 'cat-file -t %s',