
バージョン管理なしのソースをGitに載せる手順|安全な最初の一歩
本番サーバーの公開ディレクトリを ls したときの、あの光景。
index.php
index_old.php
index_new.php
index_20190312.php.bak
index_php_修正版.php
common.php
common.php.org
どれが今、動いているんでしょうか。
触りたくないですよね。消したいけれど、消していいのか分からない。分からないから、また index_20260807.php.bak を作って、その上から直す。そうやって増えてきたファイルたちです。
このやり方を選んだ人が悪いわけではありません。バージョン管理の仕組みが無い場所で、動いているものを壊さないようにするには、これがいちばん確実な方法だったというだけです。.bak を残すのは、履歴を残そうとした跡です。方向は、まったく間違っていません。
結論:Gitに載せる作業は、5つに分けると安全に進みます。①いま本番で動いているものを確定する。②公開領域の外に、まるごとコピーを作る。③最初のコミットを打つ前に、パスワードが書かれた設定ファイルと生成物を.gitignoreで外す。④「今の本番=これ」という基準点のコミットを1つ打つ。⑤しばらくは、本番との差分を見るだけに使う。
いちばん大事なのは、本番サーバーの公開ディレクトリでgit initしないこと。ここだけ守れば、あとは何度でもやり直せます。
先に安心材料をひとつ。この作業は、本番のファイルを1バイトも書き換えません。 やるのは「コピーを取って、手元で記録を始める」だけです。コピーを取る作業なので、失敗しても本番は動き続けます。だから、思っているよりずっと気楽に始められます。
Gitでデプロイする、ブランチを切って開発する——そういう話は、今日はしません。今日は「記録を始める」ところまでです。
何が起きているか:.bak は、たった一つ残った履歴
バージョン管理がないシステムは、たいてい次のどれかの事情でそうなっています。
- 作った会社が撤退した/担当者が辞めた。ソースは本番サーバーにしか無い
- 立ち上げ当時(10年以上前など)は、FTPで直接上げるのが普通のやり方だった
- 何度か「Gitを入れよう」という話は出たが、動いているものを触るリスクのほうが大きくて止まった
- 途中から入った自分は、そもそも「入れていいのか」を判断する立場にない
どれも、担当者の技術力の問題ではありません。引き継ぎの回数と、動いているものを止められない事情が積み重なった結果です。
そして、この状態で本当に困るのは、次の3つです。
- 誰が・いつ・なぜ変えたかが分からない。
common.php.orgは、いつのcommon.phpなのか。誰も答えられません。 - 戻せない。事故ったときに戻す先が「たぶんこの
.bak」しかない。バックアップから戻すにしても、どの時点に戻すのが正解かが分からない。 - 本番を直接触る以外の方法がない。だから、毎回の改修が緊張します。
Gitを入れることで、この3つが順番に解けていきます。ただし、入れた瞬間に全部解けるわけではありません。最初に手に入るのは、3つ目——「本番を直接触らなくていい状態への一歩目」でもなく、もっと地味なものです。
「今の本番は、確かにこれだった」と言える基準点。 これが最初の成果です。
地味に見えますが、これがあるだけで、次に何かを直したときに「前と何が変わったか」が機械的に出せるようになります。.bak を作る必要が、その日からなくなります。
始める前に:やってはいけないこと3つ
先に、事故になるパターンだけ潰しておきます。ここは慎重にいきます。
① 本番の公開ディレクトリで git init しない
これがいちばん大事です。
git init を実行すると、そのディレクトリに .git という隠しフォルダができます。この中には、過去のソース全部が入っています。
そして、公開ディレクトリ(/var/www/html や public_html など、Webからアクセスできる場所)に .git があると、設定によってはブラウザから中身を取れてしまうことがあります。.git/config にリモートの認証情報が入っていれば、それも一緒に。
Webサーバーの設定で .git へのアクセスを拒否すれば防げますが、そもそも公開領域に置かないほうが、考えることが減ります。
公開ディレクトリに置いてはいけないものの話は、管理画面のアクセス制限をかける|IP制限・Basic認証の基本にも通じます。「Webから見える場所には、見せていいものだけ置く」——原則はこれだけです。
② いきなり本番に git pull しない
Gitに載せると、次にやりたくなるのが「本番でも git pull すれば楽じゃないか」です。気持ちは分かりますが、まだです。
本番のファイルとリポジトリの中身が完全に一致していない状態で git pull や git checkout を叩くと、本番のファイルが上書きされたり消えたりします。「載せたつもり」の段階では、必ずどこかがズレています。
デプロイの自動化は、リポジトリと本番が一致していることを何度か確認してからの話です。今日はやりません。
③ 最初のコミットの前に、機密情報を外す
config.php にDBのパスワードが直書きされている——これは、レガシーなシステムではごく普通のことです。
問題は、一度コミットしてしまうと、あとから消すのが大変なところです。ファイルを削除して次のコミットを打っても、過去のコミットの中には残り続けます。履歴から完全に消すには git filter-repo のような専用ツールで履歴を書き換える必要があり、リモートに上げたあとなら関係者全員の作業も要ります。
最初のコミットを打つ前なら、.gitignore に1行足すだけで済みます。 手順3で、ここを丁寧にやります。
手順1:いま動いているものを確定する(15分)
コピーを取る前に、どこをコピーするかを確かめます。ここを間違えると、動いていない古いディレクトリを一生懸命Gitに載せることになります。
公開されている場所を、設定から確認する
推測ではなく、設定ファイルから引きます。
# Apache の場合(DocumentRoot の行を探す)
grep -ri "DocumentRoot" /etc/httpd/conf/ /etc/apache2/ 2>/dev/null
# Nginx の場合(root の行を探す)
grep -ri "root " /etc/nginx/conf.d/ /etc/nginx/sites-enabled/ 2>/dev/null
出てきたパスが、Webから見えている場所です。バーチャルホストが複数あるなら、対象のドメインの分だけを見ます。
シンボリックリンクに注意する
ls -l で、矢印(->)が出ていないかを見ます。
ls -l /var/www/
# lrwxrwxrwx 1 root root 22 Mar 12 2019 html -> /opt/app/releases/v3
こうなっていたら、実体は /opt/app/releases/v3 のほうです。リンク先をコピーします。
「ここだけじゃない」ものを洗い出す
公開ディレクトリの外にも、システムの一部が置かれていることがよくあります。忘れやすいのは次のあたりです。
- cronで動くバッチ(
/opt/batch/や/home/xxx/scripts/など) - cron の定義そのもの(
crontab -l、/etc/cron.d/) - Webサーバー・PHPなどの設定ファイル
- 共通ライブラリ(公開ディレクトリの1つ上に置かれていることがある)
全部を今日Gitに載せる必要はありません。「ある」ということをメモしておくだけで十分です。まずはWebの公開ディレクトリ1つから始めます。
どこに何があるかの棚卸しそのものは、サーバー・サービスの構成情報を1枚にまとめる方法や引き継ぎ初日にやることと同じ作業です。すでに1枚あるなら、それを見ながら進めるのがいちばん早いです。
手順2:公開領域の外に、まるごとコピーを作る(10分)

