秘密情報と共同作業|コマンドライン・Git・変更管理
このページはプロモーション(広告)を含みます。
コマンドライン・Git・変更管理(中級・読む35分+演習40分)
この授業のゴール
- APIキー・パスワード・個人データ・巨大データ・原本データのうち、リポジトリに入れてはいけないものを判定し、代わりの置き場所を示せる
- .gitignore と環境変数を使って、秘密情報とデータをcommitせずにコードを共有する構成を作れる
- 秘密情報をcommit・pushしてしまったときに、削除のcommitだけでは不十分な理由と、最初にすべき対応(無効化・報告)を説明できる
- clone・pull・branch・push・プルリクエスト・マージの共同作業の流れを、安全上の注意とともに説明できる
仕事でどう使うか
リポジトリは、チームの全員(公開すれば世界中)に履歴ごと配られます。一度commitして共有したものは、後のcommitで消しても過去の版として残り、他の人のMacにも写しがあります。だから、何を入れて何を入れないかを、commitの前に判断する必要があります。
| 路線 | この授業の使いどころ |
|---|---|
| DS | 顧客データ(個人データ)をリポジトリに入れず、コードと小さな架空サンプルだけを共有する。原本は書き換えない |
| ML | 学習データやモデルファイル(数百MB〜)をGitに入れず、保存場所と版・ハッシュ値を記録する。APIキーは環境変数で渡す |
| DX | 業務システムの接続情報・パスワードの管理ルールを決め、漏洩時の連絡・無効化の手順を整える |
| RS | 研究参加者の個人データを公開リポジトリに入れない。再現用には匿名化・集計済みのデータと取得手順を置く |
前提
前提:
- commit・履歴・
git show→ commitと履歴 - ブランチ・マージ・プルリクエストとレビュー → branchとmerge、競合とレビュー
- パスと作業用フォルダ → パスとコマンド
注意:この授業では、実際のAPIキーやパスワード、本物の個人データを一切使いません。例はすべて架空の値(
dummy-123など)です。個人情報の扱いは法令や所属組織の規程で決まります(執筆時点(2026年)の一般的な説明です。最新の規程は所属先や公式情報で確認してください)。
身近な状況:一度のpushで起きた3つの問題
ケンさんが、分析プロジェクトの共有リポジトリに次の3つを含むcommitをpushしました。
| 入れたもの | 何が起きたか |
|---|---|
app.py に書いたAPIキー API_KEY = "sk-live-..." |
リポジトリを読める全員がキーを使える状態になった。公開リポジトリなら、自動でキーを探すプログラムに数分で見つかることもある |
data/customers.csv(氏名・メールアドレス入り) |
閲覧権限のない人にも個人データが見える状態になった |
data/orders_2025.parquet(2GB) |
clone(複製)するたびに全員が2GBを取得。サービスの上限で push 自体が拒否されることもある |
ケンさんは慌てて3つを消すcommitを足しましたが、どれも履歴には残っています。この授業では、こうならないための構成と、なってしまったときの正しい対応を学びます。
直感的な説明:配られる本棚と、鍵のかかる引き出し
リポジトリは「チーム全員に複製して配られる本棚」です。しかも本棚には過去のすべての版が綴じられていて、新しい版で消しても古い版は残ります。
- 本棚に置くもの:コード、説明書(README)、設定の雛形、小さな架空サンプル、データの取得方法
- 鍵のかかる引き出しに置くもの:パスワード・APIキー(環境変数、パスワード管理の仕組み)
- 別の倉庫に置くもの:個人データ・巨大データ・原本(アクセス権のあるデータ置き場。本棚には「どの倉庫のどの版か」だけを書く)
たとえと実際の違い:本物の本棚なら1冊抜けば済みますが、Gitでは配った写しを回収できません。消すより「最初から置かない」ことと、「置いてしまったら鍵そのものを交換する(キーを無効化する)」ことが重要です。
正確な定義:入れないもの・.gitignore・環境変数・共同作業
リポジトリに入れないもの
| 種類 | 例 | 理由 | 代わりの置き場所 |
|---|---|---|---|
| 秘密情報 | APIキー、パスワード、トークン、秘密鍵、パスワード入りの接続文字列 | 読めた人が使えてしまう | 環境変数、.env(ignore済み)、組織のパスワード管理 |
| 個人データ | 氏名・メール・住所・顧客ID と購買の組み合わせ | 閲覧権限の範囲を超えて広がる | アクセス制御のあるデータ置き場 |
| 巨大データ・生成物 | 数百MBのCSV、学習済みモデル、ログ | clone が重くなり、履歴から消しても容量は減らない。サービスごとにファイルサイズの上限がある | データ置き場、Git LFS などの大容量ファイル用の仕組み |
| 原本 | 受け取ったままの元データ | 加工で上書きされると元に戻せない | 書き込まない置き場所に保管し、ハッシュ値を記録 |
| 個人の環境ファイル | .DS_Store、エディタの設定 |
プロジェクトと無関係 | 入れない(ignore) |
.gitignore:リポジトリの最上位に置くテキストファイルで、1行に1つ、Gitに無視させるパターンを書きます。
.env
data/raw/
*.pkl
.DS_Store
data/raw/ は「data/raw フォルダの中すべて」、*.pkl は「.pkl で終わるファイルすべて」です。無視されるのは untracked(未追跡)のファイルだけです。すでにcommitしたファイルは、.gitignore に書いても追跡され続けます(失敗1)。git check-ignore -v ファイル で、どのルールで無視されているかを確かめられます。
環境変数:プログラムの外から値を渡す仕組みです。Pythonでは os.environ.get('API_KEY') で読みます(無ければ None)。チームには、ダミー値だけを書いた .env.example をcommitして「どんな変数が必要か」を伝えます。
原本の確認:ファイルのハッシュ値(SHA-256 など。内容が1バイトでも変われば別の値になる)を受け取った日に記録しておくと、後で原本が書き換えられていないかを確かめられます。
共同作業の流れ:リモートリポジトリ(GitHubなどにある共有のリポジトリ。既定の名前は origin)を使います。
| 順 | コマンド | 働き |
|---|---|---|
| 1 | git clone URL |
リモートを手元に複製(最初の1回) |
| 2 | git pull |
リモートの最新を取り込む(取得+マージ) |
| 3 | git switch -c feature/作業名 |
作業用ブランチを作る |
| 4 | git add・git commit |
目的ごとに記録(commit前に status と diff --staged) |
| 5 | git push -u origin feature/作業名 |
自分のブランチをリモートに送る |
| 6 | プルリクエスト → レビュー → マージ | サービス上で確認して本線に入れる |
| 7 | git switch main・git pull |
本線の最新を手元に取り込む |
共有ブランチに対する git push --force(リモートの履歴を手元の内容で上書きする)は、他の人のcommitを消すことがあるので使いません。
段階付き例題:commit前の仕分けと、Macでの安全な設定
入力:新しい分析プロジェクトのフォルダに、次のファイルがあります。
| ファイル | 中身 | 判定 | 理由・代わりの扱い |
|---|---|---|---|
| src/clean.py | os.environ.get("API_KEY") を使うコード |
commit | 秘密は含まない |
| .env | API_KEY=sk-live-... |
ignore | 秘密情報 |
| .env.example | API_KEY=ここに自分のキーを書く |
commit | 必要な変数名を伝えるダミー |
| data/raw/customers_2025.csv | 氏名・メール | ignore | 個人データかつ原本。データ置き場に保管 |
| data/sample/orders_sample.csv | 架空の10行 | commit | テストと説明用。個人データを含まないことを確認済み |
| models/rf_v1.pkl | 350MBのモデル | ignore | 巨大な生成物。保存場所とハッシュ値をREADMEに記録 |
| .DS_Store | Finderの設定 | ignore | 個人の環境ファイル |
処理:上の判定から .gitignore を作ります。
.env
data/raw/
models/
*.pkl
.DS_Store
.env* のように書くと .env.example まで無視されてしまうので、.env と正確に書きます。
出力と意味:git status の untracked に .env・data/raw・models が出なくなり、git add . しても秘密・個人データ・巨大ファイルが入らない状態になります。ただし、最後の砦はcommit前に git status と git diff --staged を自分の目で見ることです。
Macのターミナルで試す(実Git):~/se01-practice/git-demo で、ダミーの値だけを使います。
cd ~/se01-practice/git-demo
echo "API_KEY=dummy-123" > .env
mkdir -p data/raw
echo "customer_id,amount" > data/raw/orders.csv
git status
printf '.env\ndata/raw/\n.DS_Store\n' > .gitignore
git status
git check-ignore -v .env
echo "API_KEY=ここに自分のキーを書く" > .env.example
git add .gitignore .env.example
git commit -m "秘密とデータを除外する.gitignoreと.env.exampleを追加"
1回目の git status には .env と data/ が untracked に出ます。.gitignore を作った後の2回目には出ず、代わりに .gitignore が出ます。git check-ignore -v .env は .gitignore:1:.env .env のように、どの行のルールで無視されたかを表示します。
環境変数の渡し方と、原本のハッシュ値の記録も試します。
API_KEY=dummy-123 python3 -c "import os; print(os.environ.get('API_KEY'))"
python3 -c "import os; print(os.environ.get('API_KEY'))"
shasum -a 256 data/raw/orders.csv
1行目は dummy-123、2行目は None と表示されます(変数=値 コマンド の形は、そのコマンドの実行中だけ環境変数を渡します)。shasum -a 256 はファイルのSHA-256を表示するmacOSのコマンドです。表示された値をREADMEなどに記録しておけば、原本が書き換えられていないかを後で照合できます。
リモートへのpushはGitHubなどのアカウントが必要なので、この授業では手順の理解までにとどめます。実際に使うときは、リポジトリを非公開(private)で作り、最初のpushの前に git log -p と git status で秘密やデータが含まれていないことを確かめてください。
操作して理解する:消しても残る履歴と、原本の指紋
ブラウザ内の模擬です。まず、APIキーを次のcommitで消しても、前のcommitに残ることを確かめます。
# 履歴は「その時点のファイル一式」の積み重ね(模擬)
history = [] # commit の列(古い順)
def commit(files, msg): history.append({'files': dict(files), 'msg': msg})
files = {'app.py': 'API_KEY = "sk-live-9f8e7d6c5b4a"\n'}
commit(files, '集計APIの呼び出しを追加')
files['app.py'] = 'import os\nAPI_KEY = os.environ["API_KEY"]\n' # 次のcommitで直した
commit(files, 'APIキーを環境変数から読むように変更')
print('最新の app.py :', history[-1]['files']['app.py'].splitlines()[1])
print('1つ前の app.py:', history[0]['files']['app.py'].strip()) # git show HEAD~1:app.py に相当
出力:
最新の app.py : API_KEY = os.environ["API_KEY"]
1つ前の app.py: API_KEY = "sk-live-9f8e7d6c5b4a"
最新版は安全に見えますが、履歴を1つさかのぼるだけでキーが読めます。リモートにpushしていれば、clone した全員の手元にもこの履歴があります。
次に、原本のハッシュ値(指紋)で、書き換えを検出します。hashlib.sha256(バイト列).hexdigest() は64文字の16進数の文字列を返します。
import hashlib
raw = 'customer_id,amount\n1,1200\n2,\n3,0\n' # 受け取った原本(2番は欠損、3番は0円)
recorded = hashlib.sha256(raw.encode('utf-8')).hexdigest() # 受け取った日に記録しておく値
print('記録した指紋:', recorded[:16], '...')
edited = raw.replace('2,\n', '2,0\n') # 誰かが欠損を0で埋めて上書きした
now = hashlib.sha256(edited.encode('utf-8')).hexdigest()
print('今の指紋 :', now[:16], '...')
print('原本のままか:', now == recorded)
出力(値の先頭16文字だけ表示):
記録した指紋: 48153a1e51849a95 ...
今の指紋 : d1ead906b58c0167 ...
原本のままか: False
指紋の値は内容だけから決まるので、同じ内容なら誰がいつ計算しても同じ値になります。見るべきは最後の行で、欠損を0で埋める上書き(「欠損」と「0円」の区別を壊す変更)が1か所あるだけで False になります。
発展:漏洩したときの対応と、仕組みによる予防
秘密情報をpushしてしまったときの順序
- キー・パスワードを無効化・再発行する(最優先。発行元の管理画面で行う)。履歴を消す作業より先に行います。
- 管理者・セキュリティ担当に報告する。いつ・どこに・どの範囲で公開されたかを伝える。個人データの場合は、組織の規程に従った報告が必要になることがあります。
- コードを環境変数から読む形に直し、.gitignore を整える。
- 履歴からの削除(
git filter-repoなどの履歴書き換えツール)は、チームと調整して行う。全員の手元の写しやフォークには残り得るので、削除だけでは安全にならないことを前提にします。
仕組みによる予防:GitHubなどのサービスには、pushされた内容からキーの形をした文字列を検出する機能(secret scanning)や、pushを止める機能があります(執筆時点の一般的な説明。機能の有無と設定は各サービスの公式情報で確認してください)。手元では、commitの前に検査を走らせる pre-commit フックを使う方法もあります。ただし、検出は文字列の形に頼るので完全ではありません。
メールアドレス:commitには user.email が記録されます。公開リポジトリに個人のアドレスを出したくない場合、サービスが提供する公開用のアドレス(GitHubなら noreply のアドレス)を設定する方法があります。
巨大データの版管理:Git LFS(大きなファイルを別の場所に置き、リポジトリには参照だけを入れる仕組み)や、データの版管理ツール(DVC など)があります。どれを使う場合も、データの置き場所・版・ハッシュ値・取得手順をREADMEに書くことが基本です。
よくある失敗
失敗1:commit済みの .env を .gitignore に書いて安心する
誤り:.env をcommitした後で気づき、.gitignore に .env を書いた。git status に何も出ないので解決したと思った。
なぜ誤りか:.gitignore が効くのは untracked のファイルだけです。追跡済みの .env は追跡され続け、次に編集すれば普通に変更として出ます。過去のcommitにもキーは残っています。
直し方:①キーを無効化・再発行する。②git rm --cached .env で「追跡をやめる」(作業ツリーのファイルは残る)。③.gitignore に .env を書いてcommitする。④新しいキーは .env にだけ書く。
失敗2:キーを消すcommitを足して「対応完了」とする
誤り:
c2 APIキーを削除 ← 最新版にはキーが無い
c1 集計APIの呼び出しを追加 ← ここにキーが残っている
「操作して理解する」の模擬のとおり、git show c1:app.py(1つ前の版の表示)でキーはそのまま読めます。pushしていれば、clone した人の手元にも c1 があります。
なぜ誤りか:Gitは「過去の版を取り出せる」ことが価値の道具なので、削除のcommitは「最新版から消えた」という記録にすぎません。
直し方:漏れたキーは漏れたものとして扱い、無効化・再発行する。報告する。そのうえで、必要ならチームと調整して履歴を書き換える。
失敗3:原本を直接加工し、巨大ファイルごとcommitする
誤り:受け取った customers_2025.csv(800MB)をそのまま data/ に置き、pandas で欠損を0埋めして同じファイル名で上書き保存し、git add . でcommitした。
| 問題 | 結果 |
|---|---|
| 原本の上書き | 「未入力」と「0」が区別できなくなり、元に戻せない(ハッシュ値の照合で False) |
| 巨大ファイルのcommit | 全員の clone が重くなり、後で消しても履歴の容量は減らない。push が拒否されることもある |
| 個人データのcommit | 閲覧権限の範囲を超えて個人データが広がる |
直し方:原本は data/raw/ など書き込まない場所に置いて .gitignore し、受け取った日にハッシュ値を記録する。加工結果は別のファイル(data/processed/)に出す。リポジトリには加工のコードと取得手順を入れる。
まとめ
- 秘密情報・個人データ・巨大データ・原本はリポジトリに入れない。commitは履歴ごと全員に配られる。
- .gitignore は untracked のファイルにだけ効く。commit済みのものは git rm --cached が必要で、履歴には残る。
- 秘密は環境変数(os.environ)と ignore済みの .env に置き、変数名は .env.example で共有する。
- 漏洩したら、削除のcommitより先にキーの無効化・再発行と報告。原本はハッシュ値で書き換えを検出する。
- 共同作業は pull → ブランチ → commit → push → プルリクエスト → レビュー → マージ。共有ブランチに push --force しない。
用語
- .gitignore(.gitignore)
Gitに無視させるファイルのパターンを1行ずつ書くファイル。untracked のファイルにだけ効く。
- 環境変数(environment variable)
プログラムの外から渡す名前付きの値。Pythonでは os.environ で読む。秘密情報をコードに書かずに渡すのに使う。
- リモートリポジトリ(remote repository)
GitHubなどにある共有のリポジトリ。既定の名前は origin。push で送り、pull で取り込む。
- 秘密情報(secret / credential)
APIキー・パスワード・トークン・秘密鍵など、知った人がシステムを使えてしまう情報。
- 原本(raw data)
受け取ったまま加工していない元データ。書き換えず保管し、ハッシュ値で同一性を確かめる。
よくある質問
非公開(private)リポジトリなら、APIキーを書いてもよいのでは?
よくありません。非公開でも、メンバー全員・将来のメンバー・連携したツールが読めます。メンバーのMacが盗まれたり、後で公開に切り替えたりすると漏れます。秘密情報は公開範囲にかかわらずリポジトリに入れません。
データを入れないなら、分析を再現するにはどうするのですか?
リポジトリに『データの置き場所・取得手順・版(日付やID)・ハッシュ値』をREADMEとして書き、加工はコードで再現できるようにします。テスト用には個人データを含まない小さな架空サンプルを入れます。
個人データかどうか迷ったら?
単独では個人がわからなくても、他の列と組み合わせて特定できるなら個人データとして扱うのが安全です。判断は所属組織の規程と担当者に確認します(法令や規程は変わり得るので、最新は公式情報で確認してください)。
どこで失敗しやすいですか?
①git add . で確認せずにcommit、②.gitignore を後から書いて追跡済みのファイルに効かない、③削除のcommitで対応完了と思う、④.env* のような広すぎるパターン、⑤原本を上書き保存、の5つです。
条件が変わって、共同研究者が別の組織の人だったら?
リポジトリの閲覧権限を相手に合わせて見直し、データは組織間の取り決め(共有範囲・保管場所・期間)に従って別経路で受け渡します。リポジトリにはコードと手順だけを置くのが基本です。
仕事でpushの前に最低限確かめることは?
①git status で意図しないファイルが無い、②git log -p や git diff --staged で秘密・個人データ・巨大ファイルが無い、③push先のリモートとブランチ名が正しい(git remote -v、git branch)、④リポジトリの公開範囲、の4つです。
確認問題
問1 APIキーを含むcommitを共有リポジトリにpushしてしまった。最初にすべきことはどれか。
- ① キーを消すcommitを追加してpushする
- ② 何もしない(気づかれなければ問題ない)
- ③ .gitignore に app.py を追加する
- ④ 発行元でキーを無効化・再発行し、管理者に報告する
解答と解説
正解:④
履歴と他人の写しにキーが残るので、キー自体を使えなくすることが最優先です。報告も早く行います。
問2 すでにcommit済みの .env を .gitignore に書いた。正しい説明はどれか。
- ① 以後Gitは自動で .env を履歴からも消す
- ② .gitignore は untracked のファイルにしか効かないので、.env は追跡され続ける
- ③ .env は作業ツリーから削除される
- ④ push できなくなる
解答と解説
正解:②
追跡をやめるには git rm --cached .env が必要で、過去のcommitにも残ります。
問3 350MBの学習済みモデルをリポジトリに入れない理由として最も適切なものはどれか。
- ① 全員の clone が重くなり、後で削除しても履歴の容量は減らず、サービスの上限で push が拒否されることもあるから
- ② Gitはバイナリファイルを一切扱えないから
- ③ モデルは毎回同じ内容になるから
- ④ モデルには必ず個人データが含まれるから
解答と解説
正解:①
履歴は全版が配られるため、巨大ファイルは全員の負担になり続けます。保存場所と版・ハッシュ値を記録する方法をとります。
問4 受け取った原本CSVについて、適切な扱いはどれか。
- ① 欠損を0で埋めて同じファイル名で上書きする
- ② 加工したらすぐ原本を削除する
- ③ リポジトリにcommitして履歴で守る
- ④ 書き込まない場所に保管してハッシュ値を記録し、加工結果は別ファイルに出す
解答と解説
正解:④
原本を残し、ハッシュ値で書き換えがないことを確かめられるようにします。加工はコードで再現します。
問5 チームの共同作業の流れとして適切な順序はどれか。
- ① main で直接commit → push --force
- ② clone のたびにリポジトリを作り直す
- ③ pull → ブランチ作成 → commit → ブランチを push → プルリクエストでレビュー → マージ
- ④ ブランチを作らずにzipで送る
解答と解説
正解:③
最新を取り込み、自分のブランチで作業し、レビューを経て本線に入れるのが基本の流れです。
問6 .env.example について正しい説明はどれか。
- ① 本物のキーを書いてcommitするファイル
- ② 必要な環境変数の名前とダミー値を書いてcommitし、各自が .env を作る手がかりにするファイル
- ③ Gitが自動で作るファイル
- ④ 必ず .gitignore で無視すべきファイル
解答と解説
正解:②
本物の値は各自の .env(ignore済み)に置き、.env.example は変数名を共有するためにcommitします。
次に学ぶこと
これで SE01 の6授業は終わりです。総合テストと、模擬Gitで競合を解いて差分と理由を残す自力課題で習得を確認します。
戻って確認するなら、パスとコマンドは パスとコマンド、3領域と差分は Gitと変更差分、commitと履歴は commitと履歴、ブランチとマージは branchとmerge、競合とレビューは 競合とレビュー です。次のモジュール 設計・型・パッケージ・依存(有料版) では、分析コードを責務ごとに分けて設計します(「責務と関数設計」(有料版))。依存の固定(「依存の固定」(有料版))でも、今回の .gitignore と環境変数の考え方を使います。
キャリア学習:データサイエンティスト・機械学習エンジニア・DX推進・研究者を目指す人の学習アプリ。164モジュール・984授業を、ブラウザで動くSQLとPythonの演習で。基礎と各分野の最初のモジュールは無料。全部は2,980円の買い切り。
アプリを無料で開く有料版と無料版の違い