本番サーバーのディレクトリに似た名前のファイルがいくつも並んでいるのを見て、どれが今動いているものかを落ち着いて確かめようとしている保守運用の担当者

バージョン管理なしのソースを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 は、たった一つ残った履歴

バージョン管理がないシステムは、たいてい次のどれかの事情でそうなっています。

どれも、担当者の技術力の問題ではありません。引き継ぎの回数と、動いているものを止められない事情が積み重なった結果です。

そして、この状態で本当に困るのは、次の3つです。

  1. 誰が・いつ・なぜ変えたかが分からないcommon.php.org は、いつの common.php なのか。誰も答えられません。
  2. 戻せない。事故ったときに戻す先が「たぶんこの .bak」しかない。バックアップから戻すにしても、どの時点に戻すのが正解かが分からない。
  3. 本番を直接触る以外の方法がない。だから、毎回の改修が緊張します。

Gitを入れることで、この3つが順番に解けていきます。ただし、入れた瞬間に全部解けるわけではありません。最初に手に入るのは、3つ目——「本番を直接触らなくていい状態への一歩目」でもなく、もっと地味なものです。

「今の本番は、確かにこれだった」と言える基準点。 これが最初の成果です。

地味に見えますが、これがあるだけで、次に何かを直したときに「前と何が変わったか」が機械的に出せるようになります。.bak を作る必要が、その日からなくなります。

始める前に:やってはいけないこと3つ

先に、事故になるパターンだけ潰しておきます。ここは慎重にいきます。

① 本番の公開ディレクトリで git init しない

これがいちばん大事です。

git init を実行すると、そのディレクトリに .git という隠しフォルダができます。この中には、過去のソース全部が入っています。

そして、公開ディレクトリ(/var/www/htmlpublic_html など、Webからアクセスできる場所)に .git があると、設定によってはブラウザから中身を取れてしまうことがあります。.git/config にリモートの認証情報が入っていれば、それも一緒に。

Webサーバーの設定で .git へのアクセスを拒否すれば防げますが、そもそも公開領域に置かないほうが、考えることが減ります。

公開ディレクトリに置いてはいけないものの話は、管理画面のアクセス制限をかける|IP制限・Basic認証の基本にも通じます。「Webから見える場所には、見せていいものだけ置く」——原則はこれだけです。

② いきなり本番に git pull しない

Gitに載せると、次にやりたくなるのが「本番でも git pull すれば楽じゃないか」です。気持ちは分かりますが、まだです。

本番のファイルとリポジトリの中身が完全に一致していない状態で git pullgit 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 のほうです。リンク先をコピーします。

「ここだけじゃない」ものを洗い出す

公開ディレクトリの外にも、システムの一部が置かれていることがよくあります。忘れやすいのは次のあたりです。

全部を今日Gitに載せる必要はありません。「ある」ということをメモしておくだけで十分です。まずはWebの公開ディレクトリ1つから始めます。

どこに何があるかの棚卸しそのものは、サーバー・サービスの構成情報を1枚にまとめる方法引き継ぎ初日にやることと同じ作業です。すでに1枚あるなら、それを見ながら進めるのがいちばん早いです。

手順2:公開領域の外に、まるごとコピーを作る(10分)

本番サーバーのファイル一式を手元にコピーし、入れるものと入れないものに仕分けてから、一冊にまとめて記録する流れを表した図
本番はそのまま触らない。公開領域の外にコピーを作り、仕分けてから最初のコミットを打つ

場所が決まったら、コピーします。本番のファイルには一切触りません。読むだけです。

置き場所は、次のどちらかです。

サーバー上でやるときは、ディレクトリが本当に公開領域の外かどうかを、手順1で確認したパスと見比べてから始めてください。

# サーバー上にコピーを作る場合
mkdir -p ~/work/myapp
rsync -a --exclude='.git' /var/www/html/ ~/work/myapp/
# 手元のPCに落とす場合(サーバーからは読むだけ)
rsync -avz サーバー:/var/www/html/ ~/work/myapp/

