git

What is Git?

GitとGitHubの設定 1/N

公開日: 2020-12-28
更新日: 2024-07-06

  Table of Contents

この記事のスコープ

  • Gitを利用するための基礎知識及び各種設定の解説

What is Git?

▶  Key Takeaways

  • Gitとはファイルやソースコードの変更を分散型でトラッキングする仕組み
  • ファイルやソースコードの変更をトラッキングする仕組みのことをバージョン管理システムと呼ぶ
  • Linux創始者Linux Torvalds氏によって2005年ごろに原型となるプログラムが開発された


Def: Version Control System

変更履歴を管理することを通じて,

  • 誰がなんの変更を加えたかの表示
  • 前回差分箇所をハイライト
  • ファイルの特定の段階に戻る
  • ファイルを復活

などを実現する仕組みがVersion Control System(VCS)です.

VCSを活用するメリットの具体例として,

  • あのとき動いたが,今は動かない… もう一回動いていた状態に戻したい
  • このコードを加えた人とタイミングを知りたい
  • 前回消してしまったコードを復活させたい
  • 同じテーマで2つファイルが発生してしまったから差分を確認したい

つまるところ,「VCSを活用することで, 一回とんでもない変更をしたとしても, すぐプロジェクト全体の状態を戻したり修正することができる」ということです.

Local VCS vs Distributed VCS

Version管理システムは分散型と集中型の2つに大別することができます.

  • 集中型: 変更履歴などのデータを一つの中央サーバーに集めて管理する種類のこと
  • 分散型: 各クライアントがリポジトリをミラーし終わったあとは, 変更履歴の参照などのVersion管理アクションは各々のクライアント内部で閉じている種類のこと

分散型は,最新のソースコードの管理が難しいというデメリットがありますが(いわゆるコンフリクト), クライアントのローカル環境内部で変更履歴の確認等の作業が完結するので, 集中型Version管理システムで直面するnetwork latency overhead問題を余り気にせずに開発作業をすることができます.

Centralized Version ControlDistributed Version Control

集中型VCSでは, 1つのrepository(=データを保存する場所)に対して多くの開発者とコンテンツを共有しているため, 2人で同じファイルを同時に編集してしまうと, 先に編集した人の変更内容が消えてしまうリスクがありました. 一方, Gitではリモート側の内容をローカルへコピーした上で, 手元のVCS内部で編集作業を行い, remoteへ同期する際は, merge conflictという形でdiffが警告され強制的な上書きを防止くれるので, 分散型のメリットに基づく平行開発環境を提供してくれています.

また, merge conflictが発生してもそれを解消するコマンドを提供してくれているので, その便利さがGitが今日普及した理由と考えられます.

What is Git Repository?

Repositoryとはデータを保存する場所のことです. Gitでは,このrepository単位でデータを管理しており,修正ログもrepository内に保存されています. Gitは分散型Version管理システムであるため,repositoryは各開発者側にlocal環境へミラーリングして利用します.

▶  Remote repository vs Local repository

  • Remote repository: GitHub/GitLab上のリモートサーバーに置かれたrepositoryのこと
  • Local repository: 開発者がlocalにおくrepository

GitによるVersion管理

Snapshots vs Differences

Version管理の方法として, Gitは差分(Differences)でなくSnapshotsでデータを管理しています. Git は基本的に,すべてのファイルが各時点でどのように見えるかをSnapshotで記録し,そのSnapshotへの参照を保存します. 効率化のため,ファイルが変更されていない場合は, Git はファイルを再び保存せず,すでに保存されている以前の同じファイルへのリンクだけを保存します.

Snapshots vs Differencesのイメージは以下のFiguresとなります.

commits are snapshots, not diffs

Gitのバージョン管理の仕組み

sequenceDiagram

  participant A as working directory<br>(working tree)
  participant B as staging area<br>(index)
  participant C as local repository<br>(local branch)
  participant D as local repository<br>(tracking branch)
  participant E as remote repository

  A->>B: git add
  B->>C: git commit
  C->>E: git push
  C->>A: git switch -c<br>(checkout)
  E->>D: git fetch
  D->>A: git merge
  E->>A: git pull (実質的には git fetch + git merge)

Gitはディレクトリ単位でVersion管理します. 管理されるディレクトリには3つのエリアが作られ,それぞれWorking Directory,Staging Area,Respositoryと呼びます.

  • Working Directory:
    • ドキュメントやプログラムファイルの作成などの作業を行う場所
    • 開発者が作業するためのディレクトリ領域
  • Staging Area:
    • 変更履歴として保存するファイルを選択し,置いておく場所
    • git addで指定したファイルが行き着くところ
  • Respository:
    • 変更履歴を記録しておく場所
    • Staging Areaに置かれているファイルを変更履歴をリポジトリに保存することを「Commit」といいます

