9
20

More than 5 years have passed since last update.

gitにハマらないために

Last updated at Posted at 2017-11-16

各用語

  • リポジトリ:コードを管理する単位。管理するソースコードが1つのディレクトリにまとまっており、そのディレクトリ内のファイルの変更履歴が管理される。
  • コミット:コードの変更をまとめて確定したもの。変更は複数のファイルにまたがるときもある。
  • ステージ:「次にこれをコミットします」と選ばれた変更のひとまとまり。
  • ブランチ:コードの変更の流れが分岐した枝のこと。複数の開発者で同時に開発するための機能。
  • HEAD:各ブランチの最後のコミット。
  • masterブランチ:一番メインのブランチ。一般的に、共同開発している場合、masterブランチに直接変更を加えるのはよくない。
  • トピックブランチ:master以外のブランチ。masterから分岐して造られる。bug_fixとかrefactorとかadd_new_featureとか(さらにはもっと詳しく)開発トピックごとに作られるのが正しい。
  • マージ:他のブランチの変更をいまいるブランチに取り込む操作。共通の分岐点(ブランチベース)以後の変更が取り込まれる。取り込まれる側(参照側)には何も変更はない。マージも1つのコミットになる。
  • プルリクエスト(これはgithub用語):(主にmasterに)自分のブランチを取り込んで(マージして)くださいと依頼する。この時のマージはレビューが入り、web上で行われることが多い。

途中でgitをやめたくなったら

git管理にあるディレクトリの最上位(ルートディレクトリと呼ぶ)に.git/というディレクトリがある(通常、隠れている)。
この中に全ての履歴や情報が入っており、gitはこれを認識している。
この.git/ディレクトリを削除してしまえば、そこはgit管理化からはずれる。(もちろん、変更履歴も何もかも全て消える。手元のファイルはそのまま。)

初めてのgit

自分用のgitの設定 (git config)

ユーザ名とeメール

git config --global user.name {myusername}
git config --global user.email {myemail@address.com}

UIの設定(オススメ)

git config --global color.ui auto
git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto
git config --global core.quotepath false

便利系エイリアス(これもオススメ)

git config --global alias.loga "log --oneline --tags --graph --decorate --all"

これを入れておくと、git logaでgranchのグラフが見える。

リポジトリの作成

git init もしくは git clone {URL}

  • 手元のディレクトリをgitリポジトリにする場合にはgit init
  • すでにあるリポジトリをコピーするならgit clone
  • githubで新しく空リポジトリを作り、手元のディレクトリにgit cloneするのも良い(空っぽのリポジトリを今のディレクトリにcloneしても、手元のファイルは消されない)

最初のコミット

touch test.c ファイルを作成
git add test.c ステージング:これからコミットにのせる変更を選択。
git commit -m "first commit"   メッセージ付きでコミット

なおここまで順にやってくると、masterブランチにコミットしているはず。

共同開発(リモートにpushするまで)

リモートの追加

git remoteで追加する。
というか追加しなくても使えるので、「エイリアスを登録するコマンド」と捉えた方が正確。
なお、originというリモートだけは特別で、省略したときのデフォルト名になる。
どうせリモートリポジトリは一つだろうから、とりあえず登録しておこう。

git remote add origin git@github.com:yourname/your_project.git

これで、 git@github.com:yourname/your_project.git と originが等価。

一般的な開発の流儀

これは基本とされる割にはどこにも書いていない。

まず、masterからbranchを作成

git checkout -b new_feature

次に、作成したbranch上で開発、commitする。

echo "p 'hello world'" >> hello.rb
git add hello.rb
git commit -m "added hello.rb"

自分がつくったbranchなので遠慮なくpushする(最初は上手くいかない。すぐ後のupstreamで説明する)。

git push

(githubなど)webインタフェースから、masterにマージしてもらうための申請をする(プルリクエスト)。

upstreamとは

各ローカルブランチに、リモート側として対応付いたブランチをupstreamブランチという。
upstreamブランチにしかpushできない。push時に対応付けする。

git push --set-upstream origin new_feature

ローカルとブランチ名が一緒である必要はない(一緒の方がわかりやすいが)

git push --set-upstream origin my_new_feature

などでも可能。

なお、とりあえずgit pushを叩くと、上記コマンドを提示してくれる。
面倒な人はそれをコピペするとよい。
--set-upstreamのショートオプションは-uだが、ゆえにロングを表記した)

pushできない場合

(すでにupstreamブランチが存在する場合)upstreamブランチ上の変更が今いるローカルブランチに含まれていない場合、pushできない。

自分だけがpushしてるはずなのに、そんなことあるもんかと思うが、upstreamブランチは、作った人専用というわけではない。
うっかり他の人と同じ名前を使ってしまうと、衝突の危険性がある(なので、bug_fixのような単純な名前はやめよう)。
逆に、あえて同じリモートブランチで一緒に作業することもある。

ともあれ、pushしようとしている先のupstreamブランチ上のすべての変更を取り込んだ状態(fast-forwardと呼ぶ)にしないと、pushできないので、これを修正する必要がある。

共同開発(他の人が編集してpushしていた時の解決)

マージ

マージそのものはリモート関係ない。ローカルブランチ同士でもできる。
あくまで、相手側(指定したブランチ)の変更を、自分側(今いるブランチ)に取り込むこと。

マージをすると
- 手元のファイルが変更される
- コミットされる(マージコミットと呼ばれる)
- マージされたということがgitの記録に残る(これがとても大事。)

なお、ここでの「変更」というのは、「共通の分岐点」からの変更である。

共通の分岐点に対して、相手側も自分側も変更を加えていた場合、コンフリクトが起こる。
コンフリクトは自動で解決される場合もあるが、手動で解決しなければならない場合もある。

fetchとpull

  • fetchはリモート(upstream)ブランチからデータを取ってくる。
  • pullfetchmergeを同時に行う。

リモートブランチとトラッキングブランチ

  • fetchでupstreamブランチのデータをダウンロードする先がトラッキングブランチ。
  • つまりトラッキング(追跡)ブランチは、ローカルにある、リモートブランチのコピー(のようなもの)。
  • トラッキングブランチに対してコミットすることはできない。
  • fetchコマンドを叩くと、リモートブランチをトラッキングブランチに持ってくる(トラッキングをリモートと同期する)。
  • これを自分のところにmergeする。
  • pullコマンドを叩くと、fetchとmergeを同時にやってくれる。
  • pullがうまくいく(コンフリクトも解決される)と、今度はpushできるようになる。
  • マージはトラッキング → ローカル → リモート の三角形の形で行われる。

トラブルシューティング

pushができない場合

とにかく、git log --oneline --tags --graph --decorate --all (先ほど追加した場合は、git loga)を頻繁に確認しよう。

  • git reset HEAD^でうっかりHEADを戻していたりしないか? → pushでは葉の先にコミットを積む方向にしか進められない

  • うっかりミスgit-promptを入れよう。常に状態がわかる。

  • 最後の手段としてgit push -fというのがある。今の自分が正しいとして強制的にpushする。他の人が同じリモートブランチを参照している場合、一貫性が取れず状態がおかしくなるので、めっちゃ怒られるだろう。(push -fを禁止しているレポジトリもある。それくらいのご法度である)

コミットに関してよくある混乱

コミットは「差分」なのか、コミット時点での「スナップショット」なのか。

  • 内部処理では、「スナップショット」を保存している。
  • けれども、たいていの操作(merge, rebase, cherry-pick)では差分を扱っていると捉えた方が理解しやすい。
  • checkoutで別のコミットからファイルを持ってくるときだけは、「スナップショット」と考える必要がある。

間違えやすいコマンド

git reset

「ステージングを解除する」機能と、「HEADを過去のcommitまで移動する」(つまり、コミットを取り消す)という機能が混ざったように見える。
(実際は、ステージングとHEADを同時に移動する機能らしい。引数がないと、両者を現在のHEADに移動する(つまりHEADそのものは移動せず、ステージングだけ移動=解除となる))

両者とも、--hardをつけると実際のファイルまで変更するので、間違えると危険。

git add test.c   # test.cの変更がステージングされる。
git reset        # ステージングが解除される
git reset HEAD^        # HEADを一つ前のコミットに移動する。

git checkout

branchの切り替えと、別のbranchからのファイルを持ってくるのが混ざっている。
ファイルパスをつけるとファイルを持ってくる。

git checkout master   # ブランチがmasterに移動する
git checkout master hello.rb        # hello.rbというファイルをmasterから持ってくるだけ。ブランチは移動しない。
9
20
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
20