Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Gitの使い方(応用編) / GitHub Advanced

kaityo256
October 11, 2021

Gitの使い方(応用編) / GitHub Advanced

物理情報工学ソフトウェア開発演習

kaityo256

October 11, 2021
Tweet

More Decks by kaityo256

Other Decks in Education

Transcript

  1. 3 37 $ git add test.txt $ git commit -m

    “updaets test.txt” スペルミスしたことにコミット後に気づいた 既に歴史に誤りが刻まれてしまった $ git log --oneline 8f7d4f8 (HEAD -> main) updaets test.txt 78efaf0 initial commit
  2. 4 37 $ git commit --amend カレントブランチの最新コミットのコミット メッセージを修正する $ git

    commit --amend -m "updates test.txt" -mと一緒に使うことが多い 歴史が修正された $ git log --oneline 52304ef (HEAD -> main) updates test.txt 78efaf0 initial commit
  3. 5 37 initial commit 78efaf0 8f7d4f8 updaets test.txt initial commit

    78efaf0 52304ef updates test.txt git commit --amendによりコミットハッシュが変わる →歴史が書き換わる プッシュしたブランチをリベースしてはならないのと同じ 理由で、プッシュしたブランチをamendしてはならない
  4. 6 37 $ git restore ファイル名 いろいろ修正したが、よくわからなくなったので 修正をなかったことにしたい 指定したファイルを、最新のコミットの状態まで戻す 注:git

    restoreはgit 2.23から導入された比較的新しいコマンド で、それまではgit checkoutやgit resetを使っていた。それぞれ 操作に失敗すると危険なので、git restoreを使うと良い(後述)。
  5. 7 37 間違えてステージングしてしまったファイルを取 り消したい $ git restore --staged ファイル名 インデックスに登録されたファイルを取り消す。インデック

    スは最新のコミットの状態に戻る。 git addで余計なファイルをステージングした時などに使う。 --stagedは-Sでも良い ワーキングツリーの修正を取り消すには--worktreeもしくは-W デフォルトでは-Wが暗黙に指定される
  6. 8 37 チェックアウトとは リポジトリからファイルを 取り出すこと リポジトリから指定したブランチのコミットを取り出して ワーキングツリーに展開する = ブランチを切り替える $

    git checkout ブランチ名 $ git checkout ファイル名 インデックスから指定したファイルを取り出してワーキング ツリーに展開する = ステージングされていない修正を取りけす
  7. 9 37 git checkoutはコミットを指定してチェックアウトできる $ git checkout コミットハッシュ main HEAD

    9b662ef $ git checkout 9b662ef main 9b662ef HEAD いわゆる「頭が取れた(Detached HEAD)」状態になる
  8. 10 37 git checkoutが担う役割が多すぎる問題 →Git 2.23.0からswitchとrestoreが追加 ブランチ切り替え機能 → git switch

    修正取り消し機能 → git restore 原則としてgit checkoutは使わない git switchもコミットハッシュを指定できるが、ブランチ名を つけることが強制される→より「安全」に $ git switch -c branchname 9b662ef ※ 古い情報にgit checkoutの用法が載っていることがあるので注意
  9. 11 37 GitHubを使っていて、SSHのつもりがHTTPSで リモートを登録してしまった git remote add origin https://github.com/appi-github/somerepository.git git

    branch -M main git push -u origin main $ git remote add origin [email protected]:appi-github/somerepository.git error: remote origin already exists. 改めてSSHで登録しようとしても…… 「既にoriginという名前のリモートリポジトリが存在 するよ」と言われて追加を拒否される
  10. 12 37 $ git remote remove origin 指定したリモートリポジトリを削除する 多くの場合originを指定するはず $

    git remote add origin [email protected]:appi-github/somerepository.git 削除後に改めてSSHで登録すればOK
  11. 15 37 main HEAD ブランチを切り替える $ git switch -c feature

    main HEAD feature 修正を適用する $ git stash pop ブランチを切り替えてから修正した状態になる
  12. 16 37 作業のまとめ 1. git stash で修正内容を退避 2. git switch

    -c branchname でブランチを作成して切り替え 3. git stash pop で修正内容をワーキングツリーに適用 git stashについて 複数の状態を退避可能(スタックになっている) 退避した状態はgit stash listで表示できる 指定した状態を適用するにはgit apply ただし、複数退避すると混乱しやすい 「退避は一つだけ」「退避したらすぐ適用」
  13. 17 37 $ git push To /URL/to/repository.git ! [rejected] main

    -> main (fetch first) error: failed to push some refs to '/URL/to/test.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. 家で作業して、ある程度まとまったので、プッシュしよう としたら拒否された あ、大学でプッシュした内容をフェッチし忘れた! ここに拒否の理由が書いてある
  14. 19 37 main リモートリポジトリ main origin/main ローカルリポジトリ main origin/main $

    git fetch origin/mainがリモートと同じ状態に まずフェッチする
  15. 20 37 main origin/main main origin/main ローカルリポジトリ $ git merge

    次にマージする ローカルに「リモートの歴史」が取り込まれた
  16. 21 37 main リモートリポジトリ main main origin/main main origin/main $

    git push 歴史を共有したのでプッシュできる
  17. 22 37 作業のまとめ 1. git fetchで最新の情報をローカルに持ってくる 2. git mergeで修正を取り込む(ここで歴史を共有) 3.

    git pushで修正をリモートに送る メッセージを読む プッシュに失敗すると、赤字でrejectedと言われ、いろいろ メッセージが出てきて焦るが、ヒントに「リモートにロー カルに存在しないコミットがあるからリジェクトされた よ」「まずリモートの修正を取り込んでからプッシュし ろ」と書いてある。
  18. 23 37 main HEAD Detached HEAD状態 HEADがブランチではなく、直接 コミットを指した状態 どんな状況でなるか? git

    checkoutで直接コミットを指定した←普通やらない git rebaseの最中に衝突した←対応後述 git bisectの実行中←git bisect resetで抜ける 上記以外で「よくわからないけど頭が取れた」時の対処
  19. 24 37 main HEAD なぜかわからないけど頭が取れた main HEAD $ git switch

    main ここでmainに戻ってしまうと、後でアクセス できなくなるコミットができてしまう
  20. 25 37 main HEAD $ git branch 20210918_detached_head 20210918_detached_head いまHEADが指しているコミットに

    適当な名前のブランチをつけておく $ git switch main main 20210918_detached_head mainブランチに戻る
  21. 26 37 作業のまとめ 1. git branchで適当な名前のブランチをつけておく 2. git switch mainでメインブランチに戻る

    3. 必要があればマージ、しばらく待って不要だと思えば ブランチを削除 普通にやっていればgit rebaseとgit bisect以外で 頭は取れないはず。上記はあくまで緊急回避。
  22. 27 37 $ git rebase main Auto-merging test.txt CONFLICT (content):

    Merge conflict in test.txt error: 99a8712を適用できませんでした... f2 Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort". Could not apply 99a8712... f2 リベースしようとしたら衝突が発生した test.txtで衝突が起きた 衝突を解消してからgit addしてgit rebase --continueしろ リベースを中止するなら git rebase --abortしろ
  23. 28 37 main branch m1 m2 m3 f1 f2 f3

    root main branch m1 m2 m3 f1’ f2’ f3’ root $ git rebase main HEAD HEAD まだこのブランチは ここにきていない ここで衝突
  24. 29 37 main branch m1 m2 m3 f1’ f2’ f3’

    root HEAD 次に作りたいコミットはここ このコミットの「あるべき姿」をインデックスに登録してコミット $ git add test.txt $ git commit -m “resolved conflict of test.txt” リベースを続行 $ git rebase --continue
  25. 30 37 作業のまとめ 1. git add により「次に作るべきコミット」をインデッ クスに登録する 2. git

    commitでコミット(merge実行時のマージコミット にあたるもの)を作る 3. git rebase --continueでリベース続行 4. 衝突するたびに1-3を繰り返す リベース中に衝突すると頭が取れるので焦りがち 「次に作るべきコミットはどういうものか」を考え、 それを作っていく
  26. 31 37 $ git blame ファイル名 指定したファイルの、どの行をいつ、誰が修正したかを表示する def func1(): print("Hello

    func1") def func2(): print("Hello func2") if __name__ == '__main__': print("Hello") func1() func2() 例えばこのPythonスクリプトの、それぞれ の関数をいつ、だれが書いたか知りたい
  27. 32 37 $ git blame test.py 56127fbb (H. Watanabe 2021-09-17

    21:22:49 +0900 1) def func1(): 56127fbb (H. Watanabe 2021-09-17 21:22:49 +0900 2) print("Hello func1") 56127fbb (H. Watanabe 2021-09-17 21:22:49 +0900 3) 56127fbb (H. Watanabe 2021-09-17 21:22:49 +0900 4) 26bdec20 (H. Watanabe 2021-09-17 21:23:31 +0900 5) def func2(): 26bdec20 (H. Watanabe 2021-09-17 21:23:31 +0900 6) print("Hello func2") 26bdec20 (H. Watanabe 2021-09-17 21:23:31 +0900 7) 26bdec20 (H. Watanabe 2021-09-17 21:23:31 +0900 8) ^fea5775 (H. Watanabe 2021-09-17 21:22:08 +0900 9) if __name__ == '__main__': ^fea5775 (H. Watanabe 2021-09-17 21:22:08 +0900 10) print("Hello") 26bdec20 (H. Watanabe 2021-09-17 21:23:31 +0900 11) func1() 26bdec20 (H. Watanabe 2021-09-17 21:23:31 +0900 12) func2() 個人開発では著者は全て自分だが「あれ?これ書 いたのいつだっけ?」と思うことがあるのでたま に便利(特にデバッグ時に)。
  28. 35 37 ここでバグ発見 ここは確実に大丈夫 e6348e408b57fdb42eb1281cb77b5c331cd400e7 is the first bad commit

    e6348e4 $ git branch bug e6348e4 $ git bisect reset main bug 見つけたコミットにブランチをつけてbisectを抜ける
  29. 36 37 作業のまとめ 1. 間違いなくバグっていないコミットを探す 2. git bisect startで二分探索開始 3.

    git bisect good/badで良いか悪いかGitに教える 4. Gitが見つけた「最初にバグが入ったコミット」にブ ランチを付けてgit bisectを抜ける 5. 「最初にバグが入ったコミット」とその前の差分を調 べる 補足 歴史に「中途半端なコミット」があると二分探索できない 合否判定を自動化できる(演習でやります)