場所が決まったら、コピーします。本番のファイルには一切触りません。読むだけです。
置き場所は、次のどちらかです。
- 自分の作業用PC(いちばん安全。落として作業する)
- サーバー上の、公開領域の外(
/home/自分/work/など。ダウンロードが重いときはこちら)
サーバー上でやるときは、ディレクトリが本当に公開領域の外かどうかを、手順1で確認したパスと見比べてから始めてください。
# サーバー上にコピーを作る場合
mkdir -p ~/work/myapp
rsync -a --exclude='.git' /var/www/html/ ~/work/myapp/
# 手元のPCに落とす場合(サーバーからは読むだけ)
rsync -avz サーバー:/var/www/html/ ~/work/myapp/
rsync が使えない環境なら、cp -a や tar でも構いません。ポイントは、コピー元のパスの末尾に / を付けること(付けないとディレクトリごと入れ子になります)。
コピーしたら、まず大きさを見る
du -sh ~/work/myapp
du -sh ~/work/myapp/* | sort -rh | head -20
ここで数GBあるなら、たいていアップロードされた画像やログが混ざっています。それらは手順3で外すので、慌てなくて大丈夫です。
Gitが覚えてくれないもの
先に知っておくと、あとで悩まずに済みます。Gitはファイルの所有者や、細かいパーミッションを記録しません。 実行権限が付いているかどうか(755 か 644 か、程度)だけを覚えます。
つまり、Gitから戻しても、本番のパーミッションや所有者は復元されません。ここは別途メモに残すのが実務です。
# パーミッションと所有者を一覧で控えておく
find /var/www/html -printf '%m %u:%g %p\n' > ~/work/permissions.txt
この1行を取っておくだけで、あとで「あれ、このディレクトリの権限どうだったっけ」がなくなります。
手順3:.gitignore で仕分ける(ここがいちばん大事・30分)
コミットする前に、入れないものを決めます。この作業が、今日のいちばんの山場です。
.gitignore は、コピーしたディレクトリの直下に置くテキストファイルです。ここに書いたパターンに当てはまるファイルは、Gitが記録しなくなります。
まず、機密情報を探す
自分の目で確認します。
cd ~/work/myapp
# パスワードらしき記述を含むファイルを探す
grep -ril "password\|passwd\|secret\|api_key\|apikey" --include="*.php" --include="*.inc" --include="*.conf" . | head -30
出てきたファイルを実際に開いて、本物の値が書かれているかを見ます。変数名だけなら問題ありません。値が書かれていたら、.gitignore の対象です。
// これは外す
define('DB_PASSWORD', 'P@ssw0rd2019');
設定ファイルを丸ごと外すと、代わりに何が必要かが分からなくなるので、値を消した見本を1つ置いておくのが定番のやり方です。
# 本体は外し、値を伏せた見本だけをGitに入れる
cp config.php config.php.sample
# → config.php.sample の中身を編集して、パスワード等を空にする
認証情報そのものの扱いはパスワード・APIキーの平文保管をやめるに、環境ごとに値が違う設定の管理は環境差のある設定をどう管理するかに整理しています。今日は「Gitに入れない」ところまでで十分です。
.gitignore の雛形
そのままコピーして、自分の環境に合わせて削ってください。分からない行は残しておいて構いません(余分に除外しても事故にはなりません)。
# --- 機密情報(最優先で外す)---
config.php
config.local.php
.env
*.pem
*.key
id_rsa*
# --- 利用者がアップロードしたファイル ---
uploads/
upload/
files/
public/media/
# --- ログ・キャッシュ・一時ファイル ---
*.log
logs/
log/
cache/
tmp/
sessions/
*.swp
*~
# --- 自動で取ってこられるもの ---
vendor/
node_modules/
# --- データベースのダンプ ---
*.sql
*.sql.gz
*.dump
# --- OSが勝手に作るもの ---
.DS_Store
Thumbs.db
.bak や _old を外すかどうかは、「今日は入れる」でいい
迷うところですが、最初のコミットには、そのまま全部入れてしまうのがおすすめです。
理由は2つあります。
- 消していいかの判断が、今はできないから。
index_old.phpがどこかからrequireされている可能性もあります - 一度コミットしてしまえば、あとから安心して消せるから。Gitに入っていれば、消しても履歴から取り出せます
つまり、.bak ファイルの掃除は、Gitに載せたあとにやるべき仕事です。順番が逆になると、消した瞬間に取り返しがつかなくなります。今日は掃除しません。
除外できているか、コミット前に確認する
.gitignore を書いたら、リポジトリを作って、入るファイルの一覧を先に見ます。
cd ~/work/myapp
git init -b main
git add -A
# 何が入ろうとしているかを一覧で確認(コミットはまだしない)
git status --short | head -50
# ファイル数と、いちばん大きいファイルを確認
git status --short | wc -l
git ls-files -z | xargs -0 du -h 2>/dev/null | sort -rh | head -10
ここで、先ほど見つけた機密ファイルが一覧に出ていないことを確認します。出ていたら .gitignore を直して、もう一度。
# .gitignore を直したら、いったん取り消してやり直す
git rm -r --cached . -q
git add -A
git status --short | head -50
git rm --cached はインデックス(コミット待ちの一覧)から外すだけで、実ファイルは消えません。何度でもやり直せます。
この確認を1回入れるかどうかが、あとで履歴を書き換える羽目になるかどうかの分かれ目です。5分だけ、丁寧にいきましょう。
手順4:基準点のコミットを1つ打つ(5分)
確認が終わったら、コミットします。
git -c user.name="運用担当" -c user.email="you@example.com" \
commit -m "初期取り込み: 2026-08-07時点の本番ソース(無変更)"
コミットメッセージには、「いつ時点の」「どこから取った」ものかを書いておきます。半年後の自分が読む唯一の手がかりです。
初期取り込み: 2026-08-07時点の本番ソース(無変更)
取得元: 本番サーバー xxx.example.jp:/var/www/html
取得方法: rsync -a(ファイルの変更なし)
除外: config.php, uploads/, logs/, *.bak以外の一時ファイル
パーミッション記録: work/permissions.txt に別途保管
これで、「2026-08-07の本番は、確かにこれだった」という基準点ができました。今日の目的は、ここまでです。
置き場所は、あとから決めていい
「リモートリポジトリ(GitHubなど)を用意しないと意味がないのでは」と思うかもしれませんが、ローカルだけでも、この時点ですでに価値があります。
とはいえ、手元のPCだけだと壊れたら終わりなので、置き場所はいずれ決めます。選択肢はこのあたりです。
- 社内で使っているサービスに相乗りする(GitHub / GitLab / Backlog など、すでに契約があるもの)
- 非公開リポジトリを新しく作る。個人利用の範囲なら無料枠で非公開リポジトリを作れるサービスもあります
- 社内のサーバーに置く(外部にソースを出せない事情がある場合)
必ず「非公開(プライベート)」で作ってください。 ここだけは、間違えると影響が大きい設定です。
会社の判断が要る話なので、今日決まらなくて大丈夫です。ローカルにコミットが1つあるだけで、「Git化の検討はここまで進んでいます」と説明できる状態にはなっています。
「なぜGitを入れるのか」を上に説明する必要があるなら、技術的負債を見える化して優先順位をつけるの考え方が使えます。「戻せない状態を、戻せる状態にする」は、いちばん説明しやすい負債返済です。
手順5:しばらくは「差分を見る道具」として使う
コミットができたら、次にやるのはデプロイではありません。本番との差分を見ることです。
# 本番と手元のコピーで、何が違うかを見る(--dry-run なので何も変更しない)
rsync -avn --exclude-from='.gitignore' サーバー:/var/www/html/ ~/work/myapp/
-n(--dry-run)を付けているので、表示するだけで、実際には1ファイルも書き換えません。ここは必ず付けてください。
これを週に1回でも回すと、こういうことが分かります。
- 誰かがFTPで直接直した(覚えのないファイルが更新されている)
- アプリが自動生成したファイルが増えている(
.gitignoreに足す候補) - 何も変わっていない(=安定して動いている、という良い情報)
この「気づける状態」は、実はかなり大きい変化です。今まで、本番が変わったことに気づく手段が無かったわけですから。
変更が入ったら、コミットを1つ足す
以後、改修で本番を直したら、手元のコピーにも同じ変更を反映してコミットします。
git add -A
git commit -m "問い合わせフォームの必須チェックを追加(依頼: 総務部 2026-08-10)"
メッセージに「誰の依頼で」「何のために」を残すのがコツです。コードを見れば何を変えたかは分かりますが、なぜ変えたかは書かないと消えます。
この記録は、変更管理台帳の付け方|いつ・誰が・何を変えたか残すと同じ役割を果たします。台帳とGitの両方に書くのが二度手間なら、Gitのコミットメッセージを台帳の代わりにすると決めてしまうのも、一人運用では現実的な割り切りです。
ここから先は、急がなくていい
「本番でも git pull する」「ブランチを切る」「CIを回す」——どれも有用ですが、差分が安定して一致するようになってからで十分です。
数か月かけて、rsync -n で毎回「差分なし」が出るようになったら、そのとき次を考えます。一致していない状態で自動化を足すのが、いちばん事故りやすいので、ここは急がないのが正解です。
具体例:10年もののPHPサイトを載せてみる
実際の流れを、1本の線でたどってみます。
状況:制作会社が2015年に作った会社サイト+問い合わせフォーム。制作会社は数年前に廃業。ソースは本番サーバーのみ。公開ディレクトリは約4GB。
- 手順1:Apacheの設定を
grepしてDocumentRoot /var/www/htmlを確認。ls -lでシンボリックリンクは無し。crontab -lを叩いたら、深夜にバックアップスクリプトが1本動いていた(→ メモだけ残す) - 手順2:サーバー上の
~/work/corpsite/にrsync -aでコピー。du -shしたら 3.9GB。内訳を見るとuploads/が 3.6GB(過去の求人応募の添付ファイル) - 手順3:
grep -ril "password"でconfig.inc.phpにDBパスワードを発見。.gitignoreにconfig.inc.phpとuploads/と*.logを追加。config.inc.php.sampleを作って値を空に。git add -Aしてgit status --short | wc -l→ 412ファイル、合計18MB - 手順4:
git commit。メッセージに取得元と除外内容を記載 - 手順5:翌週
rsync -avnを実行したら、news/index.phpが更新されていた。聞いてみたら、広報担当の方がFTPソフトでお知らせを直接書き換えていた
3.9GB が 18MB になったのは、uploads/ を外したからです。ソースコードだけなら、たいてい数十MBに収まります。
そして5つ目。これは、この作業でいちばん価値のある発見でした。「なぜ更新した覚えがないのにファイルが変わっているのか」が分かると、運用の実態そのものが見えてきます。責める話ではなく、「その更新をどう安全に続けるか」を考える出発点です。
影響:Gitに載せると、現場は何が変わるか
.bakを作らなくてよくなる。直す前のファイルはGitが覚えています。公開ディレクトリが少しずつきれいになります- 戻せるようになる。
git diffで変更点を確認し、まずければgit checkoutで戻せます。改修のたびの緊張が、目に見えて減ります - 改修前後の差分を、そのまま報告に使える。
git diff --statの出力は、そのまま「何を変えたか」の説明になります(→改修の影響範囲を見落とさない調査の順番) - 本番が勝手に変わったことに気づける。手順5の差分チェックが、簡易的な改ざん検知にもなります
- 引き継ぎができるようになる。次の人に渡せるのが「サーバーのFTP情報」だけなのと、「リポジトリと変更履歴」があるのとでは、まったく違います
一方で、これで解決しないことも正直に書いておきます。
- バックアップの代わりにはなりません。Gitに入るのはソースだけで、データベースやアップロードファイルは別です(→バックアップから本当に戻せるか確かめる復元テストの手順)
- テストの代わりにもなりません。戻せるようになっただけで、壊れないわけではありません(→テストがないコードに手を入れる前の安全策)
- パーミッションと所有者は復元されません。手順2で控えた一覧が要ります
時間の目安です。手順1(場所の確認)で15分、手順2(コピー)で10分(転送待ちを除く)、手順3(仕分け)で30分、手順4(コミット)で5分。合わせて1時間ほどです。いちばん時間がかかるのは手順3ですが、ここが本番のようなものなので、時間をかける価値があります。
明日やること:手順1と2まで、30分で
全部を明日やらなくて大丈夫です。コピーを取るところまでなら、リスクがゼロで、30分で終わります。
grep -ri "DocumentRoot"で、公開されている場所を確認する(5分)ls -lでシンボリックリンクが無いか見る(1分)crontab -lを叩いて、バッチがあるかだけメモする(2分)- 公開領域の外に
~/work/を作り、rsync -aでコピーする(10分) du -shで大きさを見て、大きいディレクトリを3つ書き出す(5分)find ... -printfでパーミッションの一覧を控える(2分)
ここまでで止めて構いません。本番には何もしていないので、途中で呼ばれても中断できます。
.gitignore と最初のコミットは、まとまった30分が取れる日に回しましょう。急いでコミットを打つより、機密ファイルを落ち着いて確認するほうが、ずっと大事です。
「Gitに載せる」チェックリスト
コピーして、進み具合の確認に使ってください。一度に全部そろえる必要はありません。
まず外せない最低ラインの3つです。
- 【最低ライン】本番の公開ディレクトリで
git initしていない - 【最低ライン】最初のコミットの前に、パスワードが書かれたファイルを
.gitignoreで外した - 【最低ライン】作業ディレクトリが、Webから見えない場所にある
ここから先は、順に埋めていく項目です。
手順1:場所の確認
-
DocumentRoot/rootを設定ファイルから確認した(推測ではなく) - シンボリックリンクの有無を
ls -lで見た - cron・バッチ・公開ディレクトリ外の共通ライブラリの存在をメモした
- 対象を「まずWebの公開ディレクトリ1つ」に絞った
手順2:コピー
- 本番のファイルは読むだけで、書き換えていない
- コピー元パスの末尾の
/を確認した -
du -shで全体の大きさと内訳を見た - パーミッションと所有者の一覧を別ファイルに控えた
手順3:仕分け(いちばん大事)
-
grep -ril "password\|secret\|api_key"で機密情報を探した - 見つかったファイルを実際に開いて、本物の値かどうかを目で確認した
- 外した設定ファイルの
.sampleを作り、値を空にした - アップロードファイル・ログ・キャッシュ・
vendor/・node_modules/を除外した -
*.sqlなどのDBダンプを除外した -
git add -Aのあとgit status --shortで入るファイルの一覧を目視した - 機密ファイルが一覧に出ていないことを確認した
-
.bakや_oldは、今日は消さずにそのまま入れた
手順4:最初のコミット
- コミットメッセージに「いつ時点」「取得元」「除外内容」を書いた
- リモートに置くなら、非公開(プライベート)で作った
手順5:そのあと
-
rsync -avn(-n必須)で本番との差分を見る習慣を作った - 改修したら、手元にも反映してコミットを打つと決めた
- 本番での
git pullは、差分が安定するまでやらないと決めた
全部に○が付かなくて大丈夫です。最低ラインの3つ、とくに「本番で git init していない」さえ言えれば、いちばんこわい事故は避けられています。
最後に
index_20190312.php.bak を作った人は、たぶん不安だったのだと思います。戻せる状態にしておきたかった。 その気持ちは、Gitを入れようとしている今のあなたと、まったく同じものです。
やり方が違うだけで、目的は最初から一致しています。だから、この作業は「間違いを正す」仕事ではありません。同じ目的を、もう少し楽な道具でやり直すだけの仕事です。
そして、最初のコミットを1つ打った瞬間から、あなたのシステムには「歴史」が生まれます。今日より前のことは分からなくても、今日から先のことは全部残る。半年後、「これ、いつ変えたんでしたっけ」と聞かれて、git log を1回叩いて答えられる日が来ます。
その日のために今日やるのは、コピーを1つ取ることだけです。本番には触りません。失敗しようがありません。
rsync を1回、叩いてみるところからで十分です。

よければ、こちらも
レガシーなシステムを引き受けたときは、この記事の前後にやることがいくつかあります。順番に見ておくと、迷いが減ります。
- 作った人がいないシステムを引き継いだ初日にやること:まだ全体像がつかめていないなら、先にこちらから。
- 仕様書がないシステムの仕様を現状から起こす進め方:ソースを載せたあと、「何をしているシステムか」を書き起こす番です。
- パスワード・APIキーの平文保管をやめる:
.gitignoreで外した設定ファイルの、その先の扱い方。 - 変更管理台帳の付け方|いつ・誰が・何を変えたか残す:コミットメッセージと台帳、どちらに何を書くかの整理に。
- 本番反映前のチェックリストと作業前バックアップの取り方:戻せる状態ができたら、次は「戻す前提の手順」です。
ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。