Git チートシート [PDF]
(wizardzines.com)- Gitでよく詰まりがちな作業を、コミット作成、ブランチ移動、変更比較、リモート同期、変更取り消しのコマンドごとにすばやく引けるよう整理したPDF
- ローカル作業フローは
git status、git add、git commit、git diffを中心に、ステージング前後の状態を確認してコミットを作る方式 - ブランチ作業は
git switch、git checkout、git branch、git merge、git rebase、git cherry-pickで 作成・移動・マージ・付け替え を行う - 履歴の探索と復旧には
git log、git blame、git show、git reflog、git reset、git restoreが含まれ、ブランチ・タグ・HEAD・リモートブランチもコミット参照として使える - リモートリポジトリ作業は
git remote add、git fetch、git pull、git push、git push --force-with-lease、git push --tagsで変更を取得・送信する手順を扱う
リポジトリの開始と現在の状態確認
- 新しいリポジトリを開始:
git init
- 既存リポジトリを複製:
git clone $URL
- 現在位置と状態を確認:
git status
コミットの準備と作成
- 追跡されていないファイルを追加:
git add $FILE
- すべての未追跡ファイルとステージングされていない変更を追加:
git add .
- ファイルの一部の変更だけを選んでステージング:
git add -p
- ファイルを削除または移動:
git rm $FILEgit mv $OLD $NEW
- ファイルを削除せずに Git の追跡対象から外す:
git rm --cached $FILE
- すべてのステージングを取り消し:
git reset HEAD
- コミットを作成:
git commit- テキストエディタを開いてメッセージを作成
- メッセージを直接指定してコミット:
git commit -m 'message'
- ステージングされていない変更も含めてメッセージ付きでコミット:
git commit -am 'message'
ブランチの移動と管理
- ブランチを切り替え:
git checkout $NAMEgit switch $NAME
- ブランチを作成:
git checkout -b $NAMEgit switch -c $NAME
- ブランチ一覧を見る:
git branch
- 最新コミット基準でブランチを並べ替え:
git branch --sort=-committerdate
- ブランチを削除:
git branch -d $NAME
- ブランチを強制削除:
git branch -D $NAME
履歴の探索とコード追跡
- ブランチ履歴を見る:
git log main
- 2つのブランチの関係をグラフで見る:
git log --graph a b
- 1行ログを見る:
git log --oneline
- ファイルの各行を最後に変更した人を確認:
git blame $FILENAME
- ファイルを変更したすべてのコミットを見る:
git log $FILENAME
- 特定のテキストを追加または削除したコミットを探す:
git log -S banana
変更の比較
- コミットとその親コミットとの差分を見る:
git show $COMMIT_ID
- マージコミットとマージされた親との差分を見る:
git show --remerge-diff $COMMIT_ID
- diff の要約を見る:
git diff $COMMIT_ID --statgit show $COMMIT_ID --stat
- 2つのコミットを比較:
git diff $COMMIT_ID $COMMIT_ID
- 特定ファイルだけをコミットと比較:
git diff $COMMIT_ID $FILENAME
- ステージング済み変更と未ステージング変更をまとめて比較:
git diff HEAD
- ステージング済み変更だけを比較:
git diff --staged
- 未ステージング変更だけを比較:
git diff
設定ファイルとオプション
- ローカル Git 設定ファイル:
.git/config
- グローバル Git 設定ファイル:
~/.gitconfig
- 無視するファイル一覧:
.gitignore
- 設定オプションを指定:
git config user.name 'Julia'
- グローバルオプションを指定:
git config --global ...
- alias を追加:
git config alias.st status
- 使用可能な設定オプションを確認:
man git-config
履歴の修正とファイルの復元
- 最新コミットを取り消し、作業ディレクトリはそのまま維持:
git reset HEAD^
- 失敗した rebase を戻す:
git reflog BRANCHNAMEgit reset --hard $COMMIT_ID
- 直近5件のコミットを1つにまとめる:
git rebase -i HEAD^^^^^^- まとめるコミットは
pickをfixupに変更
- 別ブランチや別コミットのファイル版を取り込む:
git checkout $COMMIT_ID $FILEgit restore $FILE --source $COMMIT_ID
- コミットメッセージを変更、または入れ忘れたファイルを追加:
git commit --amend
ブランチを統合する
- 現在のブランチを別ブランチ側へ最新化する fast-forward merge:
git switch maingit merge banana
- あるコミットを別ブランチへコピー:
git cherry-pick $COMMIT_ID
- 分岐したブランチを rebase で統合:
git switch bananagit rebase main
- 分岐したブランチを merge で統合:
git switch maingit merge bananagit commit
- 分岐したブランチを squash merge で統合:
git switch maingit merge --squash bananagit commit
リモートリポジトリとの同期
- リモートリポジトリを追加:
git remote add $NAME $URL
mainブランチをoriginリモートへ push:git push origin main
- 初回 push のブランチを
originに上げて追跡ブランチを設定:git push -u origin $NAME
- 現在のブランチをリモート追跡ブランチへ push:
git push
- force push:
git push --force-with-lease
- タグを push:
git push --tags
- 変更を取得、ローカルブランチは変更しない:
git fetch origin main
- すべてのブランチを fetch:
git fetch --all
- fetch 後に現在のブランチへ rebase:
git pull --rebase
- fetch 後に現在のブランチへ merge:
git pull origin maingit pull
コミットを指す方法
$COMMIT_IDの位置にはさまざまな参照を使える:- ブランチ:
main - タグ:
v0.1 - コミット ID:
3e887ab - リモートブランチ:
origin/main - 現在のコミット:
HEAD - 3つ前のコミット:
HEAD^^^ - 3つ前のコミット:
HEAD~3
- ブランチ:
変更を破棄する
- 1ファイルのステージング済み変更と未ステージング変更を両方削除:
git checkout HEAD $FILE
- 1ファイルの未ステージング変更を削除:
git checkout $FILE
- すべてのステージング済み変更と未ステージング変更を削除:
git reset --hard
- ステージング済み変更と未ステージング変更を stash:
git stash
- 未追跡ファイルを削除:
git clean
1件のコメント
Hacker Newsの意見
隠れた逸品のようなコマンドがいくつかある:
git diff --stagedは最後のコミットと現在ステージングした内容との差分を表示してくれるので、ファイルやハンクを細かく追加するときに意図した内容だけが入っているか確認するのに便利git log <file>はそのファイルが変更されたコミットだけを表示してくれるため、全体のgit logを探し回る必要が減り、特に頻繁には変わらないファイルで有用git add --patchを使うとき、Gitが分割したハンクよりもさらに細かく選びたいなら、eオプションを試すとよい。個々の行を含める/除外する方法が案内されるので、覚えておく必要はないgit diff --stagedがgit diff --cachedのエイリアスだと今知った。cachedを何年も使ってきたが、stagedのほうがずっと直感的ではある。ただ、この筋肉の記憶を消せるかどうかは分からない興味があれば見てもいいし、エイリアスを提案してもよい: https://gitlab.com/Falimonda/gitrc
git config --global commit.verbose trueを設定すると、Gitがコミットメッセージエディタのコメント領域にステージング済みの差分を自動で入れてくれる一覧にはないが、好きなコマンドの一つは
git log -LstartLine,endLine:fileName拡張された
git blameのように、指定した行が時間とともにどう変わり、誰が変えたのかを表示してくれる。行番号が変わったりファイルが移動したりしても追跡する「誰が、いつ、なぜこうしたのか?」や「いつからこうなっているのか?」といった質問に答えてくれる
「コンピュータ科学には難しいことが2つだけある。キャッシュの無効化と名前付けだ」というPhil Karltonの言葉を思い出す
そういう観点では、Gitコマンド名が**
blame**なのはかなり変に感じたし、そう思ったのは自分だけではなかったWhat does 'git blame' do? https://stackoverflow.com/questions/31203001/what-does-git-blame-do
Blame someone else for your bad code https://news.ycombinator.com/item?id=27963868
Git blame should be called git credit https://dev.to/damcosset/git-blame-should-be-called-git-credit-27h5
Does Git Blame sound too negative? https://www.reddit.com/r/ProgrammerHumor/comments/r5lzyo/does_git_blame_sound_too_negative_just_look_up/
gitというツール名を考えると、blameはブランドに合った名前のようにも思えるblameを**who**に変えた。実際にはVimの筋金入りユーザーなので、単にwとして使っているGitを学んだ方法は気に入っている。最初はサードパーティ製Git GUIのSourcetreeから始めたが、使うのも理解するのもとても簡単だった
その次に
git guiへ移ったが、この段階は飛ばしてもよい。その後、UIを開くのが面倒になってターミナルコマンドを覚えたGit初心者にはおすすめするが、あくまで自分のn=1の経験にすぎない
clone、pull、commit、pushくらいで、これまではそれで十分だった。Gitはそれくらい単純にもなり得る。もちろん必要なら、より複雑な使い方も学べるMagitを使い始めた瞬間、Gitが理解できるようになった
Sublime Mergeは褒めることしかない
Git が不必要に複雑なのには歴史的な理由があるのか気になる
このチートシートを見ると、その複雑さをさらに実感する
ここまで広く使われるようになったのは「幸運な事故」か、あるいは不幸かもしれない。GitHub が「無料の Git ホスティング」で Git を普及させた功績は大きいが、Git が多くのプロジェクトにとって理想的なバージョン管理システムだとは思わない
Linus と Linux のために作られたので、用語やワークフローが一般的な他のバージョン管理システムとは逆に感じられることがある。
Pull Requestという名前も、Linux 開発で Linus やメンテナーが変更をカーネルツリーへ取り込むから。ほかのシステムでは通常Merge Requestと呼び、どこから来た変更であれツリーにマージするという点で、そちらのほうがずっと論理的Git は巨大なコードベースを高速に扱える、ほぼ唯一のオープンソースの分散バージョン管理システムだったので、大企業でも慣性が大きくなった
個人プロジェクトでは Mercurial のほうをずっと好んで実際に使っているが、Android AOSP のような大きなコードベースで Hg を使ってみると、かなりもたつく。Mercurial はユーザーインターフェイスとコマンド体系がより良く、変更ツリーを見るためにその場で Web サーバーを立ち上げる機能もあるが、Python で書かれているため、性能とスケーラビリティは理想的ではない
git checkoutのような既存のサブコマンドに機能が次々と追加され、ユーザー体験がより混乱するようになった最近の Git では新しいサブコマンド追加のコストが下がり、
git switchのように、より具体的なコマンドを追加し始めているlog、add、commit、pull、pushのようないくつかの Git コマンドだけ知っていれば十分だと思う。GUI ツールもあるし正直、一般的な作業では Git がそれほど複雑だとは思わない。ただ、必要なときに使える強力な機能がたくさんあるだけ
カーネル開発者は分散型のやり方で互いにやり取りし、互いの作業の上にさらに作業しなければならなかった。15,000 人がコードを書くプロジェクトでどう働くのか? Git はその目的のために作られた
設計対象のワークフローを支えるのに必要なだけ複雑。もっと単純なツールを作るのは簡単だが、そうすると Linux カーネル開発のような複雑なワークフローは支えられない。だから不必要に複雑なのではないが、単純な用途には必要以上に複雑なのは確か
もっと複雑でないフロントエンドが必要なら、Git を扱いやすくしてくれるツールはたくさんある。専業のソフトウェア開発者でないなら、Git を直接使うことはおすすめしない。だが専業開発者なら、内部概念を理解するために時間をかける価値があり、いったん理解すれば、Git はやっていることのわりにかなりエレガントだ
Git を知っているかどうかでソフトウェア開発者の資格を線引きするつもりはないが、こうした基盤システムがどう動くのかを自分で把握することにドーパミンを感じないなら、この分野は好みではないのかもしれない。それでも構わないし、誰にでも向き不向きがある
Git の移動を学ぶには https://learngitbranching.js.org をおすすめする。もちろん https://xkcd.com/1597/ も当てはまる
git add -pを知らなかったが、本当に便利-p、--patchは、インデックスと作業ツリーの間のパッチハンクを対話的に選んでインデックスに追加できるようにする。修正内容をインデックスに追加する前に差分を確認する機会を与えてくれるここに追加したいのは、
git commit --fixup $COMMIT_IDとgit rebase -i upstream/master --autosquashの組み合わせ最近のワークフローでは必須要素になっている。Julia の例では直前の 5 コミットをリベースするために
HEAD^^^^^を使っているが、私は最近までHEAD~5を使っていた。ところが今では、upstream のHEADまでのすべてのコミットをそのままリベースできることが分かったgit commit --fixup $COMMIT_IDはすぐに取り入れたい。私はいつも “oops GET users/me” のような小さな修正用コミットを作り、次の対話的リベースで手動で移動して fixup していたが、このやり方のほうがずっと良い「最後の 5 コミットを 1 つにまとめる:
git rebase -i HEAD^^^^^^」はgit rebase -i HEAD~5が正しいのではないかと思う線形履歴だけをリベースしたい場合なら、という前提だが
https://stackoverflow.com/questions/2221658/whats-the-difference-between-head-and-head-in-git
最大の気づきは、すべてのコミットがまだ内部に残っていると理解した瞬間だった。タグやブランチは単にコミットを指しているだけで、ブランチが以前指していたコミットも消えたわけではない
多くのコマンド、たぶんほぼすべてと言っていいほどのコマンドは非破壊的なので、ブランチの以前の状態を簡単に参照できる
git reflogは見たことがあったが、主に「前に作業していたあのブランチは何だったっけ?」と思い出す用途にしか使っていなかった望まないコンフリクト解決の真っただ中に入り込む可能性のある操作に対して、半ば健全な恐怖心を持つようになった。たいていは間違ったコミットやブランチを参照してコンフリクトが起きていたからだ
その恐怖が完全になくなったわけではないが、少なくともリベースのような操作の途中で以前の作業を復旧することにはかなり慣れた
ある操作の後にコンフリクトが起きるか分からなければ、とりあえずやってみて確認する。コンフリクトが起きたら解決するか、コンフリクトを作った操作を取り消すか、別の場所に新しいコミットを作ってコンフリクトは後で処理するようにしておく
コンフリクトが起きると分かっているなら、関連するブランチを複製してから事前に合わせてみるか、コンフリクトする操作を実行して解決手順を試してみる。解決が面倒になったら一部の解決だけを取り消して試し続けるか、いったん保留して別の作業をするか、ツリー全体を捨てることもできる
率直に言えば、Gitはいったん置いて JJ を見るべきだ
https://martinvonz.github.io/jj/v0.17.1/
LinusでさえGitのUIはひどいと思っているはずだ。JJのワークフローは素晴らしい
ただ、少し触ってみた以外に実務で深く使ったわけではないので、断定はしにくい