Git管理されたファイルはmodified, staged, committedという状態が与えられ,それぞれの状態がファイルがどこのエリアにいるかを示しています.

状態 説明
modified ファイルの内容は変更されたがまだcommitされていない状態(データベースに記録されていない状態)
staged ファイルが次のcommitでデータベースに記録される準備ができたことを示します
committed ローカルレポジトリにファイル変更履歴が記録されていることを示します
sequenceDiagram
    participant wd as Working Directory
    participant sd as Staging Area
    participant cd as .git directory<br>(=Repository)
    cd->>wd: Checkout the project
    Note left of sd: git checkout<br>git restore
    wd->>sd: Stage Fixes
    Note left of sd: git add<br>git rm
    sd->>cd: Commit
    Note left of cd: git commit

▶  git ls-fles: stagedにインデックスされているファイルの表示

git ls-filesコマンドでステージングエリアに存在するファイルを確認することができます. ただし,gitの監視対象にある,ディレクトリに存在するファイルのみをリスト化するということであって,ディレクトリは確認することができません.

1
2
3
4
5
6
7
8
9
 % git ls-files
.gitignore
404.html
Gemfile
Gemfile.lock
Gruntfile.js
LICENSE
README.md
...

オプションを組合せてgit ls-files -io --exclude-standardと入力するとgitignoreに記載されているファイル=stagedされないファイルのみを表示することもできます.

Option Comments
-i 無視ファイル(ignore)のみを表示,利用するためには除外パターンの指定が必要
-o 管理対象外のファイルを表示
--exclude-standard .gitignore 等で指定されているものを除外パターンとしてコマンドに伝えるオプション

Gitはどこにバージョン管理のDBをもっているのか?

新たにGitでバージョン管理を始める際に,Repository初期化から始まります.具体的にはgit initコマンドを 叩くこととで初期化を実行します.

git initによってGit管理に指定したディレクトリには .gitディレクトリが作成されます. この.gitディレクトリにてGitはrepository dataを管理しており,修正ログもこの中に記録されています. Repositoryのバックアップやクローンをしたい場合,このディレクトリをどこかへコピーするだけでほぼ事足ります.

.git ディレクトリの中は以下のようになっています.

HEAD
config*
description
hooks/
info/
objects/
refs/

特に重要なのは以下の4項目となります

  • HEAD ファイル: 現在チェックアウトしているブランチを示すファイル
  • index ファイル(git init直後ではまだ作成されていない): ステージングエリアの情報を保管
  • objects ディレクトリ: データベースのすべてのコンテンツを保管
  • refs ディレクトリ: コンテンツ内のコミットオブジェクトを指すポインタ(ブランチ)を保管

なお,.gitディレクトリが存在するディレクトリ以下の特に「ワークツリー」と呼びます.とある時点のcommitまで戻したい場合は,

  • .gitディレクトリに格納されているログデータを参照
  • 対象となるデータの状態をログデータから取得し,ワークツリーに展開

という仕組みになっています.

Gitはどのように履歴データを取り出しているのか?

Gitはシンプルなキー・バリュー型データストアです.どんな種類のコンテンツでも格納でき,それに対応するキーが返されます.キーを使えば格納したコンテンツをいつでも取り出せます.ここでのコンテンツのことを「コミットオブジェクト」といいます.「コミットオブジェクト」はコミットによって生成されたデータのことです.Gitはコミットオブジェクトに対して40文字のIDを発行します.これがコミットハッシュ値でキー・バリューのキーに相当します.

コミットオブジェクトの中身を確認したい場合は,git cat-file -pで任意のコミットのコミットオブジェクトを見ることができます.

1
2
3
4
5
6
% git cat-file -p 757cd618f38d574238bae4768ff1a1aedfafdb7a
tree 05520e3bd0354e823cacf96b244987f235b3c240
parent 2476c4c7bcbf98e444b6851d67036077334502d2
author DQNEO <dqneo@example.com> 1454588308 +0900
committer DQNEO <dqneo@example.com> 1454588308 +0900
second commit
  • treeというのはtreeオブジェクトのことで,これはディレクトリツリーに対して割り振られるIDです.(もうちょっと厳密に言うと,treeオブジェクトは1つ以上のtreeオブジェクトまたはblobオブジェクトを持つツリー構造のデータです)
  • parentというのは親コミットすなわち1個前のコミットのハッシュ値です.Gitのコミットオブジェクトは必ず1つ以上の親コミットを持っており,親を順番にたどっていくことで履歴をさかのぼることができます.
  • authorとcommiterは普通同じ人になるのですが,cherry-pickしたりrebaseしたりすると異なる名前になることがあります.
  • 1行空行をはさんでそこから下がコミットメッセージです.

