Twitter Facebook github

頻出gitコマンド入門

最近、gitの使い方を人に説明することが多くなってきたので、頻出コマンドの使い方をリファレンス的に纏めておこうと思います。

なお、対象読者はgithubを用いてチーム開発をしているプログラマーです。
一人で開発するのであれば、例えばリモートサーバーへのpushやpullなどは無視してよいでしょう。

応用的なコマンドは分量を考えてまた別の記事にするかもしれません。適当にアップデートしていきます。

SourceTree使えばいいじゃん

A. 使えばいいと思います。

ただ… GUIが使えない環境で開発することになった場合や、とても単純なコミット1つするだけのためにGUIアプリケーションを起動して…というのはスマートではありません。
また、SourceTree等のGUIアプリケーションは内部的にgitコマンドを用いており、SourceTreeのGUI上で解決ができない状態に陥った場合はどうするのでしょうか?
(最近のSourceTreeは知りませんが、昔はよく直接コマンドを叩いて回復することがありました。)

またGUIに表示されるエラーやメッセージは、直接コマンドを叩いて得られる情報量と比べて少なく、問題解決や状況把握により時間がかかることもあります。

黒い画面に慣れていないデザイナーの方やディレクターの方はSourceTreeでも事足りると思いますが、実際にコードを書くエンジニアであれば、これらのコマンドは把握しておくべきだと思っています。

個人的にはGUIの裏側で勝手にコマンド流されるのが、環境壊されないか不安なんです 😉

ファイル操作

git status (現在の状態を把握する)

頻繁に使うと思います。現在のブランチ名やファイルの状態などを把握することができます。
マージやプル、ブランチの作成などを行う前には、現在のブランチを確かめる癖をつけましょう。

git add (ファイルを追跡対象に登録する)

ファイルを追加・修正・削除した場合、その時点ではワーキングツリーに登録されているだけで、インデックスされていません。
これらをコミットするために、インデックスするのがこのコマンドです。

引数にはファイル名を与えることもできますが、次の3パターンを用いることが多いです。

git add .

新たに追加または変更されたファイルをすべてaddします。削除したファイルはaddされません。

git add -u

変更または削除されたファイルをすべてaddします。新たに追加されたファイルはaddされません。

git add -A

新たに追加・変更・削除されたすべてのファイルをaddします。

このコマンドでインデックスされるのを防ぐための除外リストが .gitignore です。

git commit(変更をコミットする)

statusを実行して、Changes to be committed とマークされている内容を実際にコミットします。
単にファイルを変更しただけではコミット対象とはならず、前述のaddを先に実行しておく必要があります。

コミットにはコメントを追加する必要があります。引数なしで実行した場合、規定のエディタが起動するのでそのエディタに入力して保存することでコミットができます。

短いコミットメッセージの場合など、エディタを立ち上げたくない場合は、次のようにコミットと同時にコメントを追加できます。

これはローカルブランチに対する変更なので、github上でコミットを表示させるためにはgit pushを用いてリモートリポジトリにプッシュする必要があります。

git rm [file](ファイルを削除する)

単に git rm file.txt と実行すると、通常のrmのようにワーキングツリーから削除されてしまいます。

gitの操作をしていてたまに出会うシチュエーションとして、既にインデックスされているファイルをインデックスされていない状態に戻したいというケースです。
例えば .gitignore の除外ファイルを更新した場合、既にインデックスされている場合はコミット対象となってしまいます。

このような場合、 --cached オプションを追加することで、ワーキングツリーには残したままインデックス状態から外すことができます。

また、ディレクトリに対して実行する場合は -r を追加します。

リポジトリ操作

git clone [url](リモートリポジトリからコードをクローンする)

あるプロジェクトに参加したとき、まずしなければならないのはこのコマンドです。
githubなどにあるリポジトリを自分のマシン内にコピーしてきます。

使い方は、リポジトリを設置したいディレクトリに移動して次のコマンドを実行します。

