プルリクエストを作成できない : git checkout --orphan <branch-name> が原因?

`git checkout --orphan` で作成したブランチでプルリクエストが作れない問題の解決法

GitHubなどで課題提出用にプルリクエストを作成しようとした際、ファイルの差分表示画面までは進めるものの、その先にある「プルリクエストを作成(Create Pull Request)」ボタンが表示されず作成できないという問題が発生しました。

結論: 原因は、今回 push したブランチが git checkout --orphan コマンドで作成されたものであったことでした。

なぜプルリクエストを作成できなかったのか?

git checkout --orphan を使用してブランチを作成すると、既存のコミット履歴を一切引き継がない「独立したブランチ(親コミットを持たない状態)」が作られます。

そのため、main ブランチなどの他ブランチとの間に関係性が存在せず、「共通の祖先(共通コミット)」を持たない状態になってしまっていました。共通のコミット履歴がないため、GitおよびGitHubが差分の比較基準を判定できず、プルリクエストの作成が行えなくなっていたのが原因です。

解決手順

リモートとローカルの作業ブランチを一度整理し、変更内容を正しい履歴を持ったブランチへと移し替えることで解決しました。

  1. push したリモートの作業ブランチをいったん削除する
    git push origin --delete <作業ブランチ名>
  2. ローカルの作業ブランチのバックアップを作成する
    git checkout -b <バックアップ用ブランチ名>
  3. main ブランチに戻って作業ブランチを削除し、改めてブランチを再作成する
    git checkout main
    git branch -D <削除する作業ブランチ名>
    git checkout -b <作業ブランチ名>
  4. 作業ブランチにバックアップで退避していた内容を反映する

    手順3で再作成した作業ブランチに切り替わっている状態で、以下のコマンドを実行してバックアップの内容を反映します。

    git checkout <バックアップブランチ名> -- .

この git checkout <バックアップブランチ名> -- . を実行することで、バックアップ用ブランチのカレントディレクトリにあるファイルを作業ブランチへ反映させることができます。

いきなり直接ファイルが上書きコミットされるわけではなく、現在のカレントディレクトリとの差分があるファイルがステージングされた状態(`git add` された状態)になります。そのため、変更内容をしっかり確認しながら安全にコミットを進めることが可能です。

バックアップ内容を反映させる他の手法との比較

今回は git checkout <ブランチ> -- . を採用しましたが、他の反映方法との違いは以下の通りです。

  • git checkout <バックアップブランチ> -- .(今回採用)
    • 他のブランチから持ってきた記録(コミット履歴)は残らない
    • コミット履歴ではなく、ファイル(ディレクトリ)の最新状態をコピーしてステージングする
  • git merge <バックアップブランチ>
    • 他のブランチから持ってきた記録が残る
    • バックアップブランチのコミット履歴を新しいブランチに結合させる
  • git cherry-pick <コミットID>
    • ほかのブランチから持ってきた情報は基本的には残らない
    • 特定コミットの「変更内容」だけを新しいコミットとして新規作成する(別のコミットIDになる)

自作アプリを Android 16(API レベル 36)対応版へアップデートしました

自作のアプリ「Simple Echo」をアップデートしました。

play.google.com

アプリをインストールされる方は、いつもをご利用いただきありがとうございます。

このたび、Google Play の最新要件に対応するため、アプリを Android 16(API レベル 36)に対応したバージョンへ更新しました。

今回の更新内容

  • Android 16(API レベル 36)への対応
  • 最新の Google Play の対象 API レベル要件への対応
  • アプリの互換性および安定性の向上

アップデートについて

今回の更新は、Android の最新環境への対応を目的としたメンテナンスアップデートです。

アプリの基本的な使い方や機能に変更はありませんので、これまでどおり安心してご利用いただけます。

今後もより快適にご利用いただけるよう、改善とメンテナンスを継続してまいります。引き続き「Simple Echo」をよろしくお願いいたします。

シェルスクリプトで特定キーワードを含むHTMLファイルのタイムスタンプを更新する

