Slide 1

Slide 1 text

1 37 Gitの使い方(応用編) 慶應義塾大学理工学部物理情報工学科 渡辺 物理情報工学ソフトウェア開発演習

Slide 2

Slide 2 text

2 37 Gitのトラブルシューティング Gitの便利機能 「しまった!」「あれ?」をなんとかする 毎日使うわけではないが、知っていると 便利な機能の紹介

Slide 3

Slide 3 text

3 37 $ git add test.txt $ git commit -m “updaets test.txt” スペルミスしたことにコミット後に気づいた 既に歴史に誤りが刻まれてしまった $ git log --oneline 8f7d4f8 (HEAD -> main) updaets test.txt 78efaf0 initial commit

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

5 37 initial commit 78efaf0 8f7d4f8 updaets test.txt initial commit 78efaf0 52304ef updates test.txt git commit --amendによりコミットハッシュが変わる →歴史が書き換わる プッシュしたブランチをリベースしてはならないのと同じ 理由で、プッシュしたブランチをamendしてはならない

Slide 6

Slide 6 text

6 37 $ git restore ファイル名 いろいろ修正したが、よくわからなくなったので 修正をなかったことにしたい 指定したファイルを、最新のコミットの状態まで戻す 注:git restoreはgit 2.23から導入された比較的新しいコマンド で、それまではgit checkoutやgit resetを使っていた。それぞれ 操作に失敗すると危険なので、git restoreを使うと良い(後述)。

Slide 7

Slide 7 text

7 37 間違えてステージングしてしまったファイルを取 り消したい $ git restore --staged ファイル名 インデックスに登録されたファイルを取り消す。インデック スは最新のコミットの状態に戻る。 git addで余計なファイルをステージングした時などに使う。 --stagedは-Sでも良い ワーキングツリーの修正を取り消すには--worktreeもしくは-W デフォルトでは-Wが暗黙に指定される

Slide 8

Slide 8 text

8 37 チェックアウトとは リポジトリからファイルを 取り出すこと リポジトリから指定したブランチのコミットを取り出して ワーキングツリーに展開する = ブランチを切り替える $ git checkout ブランチ名 $ git checkout ファイル名 インデックスから指定したファイルを取り出してワーキング ツリーに展開する = ステージングされていない修正を取りけす

Slide 9

Slide 9 text

9 37 git checkoutはコミットを指定してチェックアウトできる $ git checkout コミットハッシュ main HEAD 9b662ef $ git checkout 9b662ef main 9b662ef HEAD いわゆる「頭が取れた(Detached HEAD)」状態になる

Slide 10

Slide 10 text

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の用法が載っていることがあるので注意

Slide 11

Slide 11 text

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という名前のリモートリポジトリが存在 するよ」と言われて追加を拒否される

Slide 12

Slide 12 text

12 37 $ git remote remove origin 指定したリモートリポジトリを削除する 多くの場合originを指定するはず $ git remote add origin [email protected]:appi-github/somerepository.git 削除後に改めてSSHで登録すればOK

Slide 13

Slide 13 text

13 37 Gitでは原則としてmainブランチでは作業せず、 新しいブランチを作成してからそこで作業する カレントブランチがmainのまま、いろんなファ イルを修正して保存しちゃった。 にもかかわらず・・・ まだコミットしてなければ 対応可能

Slide 14

Slide 14 text

14 37 $ git stash 修正を保存し、ワーキングツリーを最新の コミットの状態に戻す 修正したファイル $ git stash 修正が退避 最新のコミットのスナッ プショットに戻る

Slide 15

Slide 15 text

15 37 main HEAD ブランチを切り替える $ git switch -c feature main HEAD feature 修正を適用する $ git stash pop ブランチを切り替えてから修正した状態になる

Slide 16

Slide 16 text

16 37 作業のまとめ 1. git stash で修正内容を退避 2. git switch -c branchname でブランチを作成して切り替え 3. git stash pop で修正内容をワーキングツリーに適用 git stashについて 複数の状態を退避可能(スタックになっている) 退避した状態はgit stash listで表示できる 指定した状態を適用するにはgit apply ただし、複数退避すると混乱しやすい 「退避は一つだけ」「退避したらすぐ適用」

Slide 17

Slide 17 text

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. 家で作業して、ある程度まとまったので、プッシュしよう としたら拒否された あ、大学でプッシュした内容をフェッチし忘れた! ここに拒否の理由が書いてある

