🐙

Git再入門と運用検討

2023/10/02に公開


なんとなく使用していたGitを色々調べて自分なりに理解しました。Gitの基本的な構成要素と各コマンドが構成要素をどのように変化させるのかを理解することで、ある程度自分がやりたいことをコマンド群に変換することができるようになったと思います。
それと同時に普段使用する主なコマンドを取捨選択して余計なことを考えずに済むようにしました。そのための簡単な運用等も含めた備忘録です。

基本的な構成要素

  • blobオブジェクト
  • treeオブジェクト
  • commitオブジェクト
  • tagオブジェクト
  • HEAD
  • ブランチ
  • リモート追跡ブランチ
  • 作業ツリー (working directory, working tree, etc.)
  • ステージ (staging area, index, cache, etc.)
  • ローカルレポジトリ
  • リモートレポジトリ

普段使うGitのフェーズ

以下の3つのフェーズに分けて考え、それぞれのフェーズで使用するコマンドを取捨選択する。

  1. コミットグラフのノード作成
    • ファイル等の編集からコミットまでの作業
  2. コミットグラフの操作
    • ブランチの移動やコミット同士のマージ等の作業
  3. リモートとのグラフ同期
    • ローカルとリモートのリポジトリ同期作業

コミットグラフのノード作成

概要図

ノード作成

使用コマンド

  • 編集
    • git add <TARGET_PATH>: TARGET_PATHをステージに追加
    • git add -p <TARGET_PATH>: TARGET_PATHの変更ブロック(hunk)を個別でステージに追加
    • git restore --staged <TARGET_PATH>: TARGET_PATHをステージから削除
    • git restore <TARGET_PATH>: 作業ツリーのTARGET_PATHをステージのTARGETに上書き
    • git mv <SOURCE_PATH> <DESTINATION_PATH>: 履歴を引き継いでファイル名変更/移動
    • git rm --cached -r <TARGET_PATH>: TARGET_PATHを管理対象から除外
    • git commit -m "Commit message.": ステージ内容をコミット
    • git stash <SAVE_NAME>: 作業ツリーとステージの内容を一時保存コミット
    • git stash pop: 一時保存コミットから作業ツリーとステージの内容を上書き
  • 確認
    • git status -sb: ステージ,未ステージ,管理外ファイルを表示
    • git diff: 作業ツリーとステージの差分表示
    • git diff --name-only: 作業ツリーとステージの差分ファイル表示
    • git diff --staged: ステージとHEADの差分表示
    • git diff HEAD: 作業ツリーとHEADの差分表示

詳細と備考

  • git add時にblobオブジェクトが生成される
  • git add時にtreeオブジェクトファイルは生成されずindexファイルの中で管理される
  • git commit時にtree,commitオブジェクトが生成されハッシュが埋め込まれる
  • blobオブジェクトの実体はファイル名がハッシュのファイル
  • 元のファイル名はtreeオブジェクトで管理されblobオブジェクトにはない
  • treeオブジェクトはディレクトリに対応
  • blobオブジェクトは差分ではなくスナップショットとして保持
  • blobオブジェクトはzlibを用いて元のファイルを圧縮したファイル
  • ハッシュの計算はSHA-1だが、近々SHA-256になっていく
  • ファイル変更後のgit addは変更ファイルのみblobオブジェクトが生成される
  • ファイル変更後のtreeオブジェクトは変更ファイルに関するもののみ生成される
  • 対象ファイルのハッシュをポインタのようにして取り扱っている
  • git stashはcommitオブジェクトが生成される
  • Git管理下ファイルを管理から除外する場合は以下コマンドを用いる
    #---最初に.gitignoreに追記---#
    # 全てを管理対象から外した後に全てを対象に含める
    cd <PROJECT_DIR>
    git rm --cached -r .
    git add .
    git commit -m "Add ignored objects."
    

コミットグラフの操作

概要図

グラフ操作

使用コマンド

  • 編集
    • git branch <BRANCH_NAME>: BRANCH_NAMEブランチを作成
    • git branch -d <BRANCH_NAME>: BRANCH_NAMEブランチを削除
    • git branch --merged|egrep -v '\*|main'|xargs git branch -d: マージ済ブランチ削除
    • git branch <NAME1> <NAME2>: ブランチ名をNAME1からNAME2に変更
    • git switch <BRANCH_NAME>: HEAD,ステージ,作業ツリーをNAMEブランチに変更
    • git reset --hard <COMMIT_ID>: HEAD,ブランチ,ステージ,作業ツリーをCOMMIT_IDに変更
    • git reset --mixed <COMMIT_ID>: HEAD,ブランチ,ステージをCOMMIT_IDに変更
    • git reset --soft <COMMIT_ID>: HEAD,ブランチをCOMMIT_IDに変更
      • git reset --soft HEAD^: HEAD,ブランチを1つ前のコミットに変更
    • git checkout <COMMIT_ID>: HEAD,ステージ,作業ツリーをCOMMIT_IDに変更
      • HEADのみを移動させるため基本的にdetached HEADになる
    • git revert <COMMIT_ID>: COMMIT_IDを打消すコミットを生成
    • git merge <BRANCH_NAME>: BRANCH_NAMEをHEADにマージ
    • git tag <TAG_NAME> <COMMIT_ID>: COMMIT_IDを指す軽量タグTAG_NAMEを作成
    • git tag -a <TAG_NAME> -m "Tag message.": COMMIT_IDに注釈タグTAG_NAMEを作成
  • 確認
    • git diff HEAD^: HEADと直前コミットの差分表示
    • git log --all --oneline --graph: コミットグラフ簡易表示
    • git show <COMMIT_ID>: COMMIT_IDの内容表示
    • git tag -n: タグ一覧表示
    • git describe: HEADまでの最新の注釈タグ,何番目か,ハッシュ値を表示
    • git reflog: 操作履歴表示