Linux のシェルスクリプトを学習する中で、特定のキーワードを含む HTML ファイルのタイムスタンプを更新する処理を作成した。

キーワード一覧をテキストファイルから読み込み、対象ディレクトリ内の HTML ファイルを検索し、該当するファイルに touch コマンドを実行する。

ファイル構成

.
├── update.sh
├── keywords.txt
└── articles/
    ├── page1.html
    ├── page2.html
    └── page3.html

キーワード一覧は keywords.txt に1行ずつ記述する。

foo
bar
buzz

シェルスクリプト

#!/bin/bash

KEYWORD_FILE="./keywords.txt"
TARGET_DIR="./articles/"

while read -r keyword
do
    grep -ril "$keyword" "$TARGET_DIR"
done < "$KEYWORD_FILE" | sort -u | while read -r file
do
    touch "$file"
    echo "updated: $file"
done

処理の流れ

  1. キーワード一覧ファイルを1行ずつ読み込む
  2. 各キーワードでHTMLファイルを検索する
  3. 一致したファイル名を出力する
  4. 重複したファイル名を除外する
  5. 対象ファイルのタイムスタンプを更新する

キーワードの読み込み

while read -r keyword
do
    ...
done < "$KEYWORD_FILE"

read コマンドがキーワードファイルを1行ずつ読み込み、その値を keyword 変数へ格納する。

例えば以下の内容の場合、

CB750
GILLES
HEPCO

ループごとにそれぞれの値が keyword に代入される。

grepによる検索

grep -ril "$keyword" "$TARGET_DIR"

対象ディレクトリ内を再帰的に検索し、キーワードを含むファイル名のみを出力する。

  • -r : 再帰検索
  • -i : 大文字小文字を区別しない
  • -l : 一致したファイル名のみ表示

sortによる重複除去

| sort -u

複数のキーワードが同じHTMLファイルに含まれている場合、同じファイル名が複数回出力される。

sort -u を使用することでソートと重複除去を同時に行うことができる。

touchによるタイムスタンプ更新

while read -r file
do
    touch "$file"
done

検索結果として渡されたファイル名を1件ずつ受け取り、touch コマンドでタイムスタンプを更新する。

ファイル内容は変更せず、更新日時のみ変更される。

シェルスクリプトらしいポイント

このスクリプトでは変数に検索結果を保存しているわけではなく、

grep → sort → while → touch

という形で標準出力と標準入力をパイプで接続している。

シェルスクリプトでは、データを変数へ格納して処理するよりも、コマンド同士をパイプでつなぎながら処理を流していく考え方がよく使われる。

まとめ

今回のスクリプトでは以下のコマンドを利用した。

  • while read
  • grep
  • sort -u
  • touch
  • パイプ (|)
  • リダイレクト (<)

シェルスクリプトの基本である「標準入力」「標準出力」「パイプ」を学ぶ題材として非常に良いサンプルになった。

vimtutorのChapter2を見る方法

下記のコマンドでvim チュートリアルのChapter2を見ることができます。

Chapter2起動コマンド
`vimtutor -c 2`

経緯

vimtutorで開くチュートリアルのまとめ部分に「Chapter2で(チュートリアルを)続けることを検討してください」と書かれていた。

しかし、そのChaper2を見る方法がわからず調査。

vim の公式サイトにチュートリアルに関しては`See :help tutor inside Vim.`と記載があったため実行。そこにChapter2の起動方法が書かれているのを発見。

【WSL2】Debianを入れようとしてISOファイルを落としたら、謎のDドライブが出現して消せなくなった話

WSL2環境にDebianを導入しようとした際、思い込みと知識不足から完全に手順を間違え、少し焦るトラブルに遭遇しました。

今回は「こうしようとしたら、こうなってしまった」という失敗談と、その原因、そして正しい状態への戻し方を、自身の作業メモを兼ねて共有します。


【間違いの原因】なぜISOファイルをダウンロードしてしまったのか?

まず、私がやろうとしたことは「WSL2でDebianを動かすこと」でした。そこで、何の疑いもなくDebianの公式サイトへ行き、以下のファイルをダウンロードしました。

