diff --git a/bin/givi b/bin/givi index b18d9b1f4ba621cddf9b20876618426da71bfdeb..23275c26b1aaa85a74368ca6b4fc80b85d51c2fc 100755 --- a/bin/givi +++ b/bin/givi @@ -69,11 +69,26 @@ class Givi { */ protected $rebase = FALSE; + /** + * @var string, the word 'all' or comma-delimited list of repo names + */ + protected $repoFilter = 'all'; + /** * @var array ($repoName => $relPath) */ protected $repos; + /** + * @var bool + */ + protected $useGencode = FALSE; + + /** + * @var bool + */ + protected $useSetup = FALSE; + /** * @var array, non-hyphenated arguments after the basedir */ @@ -120,6 +135,8 @@ class Givi { 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': @@ -137,6 +154,12 @@ class Givi { 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(); @@ -145,6 +168,13 @@ class Givi { return $this->returnError("unrecognized action: {$this->action}\n"); } + if ($this->useSetup) { + $this->run('core', $this->civiRoot . '/bin', 'bash', 'setup.sh'); + } + elseif ($this->useGencode) { + $this->run('core', $this->civiRoot . '/xml', 'php', 'GenCode.php'); + } + $this->dirStack->pop(); } @@ -166,12 +196,21 @@ class Givi { elseif ($arg == '--dry-run' || $arg == '-n') { $this->dryRun = TRUE; } + elseif ($arg == '--gencode') { + $this->useGencode = TRUE; + } + elseif ($arg == '--setup') { + $this->useSetup = TRUE; + } elseif (preg_match('/^--d([678])/', $arg, $matches)) { $this->drupalVersion = $matches[1]; } 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]; } @@ -203,18 +242,25 @@ class Givi { 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 " --gencode: Run xml/GenCode after checking out code\n"; + echo " --setup: Run bin/setup.sh (incl xml/GenCode) after checking out code\n"; echo "Special options:\n"; echo " --core=X: Specify the branch to use on the core repository\n"; echo " --packages=X: Specify the branch to use on the packages repository\n"; @@ -242,14 +288,14 @@ class Givi { 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; } @@ -271,10 +317,10 @@ class Givi { $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); } } } @@ -295,12 +341,70 @@ class Givi { $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); } } + } /** @@ -333,16 +437,17 @@ class Givi { * @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 { @@ -355,7 +460,7 @@ class Givi { function doFetchAll() { foreach ($this->repos as $repo => $relPath) { - $this->run($relPath, 'git', 'fetch', '--all'); + $this->run($repo, $relPath, 'git', 'fetch', '--all'); } } @@ -374,6 +479,9 @@ class Givi { } function filterBranchName($repoName, $branchName) { + if ($branchName == '') { + return ''; + } if ($repoName == 'drupal') { $parts = explode('/', $branchName); $last = $this->drupalVersion . '.x-' . array_pop($parts); @@ -383,6 +491,28 @@ class Givi { 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();