Last active
August 29, 2015 14:25
-
-
Save kasperg/10a4695f2e2278a89972 to your computer and use it in GitHub Desktop.
Glue multiple git repositories into one
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/vendor/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "kasperg/git-glue", | |
"description": "Glue multiple git repositories into one", | |
"authors": [ | |
{ | |
"name": "Kasper Garnaes", | |
"email": "kasper.garnaes@gmail.com" | |
} | |
], | |
"require": { | |
"cpliakas/git-wrapper": "~1.5", | |
"symfony/filesystem": "~2.7", | |
"mnapoli/silly": "^1.1", | |
"psr/log": "^1.0" | |
}, | |
"repositories": [ | |
{ | |
"url": "https://github.com/duncan3dc/climate-logger.git", | |
"type": "vcs" | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once('vendor/autoload.php'); | |
// git-glue is a CLI script to merge the contents of a list of source | |
// repositories into subdirectories within a target repository while preserving | |
// the git history of each source. | |
// Configuration is currently hardcoded into the script but could be moved into | |
// command line arguments. | |
$app = new Silly\Application('git-glue', '0.1'); | |
$app->command('glue', function(\Symfony\Component\Console\Output\OutputInterface $output) use ($app) { | |
// Setup configuration. | |
// The local directory where repositories will be cloned to. | |
$workingDir = '/tmp/git-glue'; | |
// The name of the branches where changes will be committed to. | |
$workingBranch = 'git-glue'; | |
// The repository where all sources should be merged into. | |
$target = 'https://github.com/kasperg/ding2'; | |
// A map of repositories which should be merged into the target repository and | |
// their intended subdirectory within the target repository. | |
$sources = array( | |
'https://github.com/ding2/ddbasic' => 'themes/ddbasic', | |
'https://github.com/ding2/alma' => 'modules/alma', | |
'https://github.com/ding2/bpi' => 'modules/bpi', | |
'https://github.com/ding2/ddb_cp' => 'modules/ddb_cp', | |
'https://github.com/ding2/ding_adhl_frontend' => 'modules/ding_adhl_frontend', | |
'https://github.com/ding2/ding_availability' => 'modules/ding_availability', | |
'https://github.com/ding2/ding_base' => 'modules/ding_base', | |
'https://github.com/ding2/ding_bookmark' => 'modules/ding_bookmark', | |
'https://github.com/ding2/ding_campaign' => 'modules/ding_campaign', | |
'https://github.com/ding2/ding_contact' => 'modules/ding_contact', | |
'https://github.com/ding2/ding_content' => 'modules/ding_content', | |
'https://github.com/ding2/ding_debt' => 'modules/ding_debt', | |
'https://github.com/ding2/ding_devel' => 'modules/ding_devel', | |
'https://github.com/ding2/ding_dibs' => 'modules/ding_dibs', | |
'https://github.com/ding2/ding_dummy_provider' => 'modules/ding_dummy_provider', | |
'https://github.com/ding2/ding_entity' => 'modules/ding_entity', | |
'https://github.com/ding2/ding_event' => 'modules/ding_event', | |
'https://github.com/ding2/ding_example_content' => 'modules/ding_example_content', | |
'https://github.com/ding2/ding_facetbrowser' => 'modules/ding_facetbrowser', | |
'https://github.com/ding2/ding_frontend' => 'modules/ding_frontend', | |
'https://github.com/ding2/ding_frontpage' => 'modules/ding_frontpage', | |
'https://github.com/ding2/ding_groups' => 'modules/ding_groups', | |
'https://github.com/ding2/ding_library' => 'modules/ding_library', | |
'https://github.com/ding2/ding_loan' => 'modules/ding_loan', | |
'https://github.com/ding2/ding_news' => 'modules/ding_news', | |
'https://github.com/ding2/ding_page' => 'modules/ding_page', | |
'https://github.com/ding2/ding_periodical' => 'modules/ding_periodical', | |
'https://github.com/ding2/ding_permissions' => 'modules/ding_permissions', | |
'https://github.com/ding2/ding_place2book' => 'modules/ding_place2book', | |
'https://github.com/ding2/ding_popup' => 'modules/ding_popup', | |
'https://github.com/ding2/ding_provider' => 'modules/ding_provider', | |
'https://github.com/ding2/ding_redirect' => 'modules/ding_redirect', | |
'https://github.com/ding2/ding_reservation' => 'modules/ding_reservation', | |
'https://github.com/ding2/ding_session_cache' => 'modules/ding_session_cache', | |
'https://github.com/ding2/ding_staff' => 'modules/ding_staff', | |
'https://github.com/ding2/ding_tabroll' => 'modules/ding_tabroll', | |
'https://github.com/ding2/ding_ting_frontend' => 'modules/ding_ting_frontend', | |
'https://github.com/ding2/ding_toggle_format' => 'modules/ding_toggle_format', | |
'https://github.com/ding2/ding_user' => 'modules/ding_user', | |
'https://github.com/ding2/ding_user_frontend' => 'modules/ding_user_frontend', | |
'https://github.com/ding2/ding_varnish' => 'modules/ding_varnish', | |
'https://github.com/ding2/ding_wayf' => 'modules/ding_wayf', | |
'https://github.com/ding2/ding_webtrends' => 'modules/ding_webtrends', | |
'https://github.com/ding2/fbs' => 'modules/fbs', | |
'https://github.com/ding2/openruth' => 'modules/openruth', | |
'https://github.com/ding2/ting' => 'modules/ting', | |
'https://github.com/ding2/ting_covers' => 'modules/ting_covers', | |
'https://github.com/ding2/ting_fulltext' => 'modules/ting_fulltext', | |
'https://github.com/ding2/ting_infomedia' => 'modules/ting_infomedia', | |
'https://github.com/ding2/ting_material_details' => 'modules/ting_material_details', | |
'https://github.com/ding2/ting_new_materials' => 'modules/ting_new_materials', | |
'https://github.com/ding2/ting_proxy' => 'modules/ting_proxy', | |
'https://github.com/ding2/ting_reference' => 'modules/ting_reference', | |
'https://github.com/ding2/ting_relation' => 'modules/ting_relation', | |
'https://github.com/ding2/ting_search' => 'modules/ting_search', | |
'https://github.com/ding2/ting_search_carousel' => 'modules/ting_search_carousel', | |
'https://github.com/ding2/ting_sfx' => 'modules/ting_sfx', | |
); | |
// Create services | |
$git = new \GitWrapper\GitWrapper(); | |
$git->addLoggerListener(new \GitWrapper\Event\GitLoggerListener(new \Symfony\Component\Console\Logger\ConsoleLogger($output))); | |
$fs = new \Symfony\Component\Filesystem\Filesystem(); | |
$progress = new \Symfony\Component\Console\Helper\ProgressBar($output, count($sources) + 1); | |
$progress->setFormat("%current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% | %message%"); | |
$progress->start(); | |
// Glue each source into subdirectories within the target directory. | |
// The approach in inspired by http://gbayer.com/development/moving-files-from-one-git-repository-to-another-preserving-history/ | |
// except for the fact that we want all the content from each source added to | |
// the target so there is no need to use filter-branch. | |
// First checkout a new branch from a clean version of the target repository. | |
$progress->setMessage(sprintf('Prepare target repository %s', $target)); | |
$targetDir = $workingDir . parse_url($target, PHP_URL_PATH); | |
$fs->remove($targetDir); | |
$targetRepo = $git->cloneRepository($target, $targetDir); | |
$targetRepo->checkoutNewBranch($workingBranch); | |
$progress->advance(); | |
foreach ($sources as $source => $targetSubDir) { | |
// Now clone each of the sources. We assume that the default branch is the | |
// one we want. | |
$sourceDir = $workingDir . parse_url($source, PHP_URL_PATH); | |
$progress->setMessage(sprintf('Merge source repository %s into %s', $source, $targetSubDir)); | |
$fs->remove($sourceDir); | |
$sourceRepo = $git->cloneRepository($source, $sourceDir); | |
$sourceRepo->checkoutNewBranch($workingBranch); | |
// Move repository content into the intented subdirectory. | |
$fs->mkdir($sourceDir . DIRECTORY_SEPARATOR . $targetSubDir); | |
// GitWrapper does not support moving * as each argument is escaped. | |
// Loop through directory contents instead. | |
foreach (scandir($sourceDir) as $file) { | |
if (!in_array($file, array('.', '..'))) { | |
// Add the k option to suppress errors when trying to move into own | |
// subdirectory. | |
$sourceRepo->mv($file, $targetSubDir, array('k' => TRUE)); | |
} | |
} | |
$sourceRepo->commit( | |
sprintf('Moved %s into subdirectory %s', $source, $targetSubDir) | |
); | |
// Finally add the local clone of the source as a remote to the target and | |
// pull in the changes. | |
$targetRepo->remote('add', $targetSubDir, $sourceRepo->getDirectory()); | |
$targetRepo->pull($targetSubDir, $workingBranch); | |
$progress->advance(); | |
} | |
$progress->finish(); | |
}); | |
$app->run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment