tkrd-stack

学習ノート

自分の理解を深めるための技術メモ

← 一覧に戻る

git

バージョン管理システム(Version Control System, VCS)は、ソースコードやドキュメントなどの変更履歴を管理するツールです。プロジェクトの進行に伴い、ファイルの変更や修正を追跡できるため、複数の開発者が協力して作業する際や、過去のバージョンに戻したい場合に非常に便利です。

バージョン管理とは

ファイルの変更履歴を記録すること。

記録するファイルの情報。

  • 作成者
  • 作成日時
  • 更新者
  • 更新日時
  • コメント
  • 変更履歴

対象ファイル

  • ソース
  • ドキュメント
  • 画像
  • 動画
  • 音楽

分散型のほかに集中型もあり、SVN(SubVersion)がある。

分散型と集中型の違いは「分散型にはローカルリポジトリがある」こと。

主なバージョン管理システム

1. Git

  • 分散型バージョン管理システムの代表例。ローカル環境にリポジトリを持ち、開発者が自由に作業できる。
  • ほとんどの現代的な開発プロジェクトで使用されており、GitHub、GitLab、Bitbucket などのプラットフォームと連携してリモートリポジトリの共有が可能。
  • 特徴:
    • ブランチ管理が容易で、柔軟な開発フローをサポート。
    • ローカルでの作業が可能なので、オフラインでもコミットやブランチ操作ができる。
  • コマンド例:
    • git init
      : リポジトリの初期化。
    • git clone
      : リポジトリの複製。
    • git commit
      : 変更を保存。
    • git push
      : リモートリポジトリに変更を反映。

2. Subversion (SVN)

  • 集中型バージョン管理システム。リポジトリはサーバー上にあり、すべての開発者が同じサーバーにアクセスしてファイルを管理する。
  • 特徴:
    • 一元管理されたリポジトリで、アクセス権や管理が集中しやすい。
    • ブランチ機能やタグのサポートはあるが、Git に比べると柔軟性に欠ける。
  • コマンド例:
    • svn checkout
      : リポジトリから最新のコードを取得。
    • svn commit
      : 変更をリポジトリに保存。

3. Mercurial (Hg)

  • 分散型バージョン管理システム。Git と同様にローカルにリポジトリを持つことができ、オフラインで作業可能。
  • 特徴:
    • Git よりもシンプルで、操作が直感的。
    • 大規模プロジェクトに適しており、パフォーマンスが高い。
  • コマンド例:
    • hg init
      : リポジトリの初期化。
    • hg commit
      : 変更をリポジトリに保存。

4. Perforce

  • 大規模なプロジェクトやエンタープライズ向けに使用される集中型バージョン管理システム
  • 特徴:
    • 大規模なファイルやチームの管理に適しており、ゲーム開発や映像制作などで使われることが多い。
    • 高速で信頼性のあるパフォーマンスが特徴。

バージョン管理の基本概念

1. リポジトリ

  • ソースコードやファイルの変更履歴を保存するデータベース。ローカルリポジトリ(開発者の PC 上)とリモートリポジトリ(サーバー上)に分かれます。 リポジトリ (Repository) とは、ソフトウェアのソースコードやファイルのバージョン履歴を管理するデータベースのことです。リポジトリは、コードやファイルの変更を記録し、チーム開発やバージョン管理を容易にするために利用されます。

    リポジトリの種類

    1. ローカルリポジトリ
      • 開発者の PC(ローカル環境)に存在するリポジトリ。ローカルでファイルの変更履歴を保存し、オフラインでも操作が可能です。
      • 例: Git では、
        git init
        コマンドでローカルリポジトリが作成されます。
    2. リモートリポジトリ
      • サーバー上に存在し、複数の開発者が共有できるリポジトリ。GitHub や GitLab などのリモートサービスを使って、リポジトリをインターネット上でホスティングし、チームメンバーと共有できます。
      • 例:
        git clone
        でリモートリポジトリからローカルに複製し、
        git push
        でローカルリポジトリの変更をリモートに反映します。

    リポジトリの主な機能

    1. 変更履歴の保存
      • リポジトリは、ファイルやコードの変更をすべて記録し、誰がいつどのような変更を行ったのかを追跡できます。
    2. バージョン管理
      • 各ファイルの状態や変更履歴を「バージョン」として記録。特定の時点のバージョンに戻したり、異なるバージョンを比較できます。
    3. ブランチ管理
      • 異なる作業や新機能開発を分岐(ブランチ)させて、メインのコードベースとは独立して開発できます。ブランチを使うことで複数の開発者が同時に作業しても、互いの変更が衝突しないよう管理できます。
    4. 共同作業の効率化
      • チーム開発では、リモートリポジトリを使うことで、複数の開発者が同じプロジェクトに同時に貢献し、それぞれの作業を統合(マージ)できます。

    Git のリポジトリ例

    • ローカルリポジトリ:
      • 自分のローカル環境でコードを管理します。コマンド例は以下の通り。
        git init  # 新しいリポジトリを作成
        git add . # ファイルをステージ
        git commit -m "initial commit"  # コミットして履歴を記録
        
    • リモートリポジトリ:
      • GitHub や GitLab などのリモートホスティングサービスにリポジトリを保存してチームと共有します。
        git remote add origin https://github.com/username/repo.git  # リモートリポジトリを追加
        git push origin main  # ローカルの変更をリモートに反映
        

    まとめ

    リポジトリは、プロジェクトのコードやファイルを効率的に管理し、開発の変更履歴を記録・追跡するための重要なツールです。バージョン管理を通じて、プロジェクトの変更を柔軟に管理し、過去の状態に戻したり、チームで共同開発を進めたりする際に役立ちます。

2. コミット (Commit)

  • 変更内容をリポジトリに保存する行為。各コミットには一意の ID(ハッシュ)が付与され、変更履歴として追跡可能。

3. ブランチ (Branch)

  • 開発を分岐させるための機能。メインのコードライン(通常
    main
    master
    )から分岐して、別の作業や機能追加を行うことができる。作業が終わればブランチをマージ(統合)する。

4. マージ (Merge)

  • ブランチ間の変更を統合する操作。複数の開発者が同時に作業した場合、変更内容を結合して 1 つのコードベースにまとめる。

5. タグ (Tag)

  • リポジトリ内の特定のコミットに目印をつける機能。リリースバージョンなどを識別するために使われる(例:
    v1.0
    ,
    v2.1
    )。

バージョン管理のメリット

  1. 変更履歴の追跡:
    • どのファイルがいつ、誰によって変更されたかを確認できる。バグが発生した場合、過去のバージョンに遡って原因を特定できる。
  2. チーム開発の効率化:
    • 複数の開発者が同じプロジェクトに同時に取り組んでも、互いの作業が衝突しないようにブランチを使って管理できる。
  3. 過去のバージョンに戻せる:
    • 過去の特定の時点の状態に簡単に戻すことができ、間違った変更やバグを取り除ける。
  4. ブランチを活用した開発フロー:
    • 新機能の追加やバグ修正を別々のブランチで行い、メインブランチとは独立して作業できる。

Git の開発フローの例

  • Git Flow: フィーチャーブランチ、デベロップブランチ、リリースブランチなどを使って、開発、テスト、リリースの流れを管理する開発フロー。
    • feature/
      ブランチ: 新機能を開発するために使用。
    • develop
      ブランチ: 開発中の安定したコードを管理。
    • master
      ブランチ: 本番リリース用の安定版コード。

まとめ

バージョン管理システムは、ソフトウェア開発において欠かせないツールです。特に Git のような分散型のバージョン管理システムは、柔軟なブランチ管理やオフライン作業が可能で、多くの開発者に愛用されています。プロジェクトの規模やチームのニーズに合わせて、適切なバージョン管理システムを選ぶことが重要です。

ブランチ管理

ブランチ管理は、バージョン管理システムにおいて、コードベースを複数の「ブランチ(枝)」に分けて管理する手法です。ブランチは、プロジェクト内で別々の作業を同時に進めるための分岐を意味し、異なる機能の追加やバグ修正などをメインのコードから分けて独立して開発できます。

ブランチの基本概念

ブランチという概念とエンカウント

意味は枝、分岐のような意味。(朝食と昼食の間もブランチだよね)

では、git におけるブランチの役割は?

履歴を分岐して記録するためのもの

ブランチはイメージが大事

ブランチとは、まず安定した大きい枝があり、プログラムの修正や機能の追加を行うときに大きい枝から小さい枝を生やして、目的別に作業を行う。

この安定した大きい枝がmaster ブランチと呼ばれる。

  1. ブランチ(Branch)
    • ブランチは、リポジトリ内での開発の「分岐」を表します。通常、プロジェクトにはメインブランチ
      main
      master
      と呼ばれる)があり、そこから新しいブランチを作成して作業を進めます。
    • ブランチごとに異なる作業を行い、それらの作業が完了したら、メインブランチに統合(マージ)します。
  2. マージ(Merge)
    • ブランチで行った変更を他のブランチ、主にメインブランチに統合する操作。マージの際に、変更が衝突(コンフリクト)する場合もあり、その場合は手動で解決する必要があります。
  3. コンフリクト(Conflict)
    • 複数のブランチで同じファイルの同じ部分が異なる形で変更された場合に発生します。コンフリクトが発生した場合は、どの変更を採用するか手動で決定します。

ブランチ管理のメリット

  1. 独立した作業環境
    • 各ブランチで独立した作業ができるため、他の作業を妨げることなく新しい機能の開発やバグ修正を行うことができます。
  2. チーム開発の効率化
    • 複数の開発者が同じコードベースで同時に異なる作業を進めることができるため、並行して複数の機能や修正を行えます。
  3. リリース管理の簡素化
    • 安定したリリースバージョンのブランチと開発中のブランチを分けて管理することで、リリースのタイミングで混乱することなくコードの品質を保つことができます。

ブランチを使うことのメリット

  • 分岐したブランチの内容は他のブランチに影響を与えない。
  • 目的別に同時並行で作業を行うことができる。
  • ブランチ別に履歴管理できるので、問題の切り分けや対応が易い。

新機能を追加する時、常に動作しているもの(master)と切り離すことで常に動く記録を持つブランチと新機能用のブランチで並行して開発することができる。

あくまで理解しておきたいのはブランチは変更履歴を別々に管理するもの

ブランチの種類

最新コミットへのハッシュ。

何やらブランチには3種類あるらしい。

  • ローカルリポジトリ
  • リモートリポジトリ
  • リモートトラッキングブランチ

ローカルリポジトリ

ローカルリポジトリで管理されるブランチ。

git リポジトリを作成した場合に自動的に作成される。

例:master

リモートリポジトリ

リモートリポジトリで管理されるブランチ。

リモートリポジトリを参照できる人はみな見えるブランチ。

例:origin

リモートトラッキングブランチ

リモートブランチの状態を追跡。

ローカルリポジトリにしれっと存在するらしい。

例:origin/master  とあると リモートブランチにある origin の master を追跡中。

ブランチ管理の基本コマンド(Git の場合)

  1. ブランチ作成
    • 新しいブランチを作成し、そのブランチに切り替えます。
      git checkout -b new-feature
      
    • 上記コマンドは、
      new-feature
      という名前のブランチを作成し、そのブランチに切り替える操作です。
  2. ブランチの確認
    • 現在のリポジトリのブランチ一覧を表示します。
      git branch
      
  3. ブランチ間の切り替え
    • 既存のブランチに切り替えることができます。
      git checkout main
      
  4. マージ
    • new-feature
      ブランチの作業を
      main
      ブランチに統合します。
      git checkout main  # メインブランチに切り替える
      git merge new-feature  # new-featureブランチをマージ
      
  5. ブランチの削除
    • 作業が完了して不要になったブランチを削除できます。
      git branch -d new-feature
      

ブランチ管理の一般的なフロー

  1. メインブランチ(
    main
    master
    • リリース可能な安定版のコードを保つためのブランチです。直接このブランチに作業を加えることは少なく、ブランチを使って分岐させた変更をマージしていきます。
  2. デベロップブランチ
    • 開発中の最新のコードを保持するためのブランチです。フィーチャーブランチやバグ修正ブランチをこのブランチにマージし、開発全体の進行を管理します。
  3. フィーチャーブランチ(Feature Branch)
    • 新しい機能を開発するためのブランチです。新しい機能を追加する際、メインブランチやデベロップブランチから分岐させて作業します。作業が完了したらデベロップブランチにマージします。
  4. バグ修正ブランチ(Bugfix Branch)
    • バグ修正のためのブランチです。バグを修正する作業を独立して行い、修正が完了したらメインブランチまたはデベロップブランチにマージします。
  5. リリースブランチ(Release Branch)
    • リリース前の準備段階で使用されるブランチです。リリースの最終テストや軽微な修正を行い、完了したらメインブランチにマージしてリリースします。
  6. ホットフィックスブランチ(Hotfix Branch)
    • リリース後に発見された重大なバグを緊急で修正するためのブランチです。メインブランチから直接分岐して修正し、完了したらメインブランチとデベロップブランチにマージします。

ブランチ管理戦略

  1. Git Flow
    • Git Flowは、開発フローを体系的に管理する手法です。メインブランチ、デベロップブランチ、フィーチャーブランチ、リリースブランチ、ホットフィックスブランチなど、役割ごとに分けたブランチを使い分けます。
    • 開発チームが複数の作業を並行して進める際に非常に有効です。
  2. GitHub Flow
    • GitHub Flowは、Git Flow よりシンプルなブランチ管理戦略です。開発作業は基本的に
      main
      ブランチから分岐して作業し、すぐにマージするというフローを取ります。軽量で柔軟性が高い。
  3. GitLab Flow
    • GitLab Flowは、リリースと環境にフォーカスしたフローです。デベロップ、プリプロダクション、プロダクションといった段階ごとにブランチを管理し、各環境に適したコードを保持します。

ブランチ管理のポイント

  • 小さい単位でのブランチ作成: ブランチごとの作業範囲を限定し、頻繁にマージすることでコンフリクトのリスクを軽減。
  • 定期的なマージとテスト: 長期間ブランチを放置せず、定期的にメインブランチや他のブランチとマージして最新の状態を保つ。
  • 適切な命名: ブランチ名は何のために作業しているかをわかりやすく表現する(例:
    feature/new-login
    ,
    bugfix/fix-typo
    )。

まとめ

ブランチ管理は、ソフトウェア開発において並行作業を効率的に進めるために欠かせない手法です。チームが複数の機能や修正を同時に開発しつつ、互いの作業が干渉しないように管理することで、開発プロジェクトの品質と生産性を向上させます。

分散型を採用するメリット

  • 作業の問題点を発見しやすい
  • ファイルを過去の状態に復元できる
  • 共同作業をスムーズに進められる

基本的に今はだれかと作業することは皆無なので、CI/CD 用だったり個人で履歴確認するように使っている。

(余談:仕事していた時は SVN を使用していたが、ソース差分を読み込まずにプルやプッシュして上書きしてしまい、ソースがよく消えてしまった。当時は不便すぎてほんとにうんざりだった。git によって過去の私が起こした過ちが起きにくくなっているみたい、感謝。)

git の仕組み

なんとなく git の流れや用語を理解したので、git の仕組みについてざっくり理解する。

git はスナップショットを記録している。

スナップショットとは、ファイルの変更履歴を管理する仕組みのこと。

ファイル間の差分ではなく、すべての状態の記録を行う。

Git オブジェクトリファレンスにてデータ構造を構成している。

Git オブジェクトの種類

  • Blob オブジェクト
  • Tree オブジェクト
  • Commit オブジェクト
  • Tag

これらは以下の特徴を持つ。

  • すべて zlib で可逆圧縮されたもの
  • SHA-1 ハッシュによって識別
  • immutable
  • これらは
    .git/objects
    から確認可能

ファイルの内容が変更された場合はハッシュ値も別になる。

Blob オブジェクト

ファイルと 1:1 に対応。

blob オブジェクトとは、テキストデータ、画像データなどファイルデータの中身そのものを圧縮したもの。※ファイル名などの情報は含まれていない。

ある時点でのファイルそのもののイメージ。

Tree オブジェクト

ディレクトリに対応。

tree オブジェクトは以下の情報を持つ。

  • 参照している Git オブジェクトのタイプ(blob or tree)
  • ファイル名またはディレクトリ名
  • blob オブジェクトまたは tree オブジェクトのハッシュ値
  • blob オブジェクト、または、別の tree オブジェクトへの参照
  • ある時点でのディレクトリとその中身そのもののイメージ。

Commit オブジェクト

commit オブジェクトは以下の情報を持つ。

  • トップレベルの tree オブジェクトの参照
  • コミットしたユーザ情報(名前・メール)
  • auther のユーザ情報(名前・メール)
  • コミットメッセージ
  • タイムスタンプ
  • 親コミットへの参照
    • 親がない場合は、initial commit
    • 親が2つあるのは、merge commit コミットに対応。

これらをすべて組み合わせるとこうなる。

リファレンス(refs)

リファレンスとはcommit オブジェクトを指し示すポインタ

リファレンスファイルの中は commit オブジェクトのハッシュ値のみが書かれている。

.git/refs
で参照可能。

refs には以下の種類がある。

  • branch
  • HEAD
  • tag

branch

commit オブジェクトを指すポインタ。

ローカルもリモートも同じ考え方。

HEAD

現在チェックアウト(作業)しているリファレンスのポインタ。

リファレンスのリファレンスを示すこともあるが、Commit オブジェクトを示すこともある。

detached HEAD

HEAD がポイントを示していない状態のこと。

Tag

Tag オブジェクトは Commit オブジェクトへの参照と追加情報としてメッセージの情報を持つ。

Tag には以下の2種類ある。

  • 軽量タグ
  • アノテーションタグ(注釈付きタグ)

軽量タグ

Commit オブジェクトへのリファレンス。

アノテーションタグ

git 操作

Git オブジェクトとリファレンスから Git の仕組みを見てきたので、改めて Git 操作に沿って仕組みを見ていく。

context.txt を修正した場合について考えてみる。

git add

ワークツリー

↓ git add

ステージングエリア

git add
実行時、ファイル・ディレクトリが圧縮とハっ種で命名して、
.git/objects
blob ファイルの作成インデックスの追加が行われる。

ファイルの内容が変更されているので、ハッシュ値も別になり既存の blob オブジェクトとは別に新しいオブジェクトが作成される。

git status

主にステージングの状態を確認。

git reset

HEAD やステージングの状態、ワークツリーの状態を指定した状態に戻す。

どれを戻すかは

--soft
,
--hard
のオプション。

何もつけないと

--mixed
をつけた設定になり、ステージングと HEAD を変更する。

add や reset といった処理を取り消せる。

git commit

ステージングの状態をコミット object に変換して、コミットログとして登録。

ステージングエリア

↓ git commit

ローカルリポジトリ

git commit
実行時、treeobject の作成commit オブジェクトの作成が行われる。

main ブランチが新しくできた commit オブジェクトを指し示す。

HEAD も同じ。

内容に変更がない blob や tree は commit の度にコピーされるわけではない。同じハッシュ値になる。

データ容量の節約になっていいねってこと。

git fetch

リモートリポジトリの特定のブランチを、ローカルリポジトリにコピー。

リモートリポジトリ

↓ git feach

ローカルリポジトリ

git fetch
実行時、リモートリポジトリの git オブジェクトやリファレンスがローカルリポジトリに反映される。

ローカルリポジトリの branch や HEAD といったリファレンスは変化なし。

git config

commit オブジェクトに保存されるユーザー情報などの設定。

git log

git のコミットログを確認することができる。

HEAD から親コミットの順番に辿って表示。

git diff

2 つの状態(コミット間など)の差を見ることができる。

git remote

リモートリポジトリとして登録。

git remote add リモートリポジトリ名 url

リモートリポジトリ url の書き換え。

git remote rename-url
古い名前 新しい名前

git push

存在するリモートリポジトリへ送信。

git push リモートリポジトリ名 ブランチ名

git pull

リモートリポジトリをワークツリーにマージする。

git branch

ブランチに対する操作。

ブランチの一覧を表示。

git branch

ブランチの作成。

git branch ブランチ名

ブランチの削除。

git branch -D ブランチ名

git chechout

ブランチを切り替える操作。

また、git の履歴からファイルを復元することが可能。

git checkout -b ブランチ名

git rebase

歴史を改変することができます。コミットをまとめるとか、コミットメッセージを編集するとか、コミットの順番を変えるとか、そのコミットでの差分をなかったことにするとか、親コミットを付け替えるとか、なんでもできる。

git merge

特定のブランチでの変更をすべて取り入れる。

用語の確認

リポジトリ

過去ソースコードのデータベース。

ワークツリー

作業しているフォルダ群。

「.git」ファイルがあるフォルダを指す。

コミット

ワークツリーの内容をローカルリポジトリに登録する操作。

各コミットの一つ前のコミットのことを親コミットという。

コミット時に親コミットとの差分を比較する。

コミットメッセージ

commit 時に作業内容と変更事項をメモするためのもの。

標準の書き方は「タイトル + 空白行 + 本文」

コミットログ

過去の commit 群のこと。

ブランチ

履歴を分岐して記録するためのもの。

不整合が発生しないように。

マージ

各ブランチをまとめること。

独立した複数の開発ライン野変更分をひとつのブランチに統合する。

fast-forward マージ(-ff)

あるブランチを master ブランチのそのままマージすること。

マージコミットを作るマージ

親コミットが 2 つあるときに使用するコミット。

clone

リポジトリの内容を、丸まる(コミットログなども含めて)コピーすること。

リモートリポジトリから自分の PC にコピーすること。

流れ

git には多くの領域?(ファイルを置く場所)が存在しているため、領域を理解していないと実行コマンドが混在しやすくなる。

目的は領域や流れを把握して、どのタイミングでコマンドを実行すべきなのかを理解すること。

自分的に途中にコマンドとかあると、わからなくなるので実行例を用いる場合は、ざっくり流れとか用語とか理解した上で最後に表示させる。(画像とかのスクショとかいろいろめんどいので実行例はないかもしれない)

git に存在する領域

git には 4 つの領域が存在する。

  • リモートリポジトリ
  • ローカルリポジトリ
  • ステージングエリア
  • ワークツリー

git によって管理されているディレクトリをgit リポジトリと言う。「.git ファイル」がおかれている。

リモートリポジトリ

複数人で共有するための場所。チーム内でファイルの管理ができる。ネットワーク上のサーバにある。

ローカルリポジトリ

ユーザーが一人で管理するための場所。個人でファイルの編集履歴が確認できる。大体はお手元のマシンの中にある。

ステージングエリア

ローカルリポジトリにコミットするファイルを置く領域。

パスワードが書いてるファイルや、環境変数が書いているファイルなど、リモートリポジトリに置きたくないファイル・ディレクトリを分けることができる。

大体は手元のマシンの中にある。

ワークツリー

私たちが作業をしているディレクトリのこと。ただし、git の管理下である場合にワークツリーという。それ以外はディレクトリ。

大体は手元のマシンの中にある。

最小で git 操作するコマンド

目的別にどういったコマンドが必要なのか見ていく。

最初はとりあえず、ワークツリーからリモートリポジトリまでのやり取りができるようになることを目標とする。

領域の間にあるものが最小の実行コマンド。それぞれ必要に応じてオプション等の付与が必要。

ワークツリーからリモートリポジトリへファイルを置きたいとき。

ワークツリー

↓ git add

ステージングエリア

↓ git commit

ローカルリポジトリ

↓ git push

リモートリポジトリ

リモートリポジトリからワークツリーへファイルを持ってきたいとき。

リモートリポジトリ

↓ git feach

ローカルリポジトリ

↓ git checkout

ワークツリー

ステージングエリアを介さないのは、自分がリポジトリに置きたくないファイルを分ける場所だから。

持ってくるときにはすでに、分けられた状態で持ってこれるため。

また、リモートリポジトリを自分の PC にコピーしたい場合はcloneコマンドを使用。

リモートリポジトリ

↓ git clone ~

ワークツリー

ざっくりと流れは理解。

(余談:SNV の場合はリモートに置くときは push なのだが、持ってくるときは pull。git にも pull が存在するので feach とごちゃごちゃになってマジでわからなくなった。)