Heavy Watal

Git入門2019

2019-10-30 東北大学 生命科学研究科 進化ゲノミクス分野 牧野研

前半スライド

Environment / 環境

OS

新しめのUNIX系OSが好ましい。

Common

SSH (任意)

Essential commands / 基本操作

Fetch existing repositories: clone

  1. GitHub上の適当なリポジトリをひとつ選ぶ。 (e.g., https://github.com/heavywatal/tumopp)

  2. 右の方の緑の “Clone or download” ボタンを押す。

  3. SSHではなくHTTPSを選択し、URLをコピー。

  4. ターミナルにコマンドを入力:
    git clone https://github.com/heavywatal/tumopp.git

  5. 中身を眺めてみる:

    cd tumopp/
    ls -al
    git log
    git remote -v
    

最新版のスナップショットだけでなく、 履歴もごっそり複製するので、 このあとはオフラインでもいろいろ操作できる。

Create new repositories: init

  1. GitHubの右上の “+” から “New repository” を選択。

  2. Repository name を例えば helloworld として “Create repository” を押す。 いくつかのファイル (README.md, LICENSE, .gitignore) をここで作ることもできるけど、今回はとりあえず空っぽのリポジトリを作る。

  3. 手元のマシンにローカルリポジトリを作る:

    mkdir helloworld
    cd helloworld/
    git init
    ls -al
    

    リポジトリの本体 .git/ が作成されたことを確認。

  4. 空っぽのコミットを作る:

    git status
    git commit --allow-empty -m ":beer: Create repository"
    git status
    git log
    

    事あるごとに git statusgit log を確認すると安心。

  5. 先程作ったリモートリポジトリを紐付けて、pushしてみる:

    git remote -v
    git remote add origin https://github.com/YOUR_NAME/helloworld.git
    git remote -v
    git push -u origin master
    git status
    
  6. GitHubで履歴を閲覧し、 git log と同じになってることを確認。

Export local changes to a remote server: push

  1. 上で作ったリポジトリに、適当なファイルを追加:

    echo '# Hello, world' > README.md
    cat README.md
    git status
    
  2. 作ったファイルをstaging areaに追加:

    git add README.md
    git status
    git diff --staged
    
  3. この変更をcommit:

    git commit -m ":memo: Create README.md"
    git status
    git log
    git show
    
  4. リモートにpush:

    git push
    git status
    git log
    

Import changes from a remote server: fetch

  1. 上のリポジトリでそのまま git fetch してみる。 ローカルとリモートは同じ状態なので当然何も起こらない。

  2. 練習のためGitHub上で LICENSE ファイルを作成する。

    1. GitHub上のリポジトリのトップページを開き “Create new file” ボタンを押す。
    2. ファイル名に LICENSE と入力。
    3. 右に現れる “Choose a license template” というボタンを押す。
    4. とりあえず “MIT License” を選択。
    5. YearとNameを適当に埋めて “Review and submit”。
    6. “Commit directly to the master branch” を選択して “Commit new file”
  3. その変更をローカルリポジトリに取り寄せる:

    git fetch
    git status
    git log --all
    ls -al
    

    リポジトリ内部 .git/origin/master は更新されたが、 working directoryにはまだ反映されていない。

  4. origin/master の内容を手元のファイルに反映する:

    git merge
    git status
    git log
    git show
    ls -al
    

git fetchgit merge を一気にやってくれる git pull というコマンドもあり、 普段の一人作業ではよく使う。

Other commands

diff

差分を表示:

# HEAD vs working (staging前のファイルが対象)
git diff

# HEAD vs index (staging済みcommit前のファイルが対象)
git diff --staged

# HEAD vs working+index (commit前の全ファイルが対象)
git diff HEAD

# 特定コミットの変更点 (diffじゃない...)
git show [revision]

rm, clean

tracking対象から外して忘れさせる(手元のファイルはそのまま):

git rm --cached <file>

.gitignore で無視されてるuntrackedファイルを消す:

git clean -fdX

無視されていないuntrackedファイルも消したい場合は小文字の -fdx (危険)。

reset

git reset <DESTINATION>HEAD の位置を戻す処理で、 オプションによってindexとworing treeもそこに合わせるように変更される。 --soft なら HEAD 移動のみ。 --mixed なら移動した HEAD にindexも合わせる。 --hard なら移動した HEAD にindexとworiking treeも合わせる。 直前の動作を取り消す用途に絞って使うのが無難:

# commit直後、それを取り消す (indexとworkingはそのまま)
git reset --soft HEAD^

# add直後、それを取り消す (workingとHEADはそのまま)
git reset --mixed HEAD

# 変更したファイルをHEADの状態に戻す (DANGEROUS!)
git reset --hard HEAD

# reset直後、それを取り消す
git reset --hard ORIG_HEAD

# divergedになってしまった手元のbranchを破棄 (DANGEROUS!)
git reset --hard origin/master

直前のcommitをちょっと修正したいだけなら git commit --amend が簡単。 それより前のを修正するには git rebase -i HEAD~3 とかで戻ってrewordやedit。

リモートにpush済みのものは改変しちゃダメ!

Collaboration

Pull Request (PR)

他人のリポジトリに貢献するためのGitHubの機能。
e.g., https://github.com/Rdatatable/data.table/pull/2807

  1. 貢献したいリポジトリをForkして自分のGitHubアカウントに追加。
  2. Forkした自分のリポジトリを手元にclone。
  3. PR用のブランチを作って、そこでソースコードを編集。
  4. コミットして、ひとまず自分のGitHubアカウントにpush。
  5. GitHub上で大元のリポジトリにPRを送る。
  6. 取り込んでもらえたら、用済みのブランチを削除。

2人1組でPRとmergeを体験

(できれば横に並んで相手の画面も見えるように)

  1. GitHubで新しいリポジトリを作成

  2. 何かtypoを含む README.md を作ってpush

  3. 相手のGitHubリポジトリでその README.md が見えることを確認

  4. 右上のForkボタンで自分のGitHubリポジトリに取り込む

  5. forkした自分のリポジトリからローカルにclone:

    git clone https://github.com/{PAWN}/PROJECT.git
    cd PROJECT/
    
  6. 大元のリポジトリにupstreamという名前をつけておく:

    git remote add upstream https://github.com/{KING}/PROJECT.git
    git remote -v
    

    ちなみに自分のリポジトリには自動的に origin という名前がついている。

  7. PR用のブランチを切って移動:

    git checkout -b fix-typo
    
  8. README.md をテキストエディタで編集して commit:

    git diff
    git commit -a -m ":memo: Fix typo in README.md"
    

    Git連携機能のあるエディタを使っている場合、 そこからdiffやcommitをやってみてもよい。 コードの追加・変更・削除による色分けの便利さも体感しよう。

  9. この間にupstreamで更新が無いかどうか確認:

    git fetch upstream
    

    もしあったら、それをデフォルトブランチ(master)越しに取り込む:

    git checkout master
    git merge upstream/master
    git push origin/master
    git checkout fix-typo
    git rebase -i master
    
  10. 自分のリポジトリにpush:

    git push origin fix-typo
    
  11. GitHub上に出現する “Compare & pull request” ボタンを押す。

  12. 差分を確認し、コメント欄を埋めて提出。

  13. 受け取ったPRを確認。必要に応じて修正を要求したり、自分で修正したり。

  14. 修正を求められたらそのブランチに続けてcommitしてまたpush。

  15. 問題が無ければmergeする。

  16. 自分のローカルリポジトリに pull (fetch+merge) する。

  17. 無事マージされたら作業ブランチを消す。

Tips

Glossary / 用語

https://help.github.com/articles/github-glossary/

repository
commitの履歴を保持する拠点。 「ひとつのRパッケージ」とか「1冊の本の原稿」のような単位で作る。 git init で手元に新規作成するか、git clone でリモートから複製する。
commit
git内部でroot treeのsnapshotを指すオブジェクト。 root treeのハッシュID、著者、コメントなどの情報を持つ。 動詞としては、staging areaの情報をひとつのcommitとしてリポジトリに登録することを指す。
tree
git内部で1つのディレクトリを指すオブジェクトで、commitした時に作られる。 blobやファイル名などのメタデータに依存したハッシュIDを持ち、 その変化は親に伝播する。
blob
git内部で1つのファイルを指すオブジェクトで、add時に作られる。 ファイル名などのメタデータは持たず、 ファイルの内容にのみ依存したハッシュIDを持つ。
origin
remoteリポジトリの典型的なshortname。 clone時に自動的に追加され、 push先やfetch元を省略したときにデフォルトで使われる。 git remote -v で確認。
master
デフォルトのブランチの典型的な名前。
HEAD, @
現在checkoutしているbranch/commitを指すポインタ。 基本的にはmasterの最新commitを指していることが多い。 1つ前は HEAD^HEAD~、 2つ前は HEAD^^HEAD~~HEAD~2。 (HEAD^2merge で複数の親がある場合の2番目)

zshのEXTENDED_GLOBが有効になってる場合は HEAD^ がパターン扱いされてエラーになるので、 HEAD\^ のようにエスケープするか unsetopt NOMATCH しておいたほうがいい。

Further reading