rsync が使えない環境なら、cp -atar でも構いません。ポイントは、コピー元のパスの末尾に / を付けること(付けないとディレクトリごと入れ子になります)。

コピーしたら、まず大きさを見る

du -sh ~/work/myapp
du -sh ~/work/myapp/* | sort -rh | head -20

ここで数GBあるなら、たいていアップロードされた画像やログが混ざっています。それらは手順3で外すので、慌てなくて大丈夫です。

Gitが覚えてくれないもの

先に知っておくと、あとで悩まずに済みます。Gitはファイルの所有者や、細かいパーミッションを記録しません。 実行権限が付いているかどうか(755644 か、程度)だけを覚えます。

つまり、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つあります。

つまり、.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だけだと壊れたら終わりなので、置き場所はいずれ決めます。選択肢はこのあたりです。

必ず「非公開(プライベート)」で作ってください。 ここだけは、間違えると影響が大きい設定です。

会社の判断が要る話なので、今日決まらなくて大丈夫です。ローカルにコミットが1つあるだけで、「Git化の検討はここまで進んでいます」と説明できる状態にはなっています。

「なぜGitを入れるのか」を上に説明する必要があるなら、技術的負債を見える化して優先順位をつけるの考え方が使えます。「戻せない状態を、戻せる状態にする」は、いちばん説明しやすい負債返済です。

手順5:しばらくは「差分を見る道具」として使う

コミットができたら、次にやるのはデプロイではありません。本番との差分を見ることです。

# 本番と手元のコピーで、何が違うかを見る(--dry-run なので何も変更しない)
rsync -avn --exclude-from='.gitignore' サーバー:/var/www/html/ ~/work/myapp/

-n--dry-run)を付けているので、表示するだけで、実際には1ファイルも書き換えません。ここは必ず付けてください。

これを週に1回でも回すと、こういうことが分かります。

この「気づける状態」は、実はかなり大きい変化です。今まで、本番が変わったことに気づく手段が無かったわけですから。

変更が入ったら、コミットを1つ足す

以後、改修で本番を直したら、手元のコピーにも同じ変更を反映してコミットします。

git add -A
git commit -m "問い合わせフォームの必須チェックを追加(依頼: 総務部 2026-08-10)"

メッセージに「誰の依頼で」「何のために」を残すのがコツです。コードを見れば何を変えたかは分かりますが、なぜ変えたかは書かないと消えます。

この記録は、変更管理台帳の付け方|いつ・誰が・何を変えたか残すと同じ役割を果たします。台帳とGitの両方に書くのが二度手間なら、Gitのコミットメッセージを台帳の代わりにすると決めてしまうのも、一人運用では現実的な割り切りです。

ここから先は、急がなくていい

「本番でも git pull する」「ブランチを切る」「CIを回す」——どれも有用ですが、差分が安定して一致するようになってからで十分です。

数か月かけて、rsync -n で毎回「差分なし」が出るようになったら、そのとき次を考えます。一致していない状態で自動化を足すのが、いちばん事故りやすいので、ここは急がないのが正解です。

具体例:10年もののPHPサイトを載せてみる

実際の流れを、1本の線でたどってみます。

状況:制作会社が2015年に作った会社サイト+問い合わせフォーム。制作会社は数年前に廃業。ソースは本番サーバーのみ。公開ディレクトリは約4GB。

  1. 手順1:Apacheの設定を grep して DocumentRoot /var/www/html を確認。ls -l でシンボリックリンクは無し。crontab -l を叩いたら、深夜にバックアップスクリプトが1本動いていた(→ メモだけ残す)
  2. 手順2:サーバー上の ~/work/corpsite/rsync -a でコピー。du -sh したら 3.9GB。内訳を見ると uploads/ が 3.6GB(過去の求人応募の添付ファイル)
  3. 手順3grep -ril "password"config.inc.php にDBパスワードを発見。.gitignoreconfig.inc.phpuploads/*.log を追加。config.inc.php.sample を作って値を空に。git add -A して git status --short | wc -l412ファイル、合計18MB
  4. 手順4git commit。メッセージに取得元と除外内容を記載
  5. 手順5:翌週 rsync -avn を実行したら、news/index.php が更新されていた。聞いてみたら、広報担当の方がFTPソフトでお知らせを直接書き換えていた

3.9GB が 18MB になったのは、uploads/ を外したからです。ソースコードだけなら、たいてい数十MBに収まります。

そして5つ目。これは、この作業でいちばん価値のある発見でした。「なぜ更新した覚えがないのにファイルが変わっているのか」が分かると、運用の実態そのものが見えてきます。責める話ではなく、「その更新をどう安全に続けるか」を考える出発点です。

影響:Gitに載せると、現場は何が変わるか

一方で、これで解決しないことも正直に書いておきます。

時間の目安です。手順1(場所の確認)で15分、手順2(コピー)で10分(転送待ちを除く)、手順3(仕分け)で30分、手順4(コミット)で5分合わせて1時間ほどです。いちばん時間がかかるのは手順3ですが、ここが本番のようなものなので、時間をかける価値があります。

明日やること:手順1と2まで、30分で

全部を明日やらなくて大丈夫です。コピーを取るところまでなら、リスクがゼロで、30分で終わります。

  1. grep -ri "DocumentRoot" で、公開されている場所を確認する(5分
  2. ls -l でシンボリックリンクが無いか見る(1分
  3. crontab -l を叩いて、バッチがあるかだけメモする(2分
  4. 公開領域の~/work/ を作り、rsync -a でコピーする(10分
  5. du -sh で大きさを見て、大きいディレクトリを3つ書き出す(5分
  6. find ... -printf でパーミッションの一覧を控える(2分

ここまでで止めて構いません。本番には何もしていないので、途中で呼ばれても中断できます。

.gitignore と最初のコミットは、まとまった30分が取れる日に回しましょう。急いでコミットを打つより、機密ファイルを落ち着いて確認するほうが、ずっと大事です。

「Gitに載せる」チェックリスト

コピーして、進み具合の確認に使ってください。一度に全部そろえる必要はありません。

まず外せない最低ラインの3つです。

ここから先は、順に埋めていく項目です。

手順1:場所の確認

手順2:コピー

手順3:仕分け(いちばん大事)

手順4:最初のコミット

手順5:そのあと

全部に○が付かなくて大丈夫です。最低ラインの3つ、とくに「本番で git init していない」さえ言えれば、いちばんこわい事故は避けられています。

最後に

index_20190312.php.bak を作った人は、たぶん不安だったのだと思います。戻せる状態にしておきたかった。 その気持ちは、Gitを入れようとしている今のあなたと、まったく同じものです。

やり方が違うだけで、目的は最初から一致しています。だから、この作業は「間違いを正す」仕事ではありません。同じ目的を、もう少し楽な道具でやり直すだけの仕事です。

そして、最初のコミットを1つ打った瞬間から、あなたのシステムには「歴史」が生まれます。今日より前のことは分からなくても、今日から先のことは全部残る。半年後、「これ、いつ変えたんでしたっけ」と聞かれて、git log を1回叩いて答えられる日が来ます。

その日のために今日やるのは、コピーを1つ取ることだけです。本番には触りません。失敗しようがありません。

rsync を1回、叩いてみるところからで十分です。

本番のソースをGitに取り込み終えて、朝の光の差すデスクで肩の力が抜けた穏やかな表情をしている保守運用の担当者

よければ、こちらも

レガシーなシステムを引き受けたときは、この記事の前後にやることがいくつかあります。順番に見ておくと、迷いが減ります。

ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。

関連用語