詳細と備考

  • HEADはコミットグラフ上のカレントコミット(作業中のコミット)のイメージ
  • ブランチはcommitオブジェクトを指すポインタ
  • ブランチの実体はファイルとして存在する
  • ブランチファイルの中にcommitオブジェクトのハッシュを保存
  • HEADはブランチ又はコミットを指す特別なポインタ
  • HEADの実体はファイルとして存在する
  • ブランチ以外を指すHEADはdetached HEADと呼ぶ
  • HEADがブランチを指す場合はブランチファイルのパスをHEADファイルに保存
  • HEADがdetached HEADの場合はcommitオブジェクトのハッシュをHEADファイルに保存
  • HEADの指し先を変更した時は基本的にステージ,作業ツリーも連動して変化する
  • 軽量タグはブランチと同様コミットを指すポインタで本質的に同一
  • 注釈タグはtagオブジェクトが生成される
  • 注釈タグはcommitオブジェクト以外に対しても作成可能
  • 注釈タグはリリース用途、軽量タグはローカル又は一時的な用途
  • fast-forward mergeはブランチが存在したという情報が失われる
  • git resetはHEADが指すブランチを移動させ、結果HEADも移動したように見える
  • commitオブジェクトはgit resetでHEAD移動やgit revertで打消しても消えない
  • git reset等で消えたと思っているcommitオブジェクトは宙に浮いたコミットとして残る
    • 宙に浮いたコミットのことをdangling commitと呼ぶ
  • dangling commitはハッシュを指定してHEAD移動すれば参照可能

リモートとのグラフ同期

概要図

グラフ同期

使用コマンド

  • 編集
    • git remote add <REMOTE> <URI>: URIをリモートリポジトリREMOTEとして登録
    • git push <REMOTE> <BRANCH_NAME>: REMOTEにBRANCH_NAMEをアップロード
    • git fetch <REMOTE> <BRANCH_NAME>: REMOTEのBRANCH_NAME変更履歴をダウンロード
    • git pull <REMOTE> <BRANCH_NAME>: REMOTEのBRANCH_NAMEをHEADにマージ
      • git pullは内部的にgit fetchの後git mergeが実行されている
    • git clone <URI> <DIR_PATH>: DIR_PATHを初期化しURIリポジトリをコピー
  • 確認
    • git diff HEAD..FETCH_HEAD: git fetchとHEADの差分表示

詳細と備考

  • git fetchでコミットグラフが同期されるがブランチは変更されない
  • git fetch時にリモート追跡ブランチが移動する
  • リモート追跡ブランチはHEADが指せないポインタ
  • リモート追跡ブランチはディレクトリとファイルが実体
    • origin/masterはoriginディレクトリ内のmasterファイル
  • git mergeすることでリモート追跡ブランチの指すコミットまでブランチを進める
    • fast-forwardであればコミットグラフはリモートと同じ形のままブランチのみ移動で済む

運用

小規模のための簡単な運用方針

  • 普段使用するコマンドは以下のために決めておく
    • 覚える量を減らす
    • 同じ処理内容は同じコマンドにして明確にする
    • 普段想定外のことが発生しないようにする
  • GitHub flowを基にしてブランチを運用する
    • 個人開発のためプルリクエストは使用しない
  • git rebaseは以下理由により使用しない
    • 小規模かつ個人のためコミットの絶対数が少なくグラフ整理の必要性が薄い
    • 過去のコミットを追跡確認する場面は多くないと想定される
    • 考慮すべき事柄が増えることによる操作ミスというデメリットの方が大きい
  • git cherry-pickは考慮すべき事柄が増えるため使用しない
  • git checkoutはトラブルの元になりやすいため過去コミットの参照にのみ使用する
  • git mergegit pull時以外non-fast-forwardでマージする
  • git add時にblobオブジェクトが生成されるため安易に実行しない
  • git worktreeは便利そうだが必要になるまで使用しない
  • プロジェクトの純粋な成果物テキストファイルのみを管理対象とする
  • 認証情報などgitで管理すべきでない情報をどう扱うか最初に決める

初期設定

  • Git設定
    # 初期設定
    git config --global user.name "NAME"
    git config --global user.email "FOO@BAR.BAZ"
    git config --global color.ui auto
    # merge時のデフォルトをnon-fast-forwardに指定
    git config --global --add merge.ff false
    # pull時のmergeをfast-forwardに指定
    git config --global --add pull.ff only
    # fetch時にマージ済みブランチを削除
    git config --global fetch.prune true
    # push時に注釈タグを反映
    git config --global push.followTags true
    
  • GitHubを用いるプロジェクト初期化
    #---ここでGitHubでリポジトリ作成---#
    cd ~/projects/
    git clone https://github.com/XXXX/XXXXXX.git
    
  • ローカルのみのプロジェクト初期化
    # DIR_PATHの.gitディレクトリを初期化
    git init <DIR_PATH>
    #---ここでファイル作成---#
    git add .
    git commit -m "Initial commit."
    

参考文献

Discussion