Slide 18

Slide 18 text

18 37 main リモートリポジトリ main origin/main ローカルリポジトリ 歴史を共有していないので プッシュできない

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

21 37 main リモートリポジトリ main main origin/main main origin/main $ git push 歴史を共有したのでプッシュできる

Slide 22

Slide 22 text

22 37 作業のまとめ 1. git fetchで最新の情報をローカルに持ってくる 2. git mergeで修正を取り込む(ここで歴史を共有) 3. git pushで修正をリモートに送る メッセージを読む プッシュに失敗すると、赤字でrejectedと言われ、いろいろ メッセージが出てきて焦るが、ヒントに「リモートにロー カルに存在しないコミットがあるからリジェクトされた よ」「まずリモートの修正を取り込んでからプッシュし ろ」と書いてある。

Slide 23

Slide 23 text

23 37 main HEAD Detached HEAD状態 HEADがブランチではなく、直接 コミットを指した状態 どんな状況でなるか? git checkoutで直接コミットを指定した←普通やらない git rebaseの最中に衝突した←対応後述 git bisectの実行中←git bisect resetで抜ける 上記以外で「よくわからないけど頭が取れた」時の対処

Slide 24

Slide 24 text

24 37 main HEAD なぜかわからないけど頭が取れた main HEAD $ git switch main ここでmainに戻ってしまうと、後でアクセス できなくなるコミットができてしまう

Slide 25

Slide 25 text

25 37 main HEAD $ git branch 20210918_detached_head 20210918_detached_head いまHEADが指しているコミットに 適当な名前のブランチをつけておく $ git switch main main 20210918_detached_head mainブランチに戻る

Slide 26

Slide 26 text

26 37 作業のまとめ 1. git branchで適当な名前のブランチをつけておく 2. git switch mainでメインブランチに戻る 3. 必要があればマージ、しばらく待って不要だと思えば ブランチを削除 普通にやっていればgit rebaseとgit bisect以外で 頭は取れないはず。上記はあくまで緊急回避。

Slide 27

Slide 27 text

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 ", 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しろ

Slide 28

Slide 28 text

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 まだこのブランチは ここにきていない ここで衝突

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

30 37 作業のまとめ 1. git add により「次に作るべきコミット」をインデッ クスに登録する 2. git commitでコミット(merge実行時のマージコミット にあたるもの)を作る 3. git rebase --continueでリベース続行 4. 衝突するたびに1-3を繰り返す リベース中に衝突すると頭が取れるので焦りがち 「次に作るべきコミットはどういうものか」を考え、 それを作っていく

Slide 31

Slide 31 text

31 37 $ git blame ファイル名 指定したファイルの、どの行をいつ、誰が修正したかを表示する def func1(): print("Hello func1") def func2(): print("Hello func2") if __name__ == '__main__': print("Hello") func1() func2() 例えばこのPythonスクリプトの、それぞれ の関数をいつ、だれが書いたか知りたい

Slide 32

Slide 32 text

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() 個人開発では著者は全て自分だが「あれ?これ書 いたのいつだっけ?」と思うことがあるのでたま に便利(特にデバッグ時に)。

Slide 33

Slide 33 text

33 37 ここでバグ発見 ここは確実に大丈夫 このどこかに「初めてバグが入ったコミット」があるはず

Slide 34

Slide 34 text

34 37 $ git bisect start 問題のある場所 問題のない場所 二分探索開始。Gitが候補となるコミットを次々に持っ てくるので、そのたびに問題があるかないかを教える。 ? git bisect good git bisect bad

Slide 35

Slide 35 text

35 37 ここでバグ発見 ここは確実に大丈夫 e6348e408b57fdb42eb1281cb77b5c331cd400e7 is the first bad commit e6348e4 $ git branch bug e6348e4 $ git bisect reset main bug 見つけたコミットにブランチをつけてbisectを抜ける

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

37 37 Gitのトラブルシューティング 慣れていないと、トラブルが起きた時に焦りがち トラブルの解消のためには、ある程度Gitの知識が必要 Gitが出すメッセージをきちんと読む Gitの便利機能 Gitには「知ってると便利」な機能が多数ある コマンド名を覚えておく必要はないが、「そういう ことができる」ということだけ知っておくと良い