標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

Bitbucket から github へ PowerShell スクリプトで半自動でレポジトリのコピーを行う

Bitbucket 社からメールが届いた

GitHub と同様のソースコードのレポジトリサービスを提供している BitBucket 社からメールが届いた。

一部の訳を抜粋:

スニペットとダウンロード:これらの機能は、無料のワークスペースでは利用できなくなります。

さらに、3 か月以上アクセスまたはプッシュされていない無料リポジトリは、2025 年 4 月 28 日より前にアーカイブされる可能性があります

アーカイブされるだけでデータが失われるわけではないんだが、こういうケチくさい話が始まるとフリーサービスが終了する可能性も無視できなくなってくるので、とっととレポジトリを引き上げることにした。元々、Mercurial のために利用を始めたサービスだが、Mercurial サービスが終了し、GitHub のフリーサービスが拡大した今となっては…

引っ越しスクリプト

レポジトリをコピーするなら、本来は GitHub のインポート機能を使うべきだが、Bitbucket 側がリジェクトしているのかうまく動作しない。仕方ないので、地道に Bitbucket → ローカルディスク → GitHub とワンクッション入れて、コピーすることにする。方針は次のとおり:

  • Windows でやるので、PowerShell 7 pwsh.exe , git.exe , gh.exeGitHub コマンドラインクライアント)を使う
  • レポジトリによって差異があり、あらゆるレポジトリで一発成功するスクリプトは現実的ではないので、いちいちユーザにコマンドを実行前に確認させ、実行 / スキップ / 中止を選択させる(慎重)
  • 使用済みのワークファイルは、デスクトップのゴミ箱に捨てる(間違えて削除しても復活できるし、放っておいても、いつか消える…たぶん)

bitbucket2github.ps1

Set-PSDebug -Strict

$githubuser = "YOUR-GITHUB-USERNAME"
$bitpath = $args[ $args.Length - 1 ]

function Step-Exec($command) {
    While ($true) {
        $ans = (Read-Host ("`r`n$ {0}`r`n[Y]es: execute, [N]o: skip, [Q]uit ?" -f $command))
        if ($ans -eq "q" ){
            exit 1
        }
        if ($ans -eq "n" ){
            return
        }
        if ($ans -eq "y" ){
            Invoke-Expression $command
            return
        }
    }
}

function Get-Branches(){
    $remote = (git remote show origin)
    $result = @()
    for ($i = 0 ; $i -lt $remote.Length ; $i++) {
        if ( $remote[$i] -like "*Remote*branch*" ){
            for ( $i++ ; $i -lt $remote.Length -and $remote[$i] -like "    *" ; $i++ ){
                Write-Host ("Found branch: " + $remote[$i])
                $b = $remote[$i].SubString(4).Split()[0]
                $result += $b
            }
            return $result
        } else {
            Write-Host ("Ignore: " + $remote[$i])
        }
    }
}

function throw-trash($path){
    $shell = New-Object -comObject Shell.Application
    $trash = $shell.NameSpace(10)
    $path = (Resolve-Path $path).Path
    $trash.MoveHere( $path )
}

$repo = Split-Path -Path $bitpath -Leaf
if ( $repo -like "*.git" ){
    $repo = $repo.SubString(0,$repo.Length - 4)
}

Push-Location $env:temp
Write-Host ("chdir $env:temp")
Step-Exec "git clone $bitpath"
Push-Location $repo
$branches = (Get-Branches)
foreach ($i in $branches) {
    Step-Exec "git pull origin ${i}:${i}"
}
Step-Exec "gh repo create --private $githubuser/$repo -d `"From $bitpath`" --disable-wiki --disable-issues"
Step-Exec "git remote add github git@github.com:$githubuser/$repo.git"
Step-Exec "git push --all github"
Step-Exec "gh browse"
Pop-Location
Step-exec "throw-trash $repo"
Pop-Location

手順

(1)bitbucket 側レポジトリの Clone ボタンを押下して、SSH でのクローンパラメータを得る

git clone git@bitbucket.org:USER/REPO.git

(2)スクリプトを起動

bitbucket2git.ps1 git clone git@bitbucket.org:USER/REPO.git

git clone は無視される。コピペする時にいちいち削除するのが面倒くさいので、最後のパラメータだけを参照するようにしている

(3)コマンド実行可否を問うプロンプトに y, n, q のいずれかで答える

$ git clone git@bitbucket.org:USER/REPO.git
[Y]es: execute, [N]o: skip, [Q]uit ?:

次のようなコマンドが発行される

  1. git clone git@bitbucket.org:USER/REPO.git
  2. gh repo create --private USER/REPO -d "From git@bitbucket.org:USER/REPO.git" --disable-wiki --disable-issues
  3. git remote add github git@github.com:USER/REPO.git
  4. gh browse
  5. throw-trash REPO ← ローカルに展開したワークフォルダーをゴミ箱に捨てる

ウェブブラウザで GitHub へのコピー結果と Bitbucket の内容を軽く確認して、問題なければ Bitbucket 側のレポジトリを削除する。まぁ、確認といっても、最新のコミットの内容・ブランチが全部そろってるか・Download データがないか、くらいしか見てられないが