*/
protected $rebase = FALSE;
+ /**
+ * @var string, the word 'all' or comma-delimited list of repo names
+ */
+ protected $repoFilter = 'all';
+
/**
* @var array ($repoName => $relPath)
*/
return $this->returnError("Root appears to be invalid -- missing too many repos. Try --root=<dir>\n");
}
+ $this->repos = $this->filterRepos($this->repoFilter, $this->repos);
+
// Run the action
switch ($this->action) {
case 'checkout':
case 'resume':
call_user_func_array(array($this, 'doResume'), $this->arguments);
break;
+ //case 'merge-forward':
+ // call_user_func_array(array($this, 'doMergeForward'), $this->arguments);
+ // break;
+ case 'push':
+ call_user_func_array(array($this, 'doPush'), $this->arguments);
+ break;
case 'help':
case '':
$this->doHelp();
elseif (preg_match('/^--root=(.*)/', $arg, $matches)) {
$this->civiRoot = $matches[1];
}
+ elseif (preg_match('/^--repos=(.*)/', $arg, $matches)) {
+ $this->repoFilter = $matches[1];
+ }
elseif (preg_match('/^--(core|packages|joomla|drupal|wordpress)=(.*)/', $arg, $matches)) {
$this->branches[$matches[1]] = $matches[2];
}
echo " $program [options] status\n";
echo " $program [options] begin <base-branch> [--core=<new-branch>|--drupal=<new-branch>|...] \n";
echo " $program [options] resume [--rebase] <base-branch> [--core=<custom-branch>|--drupal=<custom-branch>|...] \n";
+ #echo " $program [options] merge-forward <maintenace-branch> <development-branch>\n";
+ #echo " $program [options] push <remote> <branch>[:<branch>]\n";
echo "Actions:\n";
echo " checkout: Checkout same branch name on all repos\n";
echo " fetch: Fetch remote changes on all repos\n";
echo " status: Display status on all repos\n";
echo " begin: Begin work on a new branch on some repo (and use base-branch for all others)\n";
echo " resume: Resume work on an existing branch on some repo (and use base-branch for all others)\n";
+ #echo " merge-forward: On each repo, merge changes from maintenance branch to development branch\n";
+ #echo " push: On each repo, push a branch to a remote (Note: only intended for use with merge-forward)\n";
echo "Common options:\n";
echo " --dry-run: Don't do anything; only print commands that would be run\n";
echo " --d6: Specify that Drupal branches should use 6.x-* prefixes\n";
echo " --d7: Specify that Drupal branches should use 7.x-* prefixes (default)\n";
echo " --fetch: Fetch the latest code before creating, updating, or checking-out anything\n";
+ echo " --repos=X: Restrict operations to the listed repos (comma-delimited list) (default: all)";
echo " --root=X: Specify CiviCRM root directory (default: .)\n";
echo "Special options:\n";
echo " --core=X: Specify the branch to use on the core repository\n";
foreach ($this->repos as $repo => $relPath) {
$filteredBranch = $this->filterBranchName($repo, $branches[$repo]);
- $this->run($relPath, 'git', 'checkout', $filteredBranch);
+ $this->run($repo, $relPath, 'git', 'checkout', $filteredBranch);
}
return TRUE;
}
function doStatusAll() {
foreach ($this->repos as $repo => $relPath) {
- $this->run($relPath, 'git', 'status');
+ $this->run($repo, $relPath, 'git', 'status');
}
return TRUE;
}
$filteredBaseBranch = $this->filterBranchName($repo, $baseBranch);
if ($filteredBranch == $filteredBaseBranch) {
- $this->run($relPath, 'git', 'checkout', $filteredBranch);
+ $this->run($repo, $relPath, 'git', 'checkout', $filteredBranch);
}
else {
- $this->run($relPath, 'git', 'checkout', '-b', $filteredBranch, $filteredBaseBranch);
+ $this->run($repo, $relPath, 'git', 'checkout', '-b', $filteredBranch, $filteredBaseBranch);
}
}
}
$filteredBranch = $this->filterBranchName($repo, $branches[$repo]);
$filteredBaseBranch = $this->filterBranchName($repo, $baseBranch);
- $this->run($relPath, 'git', 'checkout', $filteredBranch);
+ $this->run($repo, $relPath, 'git', 'checkout', $filteredBranch);
if ($filteredBranch != $filteredBaseBranch && $this->rebase) {
list ($baseRemoteRepo, $baseRemoteBranch) = $this->parseBranchRepo($filteredBaseBranch);
- $this->run($relPath, 'git', 'pull', '--rebase', $baseRemoteRepo, $baseRemoteBranch);
+ $this->run($repo, $relPath, 'git', 'pull', '--rebase', $baseRemoteRepo, $baseRemoteBranch);
}
}
}
+ /*
+
+ If we want merge-forward changes to be subject to PR process, then this
+ should useful. Currently using a simpler process based on
+ toosl/scripts/merge-forward
+
+ function doMergeForward($maintBranch, $devBranch) {
+ if (!$maintBranch) {
+ return $this->returnError("Missing <maintenace-base-branch>\n");
+ }
+ if (!$devBranch) {
+ return $this->returnError("Missing <development-base-branch>\n");
+ }
+ list ($maintBranchRepo, $maintBranchName) = $this->parseBranchRepo($maintBranch);
+ list ($devBranchRepo, $devBranchName) = $this->parseBranchRepo($devBranch);
+
+ $newBranchRepo = $devBranchRepo;
+ $newBranchName = $maintBranchName . '-' . $devBranchName . '-' . date('Y-m-d-H-i-s');
+
+ if ($this->fetch) {
+ $this->doFetchAll();
+ }
+
+ foreach ($this->repos as $repo => $relPath) {
+ $filteredMaintBranch = $this->filterBranchName($repo, $maintBranch);
+ $filteredDevBranch = $this->filterBranchName($repo, $devBranch);
+ $filteredNewBranchName = $this->filterBranchName($repo, $newBranchName);
+
+ $this->run($repo, $relPath, 'git', 'checkout', '-b', $filteredNewBranchName, $filteredDevBranch);
+ $this->run($repo, $relPath, 'git', 'merge', $filteredMaintBranch);
+ }
+ }
+ */
+
+ function doPush($newBranchRepo, $newBranchNames) {
+ if (!$newBranchRepo) {
+ return $this->returnError("Missing <remote>\n");
+ }
+ if (!$newBranchNames) {
+ return $this->returnError("Missing <branch>[:<branch>]\n");
+ }
+ if (FALSE !== strpos($newBranchNames, ':')) {
+ list ($newBranchFromName,$newBranchToName) = explode(':', $newBranchNames);
+ foreach ($this->repos as $repo => $relPath) {
+ $filteredFromName = $this->filterBranchName($repo, $newBranchFromName);
+ $filteredToName = $this->filterBranchName($repo, $newBranchToName);
+
+ $this->run($repo, $relPath, 'git', 'push', $newBranchRepo, $filteredFromName . ':' . $filteredToName);
+ }
+ } else {
+ foreach ($this->repos as $repo => $relPath) {
+ $filteredName = $this->filterBranchName($repo, $newBranchNames);
+ $this->run($repo, $relPath, 'git', 'push', $newBranchRepo, $filteredName);
+ }
+ }
+
+ }
+
/**
* Given a ref name, determine the repo and branch
*
* @param string $command
* @return string
*/
- function run($runDir, $command) {
+ function run($repoName, $runDir, $command) {
$this->dirStack->push($runDir);
$args = func_get_args();
array_shift($args);
array_shift($args);
+ array_shift($args);
foreach ($args as $arg) {
$command .= ' ' . escapeshellarg($arg);
}
- printf("\nRUN [%s]: %s\n", $runDir, $command);
+ printf("\n\n\nRUN [%s]: %s\n", $repoName, $command);
if ($this->dryRun) {
$r = NULL;
} else {
function doFetchAll() {
foreach ($this->repos as $repo => $relPath) {
- $this->run($relPath, 'git', 'fetch', '--all');
+ $this->run($repo, $relPath, 'git', 'fetch', '--all');
}
}
}
function filterBranchName($repoName, $branchName) {
+ if ($branchName == '') {
+ return '';
+ }
if ($repoName == 'drupal') {
$parts = explode('/', $branchName);
$last = $this->drupalVersion . '.x-' . array_pop($parts);
return $branchName;
}
+ /**
+ * @param string $filter e.g. "all" or "repo1,repo2"
+ * @param array $repos ($repoName => $repoDir)
+ * @return array ($repoName => $repoDir)
+ */
+ function filterRepos($filter, $repos) {
+ if ($filter == 'all') {
+ return $repos;
+ }
+
+ $inclRepos = explode(',', $filter);
+ $unknowns = array_diff($inclRepos, array_keys($repos));
+ if (!empty($unknowns)) {
+ throw new Exception("Unknown Repos: " . implode(',', $unknowns));
+ }
+ $unwanted = array_diff(array_keys($repos), $inclRepos);
+ foreach ($unwanted as $repo) {
+ unset($repos[$repo]);
+ }
+ return $repos;
+ }
+
function returnError($message) {
echo "ERROR: ", $message, "\n";
$this->doHelp();