なお,コミットオブジェクトに対応するハッシュ値の計算式は以下のようになります

hash = sha1("commit<半角スペース><コミットオブジェクトのバイト数>\0<コミットオブジェクトの中身>")

How to Install Git

Ubuntu 20.04 LTSにGitをインストールすのは簡単で以下のコマンドをターミナルで実行するだけです.

1
2
% sudo apt update && sudo apt upgrade -y
% sudo apt install git-all

Versionを確認しときます.

1
2
% git --version
git version 2.30.0

Setup

Git configファイルの置き場所は3パターンあります:

PATH 説明
/etc/gitconfig システム上のすべてのユーザーとそのすべてのリポジトリに適用される値を保持します.git config に --system オプションを指定すると,このファイルから読み書きします.これはシステム設定ファイルなので,これを変更するには管理者権限かスーパーユーザー権限が必要となります.
~/.gitconfig ユーザー自身に固有の値を指定します.--global オプションを指定することで,Git にこのファイルの読み書きをさせることができます.
config file in the Git directory その単一のリポジトリーに固有の値を指定します.このファイルの読み書きを強制的に行う場合,--local オプションを指定しますが,デフォルトで参照するようになっています.このオプションが正しく動作するには,Git リポジトリのどこかに位置している必要があります.

以下のコマンドでconfig一覧を確認することができます

1
% git config --list --show-origin

~/.gitconfigの設定

▶  User 設定

Version管理を実現するためには誰がどのファイルをいつ変更したのかのデータが必要です.まずユーザーがだれなのかをgitに教えるため,user nameとemail addressをconfigに設定します.

1
2
% git config --global user.name "John Doe"
% git config --global user.email johndoe@example.com

ここで設定した名前とアドレスがコミットログに表示されます.コミット漕ぐに表示される名前としてなので, 本名やGitHubアカウントネームである必要はありません.

▶  Editor 設定

次にEditorの設定をします.commitメッセージを書くときなどに立ち上がるEditorの設定となります. VSCodeを利用したい場合は以下のように記載します.

1
% git config --global core.editor "code --wait"

▶  Default Branch Name 設定

次に default branch nameを設定します,昔はmasterで最近はmainと変わってきたところですが,こちらは好みなので設定は任意です.

1
% git config --global init.defaultBranch main

▶  color UIの設定

Gitはカラー化されたターミナル出力をサポートしており,コマンドの出力の視覚的理解向上が期待できます. デフォルト設定でもcolorに対応していますが,~/.gitconfigに明示的に記載したい場合は以下のコマンドを入力します

1
2
3
4
% % git config --global color.ui auto

# offにしたい場合は
% git config --global color.ui false

上記の設定をした場合, .gitconfigは以下のようになっているはずです

1
2
3
4
5
6
7
8
9
10
% cat .gitconfig                
[user]
	name = John Doe
	email = johndoe@example.com
[core]
	editor = code --wait
[color]
	ui = auto
[init]
	defaultBranch = main

commit templateの作成

Commit messageのテンプレートを作成します.commit messageを作成する際に,適切なフォーマットやスタイルを自分(または他の人)にリマインドすることができるというメリットがあります.

まず,~/.gitmessage.txtというファイルを以下のように作成します.commit message conventionなるものを確認したい場合はこちらのサイトがおすすめです.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<type>[optional scope]: <description>

[optional body]

Reviewed-by: [reviewer]
Refs: [ticket]

# ==== Type ====
# Add      ADD new file, function, feature
# Update   UPDATE existing functions/features
# Clean    Refactoring
# Remove   REMOVE files
# Fix      FIX bugs
# Upgrade  Upgrading to a new version

# ==== Type ====
# [Refs]: #<number>: referring an issue on GitHub
# [close, closed, fixed, resolve, resolved] #<number>: closing issue

git commit を実行したときにDefaultのメッセージとして使うためには,commit.templateの設定値を設定する必要があります.

1
% git config --global commit.template ~/.gitmessage.txt

GPGキーの登録

Gitは, メールアドレスを使って誰がAuthorなのか,Committerであるかを判別するしています. しかし, メールアドレスは各自がlocalの git config で設定できる属性のため, 簡単になりすましができてしまうという 問題があります.

このようななりすましの対策として, GPGキーを用いたgit commitへのデジタル署名がGitHub公式ページでは推奨されています. GPG公開鍵をGitHubに登録とすると, commitとtagが正常に検証されたGPGキーで署名されている場合,「Verified」としてマークがつくようになります.

GPGキーの生成とGitHubへの登録

GitHubでサポートされていないアルゴリズムを用いてキーを生成 & 追加しようとすると, エラーが生じることがあるので, サポートされているアルゴリズムをまず確認します:

