Gitとは

バージョン管理システム。ファイルやフォルダに加えられた変更を追跡するためのツール。

一人で作業する場合には、例えばファイルに特定の変更が加えられた理由を確認できたり、複数の作業を互いに影響しない形で行ったりすることができて便利。複数人で作業する場合には、互いの更新が確認できたり、任意の修正の作業者を特定できたり、同時に作業ができたりなどの利点がある。

バージョン管理システムは他にもいろいろあるが、Gitがデファクトスタンダードになっている。Gitのコマンドは一見複雑だけど、使いこなせると便利だし、データモデルを理解していればコマンドライン操作も比較的容易に習得できるはず。

初期設定

環境構築を参考にインストールが完了したら、以下のコマンドを実行して初期設定をする。

名前とメールアドレスの設定

git config --global user.name "[ユーザー名]"
git config --global user.email "[メールアドレス]"

Warning

公開リポジトリではこの情報も公開されるので要注意。

デフォルトブランチ名の設定

git config --global init.defaultBranch main

Info

--globalをつけて設定した情報はホームディレクトリの.gitconfigというファイルに保存される。

参考資料

とても分かりやすい講義ノートがありました。以下の説明に加えて、こちらも参考にしてみてください。

データモデル

スナップショット

標準的なディレクトリとして、以下のようなものを考える。

<root> (tree)
|
+- foo (tree)
|  |
|  + bar.txt (blob, content: "hello, world")
|
+- baz.txt (blob, content: "hello, git")

Gitではディレクトリをtree、ファイルをblobと呼ぶ。treeは、それ以下のtreeやblobに対するリンクであり、blobはバイト列である。最上位のtree(ここでは<root>)に含まれるtreeやblob状態を、Gitはスナップショットとして保存していく。

スナップショットの履歴

個々のスナップショットを関連づけるために、Gitでは有向非巡回グラフ(Directed Acyclic Graph; DAG)を使用している。これはすなわち、個々のスナップショットが先行するスナップショット(parent)のセットを参照しているということである。参照しているparentは一つではなく、複数の場合もある(並行で開発されているブランチを統合する時など)。

Gitでは、このようなスナップショットをcommitと呼ぶ。

o <-- o <-- o <-- o
            ^
             \
              --- o <-- o
o <-- o <-- o <-- o <---- o
            ^            /
             \          v
              --- o <-- o

データモデルの擬似コード

// ファイルは大量のバイト列
type blob = array<byte>

// ディレクトリはファイルとディレクトリを内包している
type tree = map<string, tree | blob>

// コミットはparent、メタデータ、最上位階層のtreeを保持している
type commit = struct {
    parents: array<commit>
    author: string
    message: string
    snapshot: tree
}

objectと内容のアドレス

Gitでは、blob、tree、commitの全てをobjectとして扱う。

type object = blob | tree | commit

Gitは全てのobjectに対してSHA-1 hashを実行し、その内容のアドレスを保持している。

objects = map<string, object>

def store(object):
    id = sha1(object)
    objects[id] = object

def load(id):
    return objects[id]

そのため、全てのobjectが40字の16進数SHA-1ハッシュを持っている。

reference(参照)

commitをSHA-1ハッシュで区別することができるが、不便なので、名前をつけることができる。 この、commitにつける名前のことをreferenceと呼ぶ。

references = map<string, string>

def update_reference(name, id):
    references[name] = id

def read_reference(name):
    return references[name]

def load_reference(name_or_id):
    if name_or_id in references:
        return load(references[name_or_id])
    else:
        return load(name_or_id)

慣習的に、「main」というreferenceはメインの開発ブランチ(これは後述)における最新のcommitを指す。また、Gitでは「HEAD」という特別なreferenceを使って「現在の作業場所」を指定する。

repository(レポジトリ)

Gitレポジトリとは、データのobjectとreferenceである。Gitのデータモデルが持っているのは、objectとreferenceのみであり、Gitの操作とはつまり、commitのDAGに対して、objectの追加およびreferenceの追加と更新を行うことである。

ステージング

とはいえ、Gitではスナップショットを作成するためのコマンドが2段階に分かれる。具体的には、ステージングという概念を導入し、(1) スナップショットに含めるべき変更を指定した上で(git add)、スナップショットをとる(git commit)できるようにしている。

コマンドラインインタフェース

基本的なもの

  • git help <command>: Gitコマンドについての情報を得る
  • git init: .gitディレクトリ内に保存されたデータとともに、新しいGitレポを作成する
  • git status: 現在のレポジトリの状態を確認する
  • git add <filename>: ステージングエリアにファイルを追加する
  • git commit: 新しいコミットを作成する
    • コミットメッセージは何を目的にどのような変更を加えたか、具体的に書く習慣をつけましょう
  • git log: フラットな履歴のログを示す
  • git log --all --graph --decorate: 有向非巡回グラフ(DAG)として履歴を可視化する
  • git diff <filename>: ステージングエリアでの変更を表示する
  • git diff <revision> <filename>: スナップショット間のファイル内の差異を表示する
  • git checkout <revision>: HEADと現在のブランチを更新する

ブランチング(branching)とマージング(merging)

  • git branch: ブランチを表示する
  • git branch <name>: ブランチを作る
  • git checkout -b <name>: ブランチを作り、そのブランチに切り替える
    • git branch <name>; git checkout <name>と同様
  • git merge <revision>: 現在のブランチにマージする
  • git mergetool: マージコンフリクト(結合衝突)を解決するためのツールを使用する
  • git rebase: パッチのセットを新しいベース(土台)の上にリベース(作業が完了したブランチを分岐元のブランチにくっつける)する

リモート

  • git remote: リモートをリスト表示する
  • git remote add <name> <url>: リモートを追加する
  • git push <remote> <local branch>:<remote branch>: リモートにオブジェクトを送信し、リモートのリファレンスを更新する
  • git branch --set-upstream-to=<remote>/<remote branch>: ローカルブランチとリモートブランチ間の通信を設定する
  • git fetch: リモートからオブジェクトとリファレンスを回収する
  • git pull: git fetch; git mergeと同様
  • git clone: リモートからリポジトリをダウンロード(クローン)する

取り消し(Undo)

  • git commit --amend: コミットの内容やメッセージを編集する
  • git reset HEAD <file>: ファイルのステージングを解除する
  • git checkout -- <file>: 変更を破棄する

Git上級者向けコマンド

  • git config: Gitをカスタマイズする
  • git clone --depth=1: バージョン履歴全体は持ってこない、浅いクローン(ダウンロード)
  • git add -p: 対話的なステージング
  • git rebase -i: 対話的なリベース
  • git blame: どの行を誰が最後に編集したのかを表示する
  • git stash: 作業ディレクトリへの変更を一時的に削除する
  • git bisect: 履歴のバイナリサーチ(二分探索)を行う(回帰など)
  • .gitignore: 意図的に追跡されていないファイルを、無視するために特定する

GitHubとは

Gitのリモートレポジトリホスティングサービスで、Gitとは別物。プロジェクトのバックアップをとる時や他の人と共有する時に便利。GitHubの他にも、BitbucketGitLabなどがある。