debian-13.5.0-amd64-netinst.iso

私の頭の中には「新しくOSを導入するのだから、公式サイトから本体(インストーラー)を落としてくるのが普通だろう」という思い込み(これまでの知識)があったからです。

しかし、これが最初のボタンの掛け違いでした。実は、通常のPCへのインストールとWSL2へのインストールでは、必要となるファイルが根本から異なります。

💡 ここで学んだ知識のズレ
  • .isoファイルとは: まっさらなPC(ハードディスクが空の状態)に、イチからOSを組み立ててインストールするための「工具セット(起動用ディスクの中身)」です。
  • WSL2が必要とするもの: すでにWindowsという土台の上で動かすことが決まっているため、必要なのはインストーラーではなく、すでに組み立てが終わっていつでも動かせる状態の「Debianの本体(ファイル群)」だけです。

つまり、「OSを入れる=ISOを落とす」というこれまでの常識が、WSL2という仕組みにおいては不要(むしろ使えない)な手順だったのです。

【怪奇現象】消せないISOと、謎のDドライブの正体

間違いに気づいた私は、ダウンロードした .iso ファイルを削除しようとしました。しかし、システムから「アクセス中」と言われて削除できません。さらにエクスプローラーを見ると、身に覚えのない「Dドライブ(DVDドライブのアイコン)」が突然出現していました。

ダウンロードしただけなのに、なぜ勝手にドライブが増えて削除できなくなったのか? 原因はWindowsの親切な(お節介な)機能にありました。

💡 謎のDドライブの正体:マウント
Windowsは .iso ファイル(ディスクイメージ)がクリックされると、気を利かせて「本物のDVDドライブにディスクが挿入された」のと同じ状態をパソコン内にバーチャルに作り出します。これを「マウント」と呼びます。

私がファイルを触っているうちにWindowsがマウントを実行してしまい、「仮想のDVDドライブ(Dドライブ)」を作成。Windowsがその中身を読み込み中(使用中)にしてしまったため、元の .iso ファイルがロックされて削除できなくなっていた、というのが怪奇現象の真相でした。

【解消法】間違った状態を元に戻す2ステップ

この「使用中で消せない」「謎のドライブがある」という状態は、以下の手順で簡単に元に戻せます。

  1. 仮想ドライブの「取り出し」を行う
    エクスプローラーの「PC」を開き、出現した謎のDドライブ(DVDアイコン)を右クリックして「取り出し」を選択します。これでWindowsが「DVDをイジェクトした(抜いた)」状態になり、Dドライブが消滅します。
  2. 元のファイルを削除する
    アクセスが解除されるため、ダウンロードフォルダーにある debian-13.5.0-amd64-netinst.iso を通常通りゴミ箱へ削除できるようになります。

※もしこれでも「使用中」と出る場合は、バックグラウンドのプロセスが掴んだままになっているので、PCを一度再起動すれば確実に削除できます。

【正しい手順】WSL2でDebianを始める一番ラクな方法

散らかった環境を元に戻したところで、本来やりたかった「WSL2へのDebian導入」の正しい手順です。公式サイトからファイルを落とす必要は一切ありませんでした。

一番手っ取り早いのは、PowerShellまたはコマンドプロンプトを起動して、以下のコマンドを1行実行するだけです。

wsl --install -d Debian

これだけで、Microsoft Store経由で「WSL2用に最適化・組み立て済みのDebian」が自動的に降ってきます。あとは画面の指示に従ってユーザー名とパスワードを設定すれば、すぐに使い始めることができます。

まとめ

今回は「OSのインストールといえばISOファイル」という過去の経験による思い込みが原因で、遠回りをしてしまいました。

しかし、この失敗のおかげで「Windowsのマウントの仕組み」や「WSL2が裏側でどうLinuxを扱っているか」という構造をスッキリ理解することができました。同じ現象で「ファイルが消せない!」と焦っている方の参考になれば幸いです。

Docker学習記録② Docker DesktopでRailsコンテナを作成する

