Gitの迷いを解消!Beejさんによる超わかりやすいGit入門ガイド
引用元:https://news.ycombinator.com/item?id=42941283
みんなー、もし間違いを見つけたら、遠慮なく教えてね。直すから!よろしく、Beej
間違いじゃないんだけど、gitの話でvimが出てくるなら、commitとかoperationを中断させるために、:cqっていうステータスを返す方法も付け加える価値あるかも。
マジか!vimって30年以上使ってても新しい発見があるんだね。Vimのappendixに追加しとくよ!
それ、Gitも同じだよね
もし君がGitを30年以上使ってるなら、タイムマシンに連れてってくれ。Gitは今年で20周年だよ。
それは素晴らしい情報だ!今までcommitメッセージの行をコメントアウトして保存してたんだよね。まるで原始人だ…
有罪
有罪… 変わってない*
:q! をよく使うんだけど、同じことできるみたい。
ちょっとした違いだけど、:q! は保存せずに終了してexit codeはゼロを返すけど、:cq はnonzero exit codeで終了するんだ。Gitはnonzero exit codeを“編集失敗”と解釈する。Unixの慣例でゼロは成功って意味だからね。
もし作業中にcommitメッセージを保存しなかったら、:q! は空のテンプレートをGitに返すけど、Gitは賢いからcommitしない。
でも誤って作業の途中で保存しちゃったら、:q! は破棄したかったメッセージをcommitしちゃうよ。
それってedit bufferが空かコメントアウトされた行しかない時にしか使えないんだよね。例えばgit commit --amend
で前のcommit messageをbufferに読み込んだ時とかは、:q!じゃcommit cancelできないけど、:cqならできるよ。
:cqはshell loopでディレクトリtreeを比較して、違うファイルの中身をvimで確認したい時に便利だよ。よく使うんだよね。
Typing ^C to vimじゃどうにもならない時もあるし、もしloopがミスって1000個もファイルを返すような事態になったら:cqがないとマジで困る。:cqはbreakをtriggerしてloopを抜け出せるんだ。
こういうちょっとしたtipsマジで助かる。VIMは26年、gitは15年使ってるけど、cをつけるなんて知らなかった。:q!は:wしてない時はnon-zero status codeでexitしてほしいってずっと思ってたんだ。
へー、いつもmessageがないせいでfailureするようにbufferの中身全部消して:wqしてたわ。
覚えやすいようにmnemonic: cancel quit
まず、素晴らしいworkだって言わせて。こんなにcomprehensiveなresourceを作ってくれてありがとう。全部読んだわけじゃないんだけど、一つ気になったことがあったんだ。
Section 5.1(https://beej.us/guide/bggit/html/split/branches-and-fast-for…)で
>The default branch is called main.”
>The default branch used to be called master, and still is called that in some older repos.”って書いてあるけど、これって違うんじゃない?gitはまだmaster
がdefaultだけど、git config --global init.defaultBranch <name>
でdefaultを変えられるよ。
Hrm。知らなかった。init.defaultbranch
をunsetしたらmaster
を使うようになって、hintsが表示された。
>hint:’master’の代わりによく使われる名前は’main’、’trunk’、’development’です。作成されたbranchは次のコマンドでrenameできます。
これでfixを書くのがもっと”interesting”になるね。ありがとう!
ちょっとshout-outさせて; Lambda Schoolで先生に教えてもらった生徒の一人なんだけど、先生のinstructionはそこで過ごした時間の中でhighlightの一つだった。いつもありがとう!
どういたしまして! :)
teenの頃に先生のC programmingのguideを読んで、今日のfirmware devとしての自分があるのは先生のおかげです。本当に感謝しています。
それなー! それもそうだけど、git worktreesはマジでワークフローに欠かせないってことに触れてほしいな。知ってる人少なすぎ!stashで頭抱えなくてもブランチがごっちゃにならないって最高じゃん? worktreesはマジ使えるけど、C言語のガイド書いた後に、もうあんな大作は書かないって誓ったんだよねー(笑) コンテンツを1ページで提供してくれてマジ感謝! え、本物のBeej?ネットワーキングのガイド、めっちゃ好きで読みまくってたわ!コードで何ができるか、ネットの仕組みとか、マジで色々教えてもらった。子供の頃の最高の経験だった!ありがとう! スクリプトしか書けない子供の頃にあなたのネットワーキングガイドに出会って、C言語を始めるきっかけになったんだ。マジ感謝! Beejはマジでレジェンド!みんな大好きだよ! ガイドいつもありがとう!”15.7 Rebase時の複数コンフリクト”についてフィードバックさせて! ネットワークプログラミングガイド、大好きでした! 関係ないけど、子供の頃あなたのソケットのチュートリアルでプログラミングを学んだんだ。すごく分かりやすくて、高校から大学、今の仕事まで使ってるよ。 Beejさん、あなたのNetwork Programmingのガイドは、俺のUNIX駆け出し時代にめっちゃ役立ったんだよね。マジで多くの人に影響を与えて、大学のネットワークの授業で推薦図書になったくらいなんだから。今でも現役でガイド作ってんの、マジすごい! Beejさん、ありがとうございます! Hey Beej、マジすごい! Pro Gitも読んだけど、あんたのガイドはマジで良いね! 5.7節でさ、>But in this section we’re going to be talking about a specific kind of merge: the fast-forward. This occurs when the branch you’re merging from is a direct ancestor of the branch you’re merging into.”ってあるけど、”from”と”into”が逆じゃね?“main”が”into”で、“newbranch”が”from”で、“main”は“newbranch”の直接の祖先でしょ。 え、マジで言ってんの?これ、マジで大きな間違いだと思うんだけど。 間違いじゃないけど、HEADに関する素晴らしい説明の中で俺が気づかなかったのは、@はHEADのエイリアスで、入力するのがめっちゃ楽ってこと。 それが俺のガイドで一番読まれてないやつだと思う。Beej’s Guide to Killing Dragonsは除くけど。:) 10代の頃に「Beej’s Guide to Network Programming」とか「Beej’s Guide to Unix IPC」を読んだの覚えてるわ。めっちゃ分かりやすくて、しかも深みもあって最高だった。マジ影響受けたプログラマーの一人だよ。[0] https://beej.us/guide/bgnet/ [1] https://beej.us/guide/bggit/ 俺もだ!90年代半ばに10代だったんだけど、IRCdサーバーのコードとかボットにマジ感動したんだよね。Slackware Linux unleashed w/CD-ROMの中古本買って、C言語のネットワークコードのサンプルがあったんだ。Beej’s Networking siteは、そのコードが分からなくて見つけたんだよな。そこからめっちゃハマって深みにハマったわ。本屋巡りまくってプログラミングの本探したし。Richard Stevensのすごい参考書買ってからは振り返らなかったな。Beej、長年の情熱をありがとう! selectの使い方を学んでポートスキャナ(grabb’ Iだったかな?)を速くするために、Beejのネットワークガイドをイタリア語に翻訳したのを覚えてるわ。楽しかったな。 同じ人か確かめに来たんだけど、あの懐かしいウェブデザインで確信したわー。昔は各ページに個性があって、オフラインで読むためにページを保存しないと親父に電話代で怒られたんだよね! マジそれ!俺もほぼ同じだわ!(IPCガイドは読んでないけど) そうそう、何年も前にネットワークプログラミングを始めた頃、この素晴らしいガイドに助けられたんだ。 IPCのこと全然知らなかった!読まないと! >The Old Command:git checkout Git switchって結構新しい機能で、2019年に初めて登場したんだってさ。 >git switchなんて知らなかったし、git checkoutが古い代替手段だなんて考えたこともなかったよ。年を感じる。 switch/restoreは実験的って言っても、完全に安定してないからスクリプトとかに使うのはやめとけって意味合いだね(たまにはちゃんと使って安定させようって思うんだけど、結局やらないんだよね)。 UIを調整するために実験的になってたんだよね。必要なら後方互換性を壊せるように。でも、追加された後すぐにGitを離れたんだ。誰もこの作業を引き継いで完了させなかったみたいだね(実験的ステータスを削除するだけでもいいのに。何年も変わってないし、文句言う人もいないと思うし)。 git switchはブランチの切り替えに特化してるけど、git checkoutはもっと広い範囲をカバーしてるんだよね。 つまり、すでに 「この複雑さが理解できないから、存在すべきじゃない」ってことだね。 思うんだけど、みんなGitについて文句言う時間があるなら、その半分でも勉強すれば、30パート以上のガイドなんて誰も作らないと思うんだよね。manページに書いてあることばかりだし。 branchの作り方を知らないとか、gitの超基本的なことじゃん。作り方知らない(git checkout -bね)ってことは、git使ってないか、masterにpushしてるソロプロジェクト勢ってバレるだけだよ。 質問全部ググれば一発で出てくるじゃん(笑)昔は >質問全部ググれば一発で出てくるじゃん(笑)昔は >Stashes are more like commits — they even appear in the reflog! But they also aren’t real commits either, in the sense that you can’t check them out, or rebase them, or manipulate them directly. そもそも開発者が本番環境のcredentialsにアクセスできるのがおかしい。それが問題。 pre-commit hookが必要なんじゃない?チェックするために。 Pre commit hookは強制できないんだよね。みんながopt inする必要があるし、opt inする人はcommitする前にパスワードをチェックする人たちだよ。 Gitの説明で一番欠けてる部分は、DAGが1つじゃなくて複数あるってことだと思う。distributed version control systemってそういうこと。 またgitのスレかよ。結局、gitのデータ構造を理解すれば、gitの素晴らしさに気づけるって話でしょ。gitのUIがひどいのは明白じゃん。擁護する人たちは頭が良いんだろうけど、gitが簡単だって言うのは無理があるって。gitに苦労してる人はたくさんいるんだから。 その人はデータ構造じゃなくて、commit、branch、tagっていう基本的なインターフェースの話をしてるんだよ。それってgitに限らず、どんなバージョン管理でも学ぶ必要がある101レベルの内容じゃん。それを無視するのは、ハサミを刃の方で持ってるようなもんじゃない? まあ、そういうことだよね。とりあえずやってみればいいじゃん。commitする内容より難しくないって、マジで。比喩でごまかすのは意味ないよ。 gitを知らない人が多いのも驚きだけど、何年も、下手したら何十年も学ぼうとしない人がいるのはもっと驚きだわ。俺はそんなに頭良くないけど、インターン中にgitの問題を解決できたし。もう11年前の話だけどね。今じゃgitのことはよく分かってるし。もし何十年も経ってgitを学んでなかったら恥ずかしいと思うよ。 gitを学べない人に難しいことは任せられないって言うけど、それはプレッシャーの中で崩れる考え方だよ。集中力は有限だし、ルーチンワークが増えれば、本業に使える時間は減る。そんなことより、DelphiとかVBの時代には、CVSとかSVNを使えない人たちが、実際のアプリを作ってたんだよ。なぜなら簡単だったから。今はgitとかCSSとかframework-of-the-monthとかの知識を持ってるけど、db連携のメッセージボックスを作るのに一日かかるような人が多い。 >今はgitとかCSSとかframework-of-the-monthとかの知識を持ってるけど、db連携のメッセージボックスを作るのに一日かかるような人が多い。 まあ、半分同意で半分違うかな。gitのporcelainの部分は、95%のユーザーには十分だよ。 俺の経験では、必ず何かのエッジケースでgitの深いところにハマっちゃうんだよね。高度な機能を使わなくても、いつか変な状況に陥って、何かする必要が出てくるんだよ。gitに代わるものがないから、仕方なく使ってるけどね。 最近は変な状況に陥った記憶がないな。init、clone、fetch、checkout、branch、commit、rebase、remote、log、stash、cherry-pick、blame、configっていう基本的なコマンドしか使ってないし。 Gitを自動化せずに、rsyncの代わりに使ってるってことだよね?履歴を編集しないし、リモートリポジトリも複数扱わないし、モジュール化されたプロジェクトも扱わないし、自動化もしないって感じでしょ?たぶん、多くの人がそうやって使ってると思うよ。でも、自動化とかモジュール化されたリポジトリとか、履歴の書き換えが必要になった時に困るんだよね。中小企業だと、専門のインフラ担当がいないから、そういう問題が起きやすいんだよね。 「自動化されてない」ってどういう意味?詳しく教えてほしいな。小さいチームのリポジトリを管理することが多いんだけど、複数のリモートリポジトリを扱うことも多いけど、問題ないよ。submoduleは使わないようにしてる。履歴の編集は避けるようにしてるよ。当然だけどね。rsyncの代わりってわけじゃないよ。 >非自動化 テキストのバージョンを保存するツールとしてgitを標準化しちゃったのが問題なんだよね。gitの良さが失われてる。 君は若いのかもしれないけど、Gitは昔のツールよりずっと優れてるよ。Gitよりシンプルなものを作ろうとしたら、SVNやCVSみたいになっちゃうよ。あれらは複数人で同じファイルを編集するのが苦手だったし。もっとcontent awareなものを作ろうとしたら、Gitの名前の由来がわかると思うよ。 Mercurialの方がGitよりシンプルだよ。ただ、遅すぎるんだよね。 人によるんじゃない?Subversionしか使えなかった頃、GitとMercurialを試してみたら、Mercurialは混乱したけどGitはすぐに理解できたんだよね。理由は忘れちゃったけど、branchの扱い方が関係してたと思う。 Gitの苦痛の99%はCLIで使うせいだよ。TortoiseGitとかGUIツールを使えば、全部わかりやすいし、簡単になるよ。CLIでGitを学ぶのは時間の無駄だよ。 TortoiseGitユーザーがリポジトリを壊して、問題を理解できずにpushして、みんなの迷惑になるのを見たことがあるよ。Gitの言葉はめちゃくちゃだから、右クリックGUIじゃ直せないんだ。だって、意味のわからないラテン語みたいな象形文字をクリックしてるだけなんだもん。 TortoiseGitみたいなツールでリポジトリが”壊れる”なんてありえないと思うよ。それはGitを理解してないってことじゃない? 間違った操作をしてしまう可能性はあるけど、pushできて他の人の問題になるなら、それは彼らのせいじゃないよ。Gitを使わされてるんだから、Gitを理解してる人がいるはずだよ。もっとコメントを表示(1)
だから基礎を教えたら、あとは他のリソース使ってって感じにしたいんだ。
でも、worktreesはマジ便利だよね。基本の使い方だけならすぐ終わるし、ブランチ理解してる人ならすぐ使えるようになると思う。知らない人が多いってことは、追加する価値あるかもね。
考えとく!
長年のガイド、本当に貴重だよ。
今もこうして記事書いてるの、すごいね!
90年代の僕らの希望の光だった!
Rebase教える時、2つのワークフローを勧めてるんだ。
>Rebaseはコミットを新しいベースに1つずつ”リプレイ”するので、リプレイごとにコンフリクトが発生する可能性がある。
>だから、コンフリクトを1つずつ解決するのが役に立たない場合は、まずfork pointからsquash rebaseしてコミットを1つにまとめる。
>git rebase -i git merge-base main --fork-point
Squashしてから:
>git rebase -i main
あと、何度もrebaseしてると同じコンフリクトを何度も解決することになるよね?
”rerere”を使うとgitが”reuse recorded resolution”してくれるから、毎回手動で解決しなくて済むよ。
[alias]とか[rerere]の設定はこんな感じ。
あなたのチュートリアルがなかったら、今のキャリアを選んでなかったかも。愛と努力をありがとう!同じように影響を受けた人はたくさんいると思う。
他の多くの人と同じように、あなたのネットワークプログラミングガイドは、俺の教育とキャリアの初期の頃にめっちゃくちゃ役に立ちました。本当に感謝しかない!
図5.4で、2つのコミットを新しいものにマージすると、なぜか両方のブランチが新しいコミットを指すって言ってるけど、Git初心者マジで混乱すると思うよ。
anotherBranchをsomeBranchにマージして、formerはそのままにしとくって書いた方が良くない?次のマージも同じ感じで。ただの提案ね。
9.4で、reallinux/masterがマージ後にmasterと同じコミットを指すわけないじゃん。1コミット遅れた状態のままじゃん。
修正したし、すべての書籍のissueとPRをリストアップして見逃さないようにするスクリプトを書いたよ。もっとコメントを表示(2)
そしてコードが動いた時の喜び!人生の失敗と拒絶に対する最高の証明だった!コンピューター間でメッセージを送る喜びよ!
Beej、ありがとう。
git switchなんて知らなかったし、git checkoutが古い代替手段だなんて思ってもみなかった。年を感じるわ。
10年弱前にgitを使い始めたんだけど、今gitを学ぶ人がgit checkoutを使う理由が分からないって思うなんて、マジで言葉にできない。
本題に戻って、このガイドは俺が学んでた時にあったら超便利だっただろうな。めっちゃ分かりやすくて、よくあるFAQを網羅してるし。
初めてmerge conflictに遭遇して、それを中断して、conflictを回避するために回避策を講じたことをよく覚えてるわ。
2021年とごく最近の議論があるけど、後者ではgit switch
がまだ実験的だってドキュメントに書いてあるらしいよ。
https://news.ycombinator.com/item?id=28024972
https://news.ycombinator.com/item?id=42649858
いやいや、git checkout
が”古い代替手段”ってわけじゃないと思うよ。少なくともまだね。最後に確認したとき、switch
はまだ実験段階だったし、15年前にGitを使い始めたときに覚えたワークフローやコマンドから離れることは考えたことすらないんだよね。やりたいことは全部今までと全く同じようにできるし(git checkout
も以前と全く同じ動きをするし)、他のGitユーザーとも協力できるし、なんでワークフローを変える必要があるんだ?git checkout
を知ってて使ってる人は、特にswitchを使う必要はないってことだね(pun intended)。全部うまく動いてるし、近い将来deprecatedになることもないだろうし。
Commitsはツリーのスナップショット。先祖のリストを持ってて(普通は1つだけ)。Tagsは変わらないcommitへの名前付きポインタ。Branchesは変わるcommitへの名前付きポインタ。Indexはまだ作成途中のcommitのプロトタイプ。
これで全部。もっと知りたければ、ガイドを読むな。「ツリーに影響を与えずに特定のGit commitに切り替えるには?」「変更したファイルの一部だけをcommitするには?」「別の場所から現在のツリーにcommitをコピーするには?」をググれ。
基本はシンプルで簡単。やりたいことは複雑。基本を学んで、やりたいことをググれ。ガイドを読むな。
branchの切り替えは’git checkout’でもできるし、変更は’git stash’すればいいじゃん。
’git reset’でファイルをresetするのもいいけど、repoを”clean”にしたいだけならstashでしょ。
Stagedファイルをresetするのも’git reset’でしょ。
適当なcommitをcheckoutしてcommitしたい理由が分からんけど、’git reset –hard HEAD~x’で戻れるよ。’x’は戻りたいcommitの数ね。Hardはoptionalだけど、コメントからするとそうしたいんでしょ。
変更箇所によるよ。違う行を変更したらconflictしないよ。
これ全部、複数人で開発するなら知っておくべき基本だよ。
全部コマンドを暗記しようとするから苦労するんだよ。そんな必要ないのに文句ばっか。
>別に反対するわけじゃないけど、マジでgitクソって言いたいだけかも。
まさにそれ。
>めんどくさいって言いたいだけでしょ。
それおかしいって思うんだよね。だって、そもそも必要ないならやりたくないじゃん!
staging(index)ってcommitみたいなもんでしょ?変更のスナップショットって意味では。でもcommitみたいには使えなくて、特別なコマンドが必要じゃん。その特別なコマンドって、commitと比べてそんなに違うことしてる?そんなことないよね。
stashもcommitっぽいけど、reflogに出てくるのにcommitじゃない。checkoutとかrebaseとか直接操作できないじゃん。また特別なコマンドが必要になるけど、ただの匿名commitでしょ?
branchはcommitへのpointerってみんな知ってるけど、branchとcommitをcheckoutするのって何が違うの?片方は普通で、もう片方は警告が出てデータが消えそうに見えるの?reflogを知らないと損した気分になるじゃん。branchがpointerなら、自由に移動させてもよくない?pushはできるけど、別のcommitに移動させるのめっちゃ面倒じゃん。
tagはbranchみたいだけど、動かない。動くか動かないかって、そんなに重要な違いなの?Gitはbranchと2種類のtagが必要なの?
>Stashはcommitみたいなもんで、reflogにも出てくる!でもcommitじゃなくて、checkoutとかrebaseとか直接操作できないんだよね。”
いやいや、commitだよ。checkoutできるし(stash{N}
って書けば)。でも、stashを使うためのコマンドが多すぎるって点を強調してるんだと思う。
これって、Gitのもっとコメントを表示(3)
みんなcentralisedな考え方でGitを使い始めるから、repoとDAGが1つしかないと思っちゃうんだよね。自分のmaster branchが”the” master branchと同じだと思ってる。それじゃGitは上手くならないよ。
バージョン管理は難しい問題だし、エキスパート向けのツールは理解するのに時間がかかるのは当然じゃん。俺らってソフトウェアのエキスパートでしょ?gitを学べない人に、もっと難しいことを任せられないよ。
でも、最近は簡単にしろって言う人が多すぎる。ソフトウェアが酷いのも当然だよね。
>もしgitに問題がない人が欲しいなら、採用の幅を狭めて、高い給料を払う覚悟が必要だよ。採用プロセスを見直して、予算を確保するべき。
ソフトウェアが酷いのは当然だよね。
俺の経験とは真逆だな。gitは実用的なツールで、実用的なことをしたい人に人気がある。机上の空論を語る人は嫌う傾向にある。gitを使える人は、Rustの細かい仕様を暗記してる人よりも、開発者として成功する可能性が高いと思う。gitが好きな人は、物事を成し遂げるのが好きな人だ。rebase -i
にはコマンドの説明があるし、git log
の出力形式をカスタマイズする方法も調べれば出てくる。git plumbingは、ちょっと分かりにくいけど、porcelainコマンドではできないことができる。でも、それは一般的な使い方から外れてる場合が多い。
結局、gitは巨大だけど、ほとんどのdevにとっては、必要ない機能が多いってこと。
commitの構造を理解するのに1年くらいかかったし、footgun(push –force-with-leaseとか)を避けるのに数年かかったかな。複雑すぎるから、もっと良いのがあれば乗り換えたいけど、SVNに戻るのは絶対嫌だ。
例えば、CIのためにスクリプトでリポジトリを作成・管理することがあるんだけど、スクリプトでcheckout、commit、rebaseなどを自動化する必要があるんだよね。例えば、”git-rebase -i”を自動化するスクリプトを書いたことがあるよ(エディタをシミュレートして、gitが生成するファイルを解析してrebaseを設定したり)。
別の例としては、リポジトリの統計情報を自動で生成したり。これはbuild serverの状況を分析するために使ってたんだ。必要な数とか、負荷分散の方法とかね。
>rsync
多くのプログラマーはGitをappend-onlyなデータベースとして使ってるってこと。履歴の状態を気にしないし、履歴を活用することもない。ソースコードにアクセスできればそれでいいんだよね。でも、それだと自動化とかインフラ担当者が困るんだよ。履歴に特定の性質を持たせたいのに、無視されちゃうんだ。