ローカルで作成したプロジェクトをGitHubへ公開するときは、Gitの初期化、READMEの作成、コミット、リモートリポジトリの作成、originの登録、pushという複数の操作が必要です。
毎回同じコマンドを入力するなら、Windowsバッチにまとめると作業を大幅に簡略化できます。本記事では、現在のフォルダ名をリポジトリ名として利用し、GitHubへの公開までを自動化するバッチを作ります。
結論:古いoriginを処理する機能は必要
古いoriginが残っているケースへの対処は入れておくべきです。
たとえば、別アカウントのGitHubリポジトリを登録していた場合や、プロジェクトフォルダをコピーして再利用した場合、以前のoriginが残ることがあります。その状態でpushすると、意図しないリポジトリへ送信する危険があります。
ただし、異なるoriginを見つけた瞬間に自動削除する設計は安全とはいえません。バッチを間違ったフォルダで実行した可能性もあるためです。
そこで、次の方針にします。
originがなければ追加するoriginが期待するURLなら変更しない- 異なるURLなら現在値と変更先を表示する
- ユーザーが承認した場合だけ
git remote set-urlで変更する
git remote remove originとgit remote add originを連続実行する方法でもURLは変更できますが、URL変更が目的ならgit remote set-url origin URLの方が意図が明確です。
このバッチで自動化する処理
完成版では次の処理を行います。
- GitとGitHub CLIの存在確認
- GitHub CLIのログイン確認
- ログイン中のGitHubユーザー名取得
- 現在のフォルダ名をリポジトリ名として取得
- Gitリポジトリの初期化
README.mdの作成.gitignoreの作成、または必要項目の追記- ファイルのステージングとコミット
- ブランチ名を
mainへ統一 - 古い
originの検出と安全な修正 - GitHubリポジトリの存在確認
- 未作成ならPublicまたはPrivateを質問して作成
mainブランチをpush
完成版バッチ
次の内容を、たとえばpublish-to-github.batという名前で保存します。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
REM =========================================
REM Current folder -> GitHub repository
REM =========================================
for %%I in (.) do set "DIRNAME=%%~nxI"
REM GitHub repository names cannot contain spaces.
REM Replace spaces in the folder name with hyphens.
set "REPONAME=%DIRNAME: =-%"
echo =========================================
echo Folder name: %DIRNAME%
echo Repository name: %REPONAME%
echo =========================================
echo.
REM -----------------------------------------
REM Check required commands
REM -----------------------------------------
git --version >nul 2>&1
if errorlevel 1 (
echo ERROR: Git is not installed.
goto :error
)
gh --version >nul 2>&1
if errorlevel 1 (
echo ERROR: GitHub CLI "gh" is not installed.
echo Install GitHub CLI first.
goto :error
)
gh auth status >nul 2>&1
if errorlevel 1 (
echo ERROR: GitHub CLI is not logged in.
echo.
echo Run:
echo gh auth login
goto :error
)
REM -----------------------------------------
REM Get current GitHub username
REM -----------------------------------------
for /f "delims=" %%A in ('gh api user --jq ".login"') do set "GHUSER=%%A"
if not defined GHUSER (
echo ERROR: Could not get GitHub username.
goto :error
)
set "REPO=%GHUSER%/%REPONAME%"
set "EXPECTED_HTTPS=https://github.com/%REPO%.git"
set "EXPECTED_SSH=git@github.com:%REPO%.git"
echo GitHub user: %GHUSER%
echo Repository: %REPO%
echo.
REM -----------------------------------------
REM Initialize Git repository
REM -----------------------------------------
if not exist ".git" (
echo [1/8] Initializing Git repository
git init
if errorlevel 1 goto :error
) else (
echo [1/8] Git repository already exists
)
REM -----------------------------------------
REM Create README.md
REM -----------------------------------------
if not exist "README.md" (
echo [2/8] Creating README.md
>"README.md" echo # %DIRNAME%
if errorlevel 1 goto :error
) else (
echo [2/8] README.md already exists
)
REM -----------------------------------------
REM Create or update .gitignore
REM -----------------------------------------
echo [3/8] Checking .gitignore
if not exist ".gitignore" type nul >".gitignore"
call :append_ignore ".env"
call :append_ignore ".env.*"
call :append_ignore "!.env.example"
call :append_ignore ".venv/"
call :append_ignore "venv/"
call :append_ignore "__pycache__/"
call :append_ignore "*.pyc"
call :append_ignore "node_modules/"
call :append_ignore "dist/"
call :append_ignore ".vscode/"
call :append_ignore ".idea/"
call :append_ignore "desktop.ini"
call :append_ignore "Thumbs.db"
call :append_ignore ".DS_Store"
REM -----------------------------------------
REM Add and commit files
REM -----------------------------------------
echo [4/8] Staging files
git add .
if errorlevel 1 goto :error
git diff --cached --quiet
if errorlevel 1 (
echo [5/8] Creating commit
git commit -m "Initial commit"
if errorlevel 1 goto :error
) else (
echo [5/8] Nothing to commit
)
REM -----------------------------------------
REM Rename current branch to main
REM -----------------------------------------
echo [6/8] Setting branch to main
git branch -M main
if errorlevel 1 goto :error
REM -----------------------------------------
REM Check and repair origin
REM -----------------------------------------
echo [7/8] Checking origin
setlocal EnableDelayedExpansion
set "CURRENT_ORIGIN="
for /f "delims=" %%A in ('git remote get-url origin 2^>nul') do (
if not defined CURRENT_ORIGIN set "CURRENT_ORIGIN=%%A"
)
if not defined CURRENT_ORIGIN (
echo origin is not configured.
) else if /I "!CURRENT_ORIGIN!"=="%EXPECTED_HTTPS%" (
echo origin is correct: !CURRENT_ORIGIN!
) else if /I "!CURRENT_ORIGIN!"=="%EXPECTED_SSH%" (
echo origin is correct: !CURRENT_ORIGIN!
) else (
echo.
echo WARNING: origin points to a different repository.
echo Current: !CURRENT_ORIGIN!
echo Expected: %EXPECTED_HTTPS%
echo.
set "ANSWER="
set /p "ANSWER=Change origin to the expected repository? [y/N]: "
if not defined ANSWER set "ANSWER=N"
if /I not "!ANSWER!"=="Y" (
echo Operation cancelled. origin was not changed.
goto :error
)
git remote set-url origin "%EXPECTED_HTTPS%"
if errorlevel 1 goto :error
set "CURRENT_ORIGIN=%EXPECTED_HTTPS%"
echo origin was updated.
)
REM -----------------------------------------
REM Check whether GitHub repository exists
REM -----------------------------------------
echo Checking GitHub repository...
gh repo view "%REPO%" >nul 2>&1
if errorlevel 1 (
echo Repository does not exist.
echo.
call :select_visibility
if errorlevel 1 goto :error
echo.
echo Creating !REPO_VISIBILITY_NAME! repository...
gh repo create "%REPO%" !REPO_VISIBILITY!
if errorlevel 1 goto :error
) else (
echo GitHub repository already exists.
)
if not defined CURRENT_ORIGIN (
echo Adding origin...
git remote add origin "%EXPECTED_HTTPS%"
if errorlevel 1 goto :error
)
REM -----------------------------------------
REM Push
REM -----------------------------------------
echo [8/8] Pushing main to GitHub
git push -u origin main
if errorlevel 1 goto :error
goto :success
REM -----------------------------------------
REM Append one entry only when it is missing
REM -----------------------------------------
:append_ignore
findstr /l /x /c:%1 ".gitignore" >nul 2>&1
if errorlevel 1 >>".gitignore" echo %~1
exit /b 0
REM -----------------------------------------
REM Select repository visibility
REM -----------------------------------------
:select_visibility
echo Select repository visibility:
echo [1] Public - Anyone can view the repository
echo [2] Private - Only authorized users can view it
echo.
set "VISIBILITY_CHOICE="
set /p "VISIBILITY_CHOICE=Enter 1 or 2 [default: 2]: "
if not defined VISIBILITY_CHOICE set "VISIBILITY_CHOICE=2"
if "!VISIBILITY_CHOICE!"=="1" (
set "REPO_VISIBILITY=--public"
set "REPO_VISIBILITY_NAME=PUBLIC"
exit /b 0
)
if "!VISIBILITY_CHOICE!"=="2" (
set "REPO_VISIBILITY=--private"
set "REPO_VISIBILITY_NAME=PRIVATE"
exit /b 0
)
echo.
echo ERROR: Enter 1 or 2.
echo.
goto :select_visibility
REM -----------------------------------------
REM Success
REM -----------------------------------------
:success
echo.
echo =========================================
echo Completed successfully
echo =========================================
echo.
echo Repository:
echo https://github.com/%REPO%
echo.
pause
exit /b 0
REM -----------------------------------------
REM Error
REM -----------------------------------------
:error
echo.
echo =========================================
echo FAILED
echo =========================================
echo.
echo Check the error message above.
echo.
pause
exit /b 1
originとは何か
originは、ローカルリポジトリから見たリモートリポジトリの通称です。
次のコマンドで確認できます。
git remote -v
一般的には、次のようなURLが登録されています。
https://github.com/ユーザー名/リポジトリ名.git
またはSSH形式です。
git@github.com:ユーザー名/リポジトリ名.git
なぜoriginを無条件で削除しないのか
異なるoriginが見つかったからといって、必ずしも古くて不要とは限りません。
- 意図して別のリポジトリへ接続している
- フォーク元を登録している
- フォルダ名とリポジトリ名が異なる
- 組織アカウントのリポジトリを使用している
- バッチを別のフォルダで誤実行した
このため、現在のURLと期待するURLを表示し、承認を得てから変更します。
URL変更には次のコマンドを使います。
git remote set-url origin https://github.com/USER/REPOSITORY.git
この方法なら、originというリモート設定を削除して作り直す必要がありません。
既存の.gitignoreも更新する
元のバッチでは、.gitignoreがすでに存在すると何も処理しません。そのため、既存ファイルに.envが書かれていなければ、環境変数やAPIキーを誤ってコミットする可能性があります。
完成版では:append_ignoreサブルーチンを使い、項目が存在しない場合だけ追記します。
:append_ignore
findstr /l /x /c:%1 ".gitignore" >nul 2>&1
if errorlevel 1 >>".gitignore" echo %~1
exit /b 0
.env.*は.env.localなども除外します。一方、設定例として公開する.env.exampleは追跡できるように、後続行で!.env.exampleを指定しています。
完全一致検索を採用する理由
findstrには/lと/xを指定し、正規表現ではなく行全体の完全一致で確認しています。
findstr /l /x /c:%1 ".gitignore"
既存行に末尾の空白がある場合は同じ指定を追記する可能性があります。ただし、.gitignoreでは#がコメントになるのは行頭にある場合です。たとえば次の行は、node_modules/に対する行末コメントとして扱われるとは限らず、別のパターンになります。
node_modules/ # comment
この行をnode_modules/と同一とみなして追記を省略すると、意図した除外が有効にならない可能性があります。そのため、本バッチでは単純で予測可能な完全一致を維持します。
フォルダ名に空白が含まれる場合
コマンドの引数は引用符で囲んでいますが、それだけではGitHubのリポジトリ名として空白を使用できるようにはなりません。GitHubのリポジトリ名に使用できるのは、ASCII英数字と.、-、_で、最大100文字です。
なお、git add .の.は現在のディレクトリを表すため、現在のフォルダ名に空白が含まれていても、このコマンド自体には影響しません。問題になるのはGitHubへ渡すリポジトリ名です。
そこで、ローカルフォルダ名に含まれる空白をハイフンへ変換し、GitHub用の名前を別変数に保持します。
for %%I in (.) do set "DIRNAME=%%~nxI"
set "REPONAME=%DIRNAME: =-%"
たとえば、次のように変換されます。
| ローカルフォルダ名 | GitHubリポジトリ名 |
|---|---|
My Project | My-Project |
hydraulic tools | hydraulic-tools |
ローカルフォルダ名とREADMEの見出しには元のDIRNAMEを使い、GitHubのURLとリポジトリ作成にはREPONAMEを使います。
ENTER入力時の安全策
set /pは、何も入力せずEnterを押すと変数へ新しい値を書き込みません。本バッチでは入力前に変数を空にしたうえで、未入力なら明示的にNを設定します。
set "ANSWER="
set /p "ANSWER=Change origin to the expected repository? [y/N]: "
if not defined ANSWER set "ANSWER=N"
これにより、Enterだけならoriginを変更せず停止するという挙動がコード上でも明確になります。
git diff –cached –quietの意味
次のコマンドは、ステージング済みの変更があるかを終了コードで判定します。
git diff --cached --quiet
- 差分なし:終了コード
0 - 差分あり:終了コード
1
バッチでは、終了コードが1ならコミットを実行し、0ならNothing to commitと表示します。
GitHubリポジトリ作成時の処理
GitHub上に対象リポジトリが存在しない場合は、作成前に公開範囲を質問します。
Select repository visibility:
[1] Public - Anyone can view the repository
[2] Private - Only authorized users can view it
Enter 1 or 2 [default: 2]:
1:Publicリポジトリを作成2:Privateリポジトリを作成- Enterのみ:安全側のPrivateを選択
- それ以外:正しい値が入力されるまで再質問
選択結果は、GitHub CLIへ渡すオプションとして保存します。
if "!VISIBILITY_CHOICE!"=="1" (
set "REPO_VISIBILITY=--public"
)
if "!VISIBILITY_CHOICE!"=="2" (
set "REPO_VISIBILITY=--private"
)
GitHub CLIで非対話的にリポジトリを作成するときは、--public、--private、組織向けの--internalのいずれかを指定します。本バッチでは個人開発で一般的なPublicとPrivateの2択にしています。
完成版では、リポジトリ作成とpushを分けてエラー箇所を判別しやすくしています。
gh repo create "USER/REPOSITORY" --public
# または
gh repo create "USER/REPOSITORY" --private
git remote add origin https://github.com/USER/REPOSITORY.git
git push -u origin main
主なオプションは次のとおりです。
| オプション | 意味 |
--public | パブリックリポジトリとして作成する |
--private | プライベートリポジトリとして作成する |
GitHub CLIでは、--source=., --remote=origin, --pushを指定して作成・リモート登録・pushを一度に行うこともできます。ただし、既存originの有無によって処理を分岐する本バッチでは、各処理を分けた方が状態を管理しやすくなります。
実行前の準備
GitとGitHub CLIをインストールし、GitHub CLIへログインしておきます。
gh auth login
初回コミットで名前やメールアドレスの設定を求められる場合は、Gitのユーザー情報を登録します。
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
実行方法
- バッチファイルを公開したいプロジェクトフォルダへ置く
- バッチファイルを実行する
- 異なる
originが検出された場合は内容を確認する - 修正してよければ
yを入力する - 新規作成の場合はPublicまたはPrivateを選択する
- 完了後、表示されたGitHub URLを開く
注意点
GitHub側に既存コミットがある場合
GitHub上でREADMEなどを先に作成していると、ローカルとリモートの履歴が分岐し、pushが拒否される場合があります。この状態を自動的にforce pushで上書きするのは危険です。
このバッチでは、履歴が競合した場合はエラーで停止します。内容を確認したうえで、git pull、マージ、リベースなどの方法を個別に選ぶべきです。
フォルダ名とリポジトリ名が異なる場合
このバッチは現在のフォルダ名を基本にしますが、空白はハイフンへ自動変換します。それ以外の名前にしたい場合は、REPONAMEを入力させる処理を追加するか、バッチ内で直接指定してください。
組織アカウントへ作成する場合
このバッチは、GitHub CLIでログインしている個人アカウント配下へリポジトリを作成します。組織配下へ作成する場合は、所有者名を別変数にして指定する必要があります。
既存プロジェクトへの適用
git branch -M mainは現在のブランチ名をmainへ変更します。すでに複数人で運用しているリポジトリや、ブランチ名を変更したくないプロジェクトでは、このバッチをそのまま実行しないでください。
まとめ
古いoriginを処理する機能は必要ですが、無条件に削除してはいけません。
実用的なバッチにするには、次の設計が適しています。
originの有無とURLを確認する- 正しいURLならそのまま利用する
- 異なる場合は変更前に確認する
- URL変更には
git remote set-urlを使う - 既存
.gitignoreにも不足項目を追記する - 新規作成時にPublicまたはPrivateを選択する
- フォルダ名の空白をGitHub用リポジトリ名ではハイフンへ変換する
- Enterだけでは古い
originを変更しない - リモート履歴の競合時はforce pushせず停止する
この構成なら、新規プロジェクトだけでなく、Git設定が一部残っているフォルダにも比較的安全に適用できます。