なお、フォルダ名は指定しなければリポジトリ名になるため、省略することが多いです。

リポジトリのURLはgithubの場合、リポジトリを開いた後に画面中央あたりに記載されているものを使います。
httpsとssh、どちらでも問題ありません。経験上、sshを選んだ方がトラブルが少ない印象があります。
また、プライベートリポジトリの場合は事前にgitを使う端末の公開鍵をgithubに登録する必要があります。

Subversionなどのバージョン管理ではcheckoutと呼ばれていますが、大きな違いとして、gitのcloneはリモートリポジトリを丸々コピーしてくるという点にあります。
これには、すべてのサブブランチ、すべてのブランチのコミット(ヒストリ)が含まれます。
この性質により、万が一リモートサーバーのデータを失った場合でも、開発者の一人が以前にクローンものをプッシュするだけで元の状態に復元することができます。

git init (リポジトリを作る)

リポジトリを新しく作る時に使うコマンドです。

これをgithubで管理するためには、github上でリポジトリを作り、リモートリポジトリとして登録し、プッシュする必要があります。

余談ですが、gitのリポジトリを削除するにはリポジトリルートにある .git ディレクトリを単に消すだけです。

git pull [repository] [branch] (ローカルブランチに変更をプルする)

このコマンドは扱いが難しく、使わない方が良いと言われていたりもするのですが、皆手軽なのでこのコマンドを使いたがります。
というのも、このコマンドは実際には git fetch + git merge で成り立っており、gitを使う上でpullは覚えなくともなんとかなります。
ただし、上のfetchとmergeを理解するためにはgitのトラッキングブランチについての理解が必須であり、git初学者にとってはハードルが高いように思います。

このコマンドの注意点としては、このコマンドを実行したブランチに対して変更を取り込むため、次のように実行するとmasterブランチにdevelopブランチがマージされてしまいます。

また、ローカルブランチで何かコミットを行った場合はマージコミットが作られるため、マージしたくない時にpullを使ってはいけません。

使う場合には、現在のブランチと、マージしてもよい状態かを確かめるようにしましょう。

なお、後述しますがデフォルトのリモートリポジトリ(upstream)を登録することで引数を省略できます。

git push [repository] [branch] (リモートブランチに変更をプッシュする)

pullをダウンロードと捉えると、pushはアップロードになります。

git pullとの引数の扱いについて、

のように実行した場合でも、ローカルのmasterブランチがリモートのmasterブランチにプッシュされ、
間違ってもdevelopがmasterに入るということはありません。

なお、次のようにローカルとリモートのブランチ名を明示的に指定することもできますが、プッシュ時にマージされることはないため、fast-forwardの関係になっていないブランチにプッシュをしようとするとリジェクトされます。

また、pull同様、upstreamを設定することで引数は省略できるようになります。

ブランチ操作

git branch (ブランチの一覧を見る)

そのままです。カレントブランチには先頭にアスタリスクが付きます。
-r を付けることでリモートブランチを、 -a を付けることでローカル・リモートすべてのブランチが表示されます。

git checkout [branch] (ブランチの切り替え)

他のブランチに切り替えるときに使います。developブランチに切り替えるときは次のようにします。

注意として、今いるブランチにコミットしていない変更がある場合などはエラーとなり、切り替えできないことがあります。
gitに慣れてくれば、git stash等で一時退避などのワザがあるのですが、慣れるまではコミットするか、変更を元に戻すかして対応しましょう。

git branch [branch] (ブランチを作る)

fix-aaaブランチを作るためには次のようにします。

注意点は、現在参照しているブランチを元に指定したブランチを作成することになります。
例えば、masterからブランチを切り出したい場合はmasterに先に移動してからコマンドを実行します。
切り出す元となるブランチを間違えてしまうと面倒なことになるので気をつけましょう。

また、このコマンドはブランチを作るだけで、参照しているブランチを切り替えるには別にcheckoutを実行する必要があります。

前述した git checkout のコマンドに引数を付けることで、作成と移動を同時に行うこともできます。

これは次のコマンドと等価です。

git branch -d [branch] (ブランチを削除する)

簡単ですね。このコマンドではローカルブランチを消すだけなので、間違えて消してしまってもリモートから取得しなおせば良いです。
カレントブランチは削除できないので、別のブランチに移動した後で実行しましょう。

このコマンドはリモートにマージされていないブランチを削除しようとした場合に警告を出してくれます。無視して削除する場合は --force を追加するか、-dの代わりに大文字の-Dを使います。

また、リモートブランチを削除する場合は次のようにします。

または

前者は、”何も無いブランチ”を”branch”にプッシュする(=つまり削除する)イメージです。

リモートブランチの削除については危険を伴うために、一部の上級エンジニアにしか許可されないことが多いです。勝手に実行しないようにしましょう。

git merge [branch] (ブランチをマージする)

カレントブランチに指定したブランチをマージするコマンドです。苦手な人も多いですが、避けては通れない道です。

例えば、元々存在した branch_A から派生した branch_B を、 branch_A にマージする状況を考えます。

このとき、branch_Bの以前の親はbranch_Aにあるため、branch_Bで変更したファイルと競合する変更がbranch_Aから派生させた後のコミットに含まれない場合、gitはrecursiveというストラテジーによって自動的にマージが行われます。

このマージはとてもシンプルで、特にユーザー側によって手動でマージが必要なものではなく、git mergeの実行と同時に完了します。

また、更にシンプルなもので、派生元のブランチがマージされるまで全く変更されていない場合、単にHEADのポインタを進めるFast-forwardというマージが実行されることもあります。これは git pullの際によく当てはまります。

しかし、branch_Aとbranch_Bの間に競合するコミットが存在する場合、gitはどちらのブランチのコミットを採用するか判断ができません。この場合は自動マージに失敗し、コンソールにCONFLICTと表示され、ユーザーが手動でマージしなければならない状態におかれます。

この状態から抜け出すためには、まずgit statusを実行して Unmerged paths とマークされているコンフリクトしたファイルをエディタで開き、手動でマージする必要があります。

コンフリクトしたファイルには、gitによって次のようなコンフリクトマーカーが挿入されています。

このコンフリクトを解消するために、ここでは branch_A のコミットを採用するために、次のように書き換えます。

すべてのコンフリクトマーカーを削除し、手動でコンフリクトを解消したら、addとcommitによってマージを完了します。

その他

git config [key] [val](設定を変更する)

gitを使う場合にまず初めに実行するコマンドの一つで、自分のユーザー名とメールアドレスを登録します。
githubでは、githubで登録されているメールアドレスと紐付いてコミットしたユーザーが識別されるため、この設定は非常に重要です。

このコマンドによる設定はリポジトリ単位で変更できますが、ユーザー名・メールアドレスなどリポジトリによって使い分ける必要のない設定は --global を追加することによって、すべてのリポジトリで有効になります。

また、コミットメッセージなどを入力する際に起動されるエディターはシステムで規定のものが使用されます。
大抵はviまたはvimになっているはずなので、例えばEmacsを使いたい場合はeditor設定によって上書きできます。

git gc (リポジトリの最適化を行う)

リポジトリ内を圧縮したり、不要になったファイルを削除してリポジトリを最適化してくれます。
リポジトリ全体の容量が減るほか、パフォーマンスも改善することがあり、マイナーながら個人的にはよく使うコマンドです。
頻繁に実行する必要はまったく無いのですが、大量コミットや大きなマージを行ったあとに実行すると効果的です。

To be continued…

log, revert, rebase, cherry-pick, reset, stash, tag, blame, remote あたりは追記したいな〜って思ったり、思わなかったり…

コメントを残す

メールアドレスが公開されることはありません。