前回は Docker の基本概念について学習した。

今回は実際に Docker Desktop をインストールし、Rails が動作するコンテナを作成してブラウザからアクセスするところまで試した。

なおこの記事はChatGPTとの会話をChatGPTにブログ形式で出力させたものとなっております。

Docker Desktopをインストールした

Windows環境へ Docker Desktop をインストールした。

インストール時には、

  • Hyper-V
  • WSL2

の選択肢が表示されたが、今回は WSL2 を利用する設定を選択した。

現在の Docker Desktop では WSL2 を利用する構成が一般的となっている。

また、Docker Desktop の動作には内部的に Linux 環境が利用されている。

Welcome to Docker コンテナとは何か

Docker Desktop を起動すると、

welcome-to-docker

というコンテナが表示されていた。

これは Docker Desktop の動作確認やチュートリアル用のサンプルコンテナであり、ユーザーが作成したコンテナではない。

Docker Desktop の導入が正常に完了していることを確認するためのものらしい。

RailsイメージではなくRubyイメージを利用する理由

Docker Hub を確認すると Ruby の公式イメージは存在するが、Rails の公式イメージは見当たらなかった。

当初は不思議に思ったが、Rails は独立したアプリケーションではなく Ruby のライブラリである。

そのため一般的には、

FROM ruby:3.3

RUN gem install rails

のように Ruby イメージをベースとして Rails を追加インストールする。

Dockerfileを作成した

学習用として次の Dockerfile を作成した。

FROM ruby:3.3

WORKDIR /app

RUN gem install rails

CMD ["bash"]

この Dockerfile を build すると、

  • Linux
  • Ruby
  • Rails

がインストールされた独自イメージが作成される。

buildとrunの違いを体験した

Dockerを触り始めて最初に混乱したのが build と run の違いだった。

docker build -t my-rails .

を実行するとかなり時間がかかった。

これは Ruby イメージの取得や Rails のインストールが実行されているためである。

一方、

docker run

は作成済みのイメージからコンテナを生成するだけなので非常に高速だった。

実際に触ってみると、

  • build = 環境を作る
  • run = 環境を起動する

という違いが理解しやすかった。

CMD["bash"]の意味を理解した

Dockerfile の最後には

CMD ["bash"]

を記述した。

当初は Docker Desktop の Start ボタンを押せば bash が開くと思っていた。

しかし実際には何も表示されず、コンテナも停止してしまった。

調べてみると、

docker run -it my-rails

のように TTY を接続して起動しなければ、bash はすぐに終了してしまうことが分かった。

Docker Desktop の Start ボタンはコンテナを起動するだけであり、ターミナルへ接続する機能ではない。

コンテナが停止した理由

作成したコンテナの状態を確認すると、

Exited (0)

となっていた。

Dockerではメインプロセスが終了するとコンテナも終了する。

今回の場合、

bash

がメインプロセスだったため、

bash が終了するとコンテナも終了していた。

これは MySQL や nginx のような常駐プロセスとの大きな違いである。

Volumeを利用してローカルフォルダを共有した

Rails プロジェクトをコンテナ内だけに作成すると、コンテナを削除した際にソースコードも失われる。

そこで Volume を利用して、

Windowsフォルダ
⇔
コンテナの /app

を共有した。

これにより、コンテナ内で作成したファイルをホストOS側からも編集できるようになった。

rails newでプロジェクトを作成した

コンテナ内で

rails new .

を実行した。

すると Rails プロジェクト一式が生成された。

実行中には Dockerfile を上書きするか確認された。

これは Rails が独自の Dockerfile を生成しようとしたため。 Rails が生成する Dockerfile は「Railsアプリを配布・実行するための Dockerfile」 であり、今回は学習用に作成した Dockerfile を残すため上書きしなかった。

コンテナ削除後もファイルが残った理由

rails new 実行後にコンテナを削除した。

当初は Rails プロジェクトも消えたと思っていたが、実際にはファイルが残っていた。

理由は Volume を利用していたためである。

生成された Rails プロジェクトはホストOS側に保存されていた。

そのためコンテナを削除しても、

  • app
  • config
  • db
  • Gemfile

などはそのまま残っていた。

ポート公開とは何か

Rails サーバーへブラウザからアクセスするため、

-p 3000:3000

を指定してコンテナを作成した。

これは、

ホストOSの3000番
↓
コンテナの3000番

を接続する設定である。

この設定を追加したことでブラウザから Rails サーバーへ接続できるようになった。

0.0.0.0と127.0.0.1の違いを理解した

Rails を起動する際、

rails s -b 0.0.0.0

を使用した。

最初は、

3000番ポート

0.0.0.0

の違いが理解できなかった。

調べてみると、

IPアドレス
:
ポート番号

という構成になっていた。

例えば、

127.0.0.1:3000

は localhost の 3000 番ポートを意味する。

一方、

0.0.0.0

は特殊なアドレスであり、

すべてのネットワークインターフェースで待ち受ける

という意味になる。

Docker のポートフォワーディングを利用する場合は 0.0.0.0 で待ち受ける必要がある。

Railsのトップページへアクセスできた

最終的に、

rails s -b 0.0.0.0

を実行し、

http://localhost:3000

へアクセスした。

すると Rails のトップページが表示された。

Docker コンテナの中で動作しているアプリケーションへ、ホストOSのブラウザからアクセスできることを確認できた。

Dockerのデータはどこまで削除されるのか

学習中に、

  • コンテナ
  • イメージ
  • Volume

の違いについても理解できた。

コンテナを削除しても、

  • イメージ
  • Volume
  • ホスト側のファイル

は残る。

また Docker には、

docker system prune

という不要データを削除するためのコマンドも存在する。

Docker を使い続けると不要なイメージやビルドキャッシュが蓄積するため、定期的な整理が必要になる。

今回理解できたこと

今回の実践を通して次の点を理解できた。

  • Docker Desktop は WSL2 上で Linux コンテナを動かしている
  • Ruby イメージをベースに Rails 環境を作成できる
  • build と run は役割が異なる
  • bash は TTY がなければ終了する
  • コンテナの終了はメインプロセスの終了を意味する
  • Volume によってソースコードを永続化できる
  • ポート公開によってブラウザからアクセスできる
  • 0.0.0.0 は全インターフェースでの待ち受けを意味する
  • コンテナを削除してもイメージやVolumeは残る

Docker の概念だけでなく、実際にコンテナを作成して Rails を動かしたことで理解が大きく進んだと感じた。

Dockerを理解するためにTCP/IPとミドルウェアから整理した話

Dockerについて調べ始めた当初、私は「同じ『仮想環境』という言葉で語られるPythonのvenvとどのように異なるのだろうか?」という疑問を抱いた。 しかし理解を進めるうちに、Dockerは単なる仮想環境ではなく、「小さな独立したマシン群を扱う技術」と考えると理解しやすいことが分かった。 この記事では、自分の理解を整理するために、Docker・ミドルウェア・TCP/IP通信についてまとめる。

なおこの記事はChatGPTとの会話をChatGPTにブログ形式で出力させたものとなっております。

venvとDockerの違い

Pythonのvenvは、Pythonパッケージの依存関係を分離するための仕組みである。 一方Dockerは、OSレベルに近い実行環境そのものを分離する。

venvでは以下のようにプロジェクト内に仮想環境フォルダが存在する。

project/
├─ venv/
├─ main.py
└─ requirements.txt

しかしDockerでは、コンテナ自体はフォルダとして見えない。 代わりにDockerfileやdocker-compose.ymlなどで環境定義を記述する。

project/
├─ app/
├─ Dockerfile
├─ docker-compose.yml
└─ .dockerignore

Dockerは「環境そのものをコード化する」という思想が強い。

Dockerコンテナは小さいマシンと考えると分かりやすい

Docker初心者が混乱しやすい理由の一つは、「コンテナ」が何なのかイメージしにくいことである。

理解しやすかった考え方は、 「Dockerコンテナは、小さい独立したLinuxマシンのようなもの」 というものだった。