1
RSA,ElGamal,DSA,ECDH,ECDSA,EdDSA

GPGキー生成

1
% gpg --full-generate-key
  • キーは少なくとも 4096 ビットである必要があり
  • キー有効期間は, 無期限を示すデフォルトの選択を指定(公式の推奨)
  • ユーザID情報時に求められるメールアドレスは, GitHub アカウント用の検証済みメールアドレスを入力
  • GPGキーはホームディレクトリ以下の.gnupg/openpgp-revocs.dに生成されます

生成されたGPGキーの確認

生成されたGPGキーの確認は以下のコマンドでできます. GitHubでは秘密鍵のGPGキーIDの長い形式の登録が必要なので, ` –keyid-format=long` オプションをつけて表示します.

1
2
% gpg --list-secret-keys --keyid-format=long  
% gpg --list-public-keys --keyid-format=long  

なおそれぞれ2つのIDがでてきますが, それらの意味は以下です:

sec SECret key
ssb Secret SuBkey
pub PUBlic key
sub public SUBkey

GitHubに登録するGPGキーの確認

まず秘密鍵のGPGキーを確認します. この例では, GPG キー ID は 3AA5C34371567BD2 です

1
2
3
4
5
6
% gpg --list-secret-keys --keyid-format=long
/Users/hubot/.gnupg/secring.gpg
------------------------------------
sec   4096R/3AA5C34371567BD2 2016-03-10 [expires: 2017-03-10]
uid                          Hubot 
ssb   4096R/42B317FD4BA89E7A 2016-03-10

次に, 秘密鍵情報をASCII形式(テキスト形式)で出力します

1
% gpg --armor --export 3AA5C34371567BD2

—–BEGIN PGP PUBLIC KEY BLOCK—– で始まり、—–END PGP PUBLIC KEY BLOCK—– で終わる GPG キーをコピーし, それを登録します.

Git へ GPG キーを伝える

GitHubへのGPGキー登録後, GitでGPG署名キーを設定する必要があります.

GPG キー ID は 3AA5C34371567BD2の場合,

1
% git config --global user.signingkey 3AA5C34371567BD2

きちんと登録されているかどうかの確認するため, .gitconfigを開きます(場所は個人次第)

1
% cat ~/.gitconfig

.zshrcへの登録

1
% [[ -f ~/.zshrc ]] && echo 'export GPG_TTY=$(tty)' >> ~/.bashrc

[[ -f ~/.zshrc ]]は条件式で -f 後のファイルパスが存在するならば 1 else 0を返します.

コミットに署名する

-S フラグをgit commitコマンドに追加し, pushするだけで完了です.

1
2
3
4
% git commit -S -m "your commit message"
# Creates a signed commit
% git push -u origin main
# ローカルコミットをリモートリポジトリにプッシュする

タグに署名する

  1. タグに署名するには, git tag コマンドに -s を追加します.
  2. タグをgit tag -v [tag-name] コマンドで検証(verify)します
1
2
3
4
% git tag -s mytag
# 署名済みのタグを作成する
% git tag -v mytag
# 署名済みのタグを検証する

Appendix: BFG Repo-Cleanerのインストール

BFG Repo-Cleanerとは?

BFGは, git-filter-branchと同様にGit Repository Historyから機密データ(例:パスワードや認証情報、その他のプライベートなデータ)をクレンジングしてくれるツールです.オープンソースコミュニティによって構築およびメンテナンスされています.

誤って個人情報を含んだファイルをrepositoryに上げてしまい情報流出が発生してしまう恐れは多々あります. その際,ファイルの削除だけでなくhistoryの削除も実施する必要があり,そのようなときにBFGが役に立ちます.

BFGのインストール

1
2
% cd ./tools/bfg
% wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar
  • ./tools/bfgは自分がapt以外の経由でパッケージをインストールする際に使っているディレクトリなので,任意の場所でも構いません

つぎにCLIから簡単に呼び出すことができるようにaliasを指定します. 自分はzshを使っているので.zshrcに以下のラインを追記します.

1
alias bfg='java -jar ~/tools/bfg/bfg-1.14.0.jar'
  • javaコマンドが必要なのでsudo apt install -y default-jdkとかでOpenJDKをインストールすることが必要です

利用方法

Delete all files named ‘id_rsa’ or ‘id_dsa’ :

1
% bfg --delete-files id_{dsa,rsa}  my-repo.git

Replace all passwords listed in a file (prefix lines ‘regex:’ or ‘glob:’ if required) with REMOVED wherever they occur in your repository :

1
% bfg --replace-text passwords.txt  my-repo.git

References



Share Buttons
Share on:

Feature Tags
Leave a Comment
(注意:GitHub Accountが必要となります)