例えばRails + MySQL構成では以下のようになる。

[ Railsコンテナ ] ←→ [ MySQLコンテナ ]

これらは別々のコンテナであり、それぞれ別の空間を持っている。 localhostも共有していない。

localhostはどこのlocalhostなのか

Dockerで特に重要なのは、「どの空間のlocalhostか」を意識することである。

例えばRailsコンテナ内で localhost を指定した場合、それはRailsコンテナ自身を指す。 MySQLコンテナではない。

つまり以下は誤りになりやすい。

host: localhost

Docker Composeでは、通常サービス名で通信する。

services:
  web:
  db:

この場合、Rails側からは以下のように指定する。

host: db

Docker内部のDNS機能が、dbという名前を実際のIPアドレスへ変換してくれる。

Docker内部ではTCP/IP通信が行われている

Dockerの通信は特殊なものではなく、基本的には普通のTCP/IP通信である。

RailsとMySQLも、内部では以下のような通信をしている。

Rails → db:3306

ここで、

  • db = ホスト名
  • 3306 = MySQLの待受ポート

である。

つまりDockerは、「コンテナ間通信を簡単にする小さなネットワーク」を作っていると考えると分かりやすい。

IPアドレスとPORT番号

ネットワーク通信では、通信先は「IPアドレス + PORT番号」で決まる。

例えば以下のような形式で通信する。

192.168.1.10:3306

Dockerコンテナも内部的にはIPアドレスを持っている。

ただしDockerではIPは動的に変わる可能性があるため、通常は直接IPを指定しない。 代わりにサービス名を使用する。

Docker Composeは小さいLANを作っている

Docker Composeは、コンテナ同士を同じ仮想ネットワークに接続する。

イメージとしては、小さい社内LANを自動構築している感覚に近い。

[ web ]
[ db ]
[ nginx ]

それぞれが別マシンのように存在し、Docker内部DNSによって名前解決される。

portsとは何か

コンテナ同士の通信は内部ネットワークで完結する。 しかしブラウザなど、ホストOS側からアクセスするにはport公開が必要になる。

ports:
  - "3000:3000"

これは以下を意味する。

ホストOS:3000 → コンテナ:3000

つまりDockerがポートフォワーディングを行っている。

ブラウザはホストOS側で動いている

ChromeやEdgeなどのブラウザは通常ホストOS上で動作する。

ブラウザから localhost:3000 にアクセスすると、

ブラウザ
↓
ホストOS
↓
Dockerのport forwarding
↓
Railsコンテナ

という流れで通信が行われる。

ミドルウェアとは何か

ミドルウェアとは、「OSとアプリケーションの間で共通機能を提供するソフトウェア」である。

例えば以下が代表例である。

  • MySQL
  • MariaDB
  • nginx
  • Redis

Railsはこれらを利用する側であり、Rails自身はWebアプリケーションフレームワークである。

XAMPPとDockerの違い

XAMPPもApache・PHP・MariaDBをまとめて扱えるため、Dockerと少し似ている。

しかしXAMPPは「ホストOSへ直接インストール」する形式である。

Windows
├─ Apache
├─ PHP
└─ MariaDB

一方Dockerでは、それぞれを独立したコンテナとして分離できる。

Docker
├─ Apacheコンテナ
├─ PHPコンテナ
└─ MariaDBコンテナ

これにより、

  • プロジェクトごとの独立環境
  • バージョン衝突回避
  • 環境再現性
  • 本番環境との統一

などが実現しやすくなる。

現時点での理解

今回の理解で特に重要だった点は以下だった。

  • Dockerコンテナは小さい独立マシンとして考える
  • localhostはコンテナごとに異なる
  • Docker内部では普通のTCP/IP通信をしている
  • Composeは小さいLANを構築している
  • サービス名がDNSのホスト名として機能する
  • portsはホストOSとコンテナを繋ぐ

Dockerは最初かなり魔法のように見えるが、 「ネットワークを持つ小さいマシン群」 として理解すると、一気に整理しやすくなった。