6 ポイント 投稿者 GN⁺ 2023-11-03 | 1件のコメント | WhatsAppで共有
  • Gitは長く使っていても混乱しやすい用語の重複と再利用が多く、コマンドを覚えるだけでは実際の動作を予測しにくい
  • HEADheads、detached HEAD、ours/theirs../...のように、同じ単語や記号が文脈によって異なる意味に変わる
  • indexstagedcachedはいずれも.git/indexに関連するが、resetrevertrestoreは似た名前とは裏腹に、コミット履歴・ワークツリー・ブランチポインタへ与える影響が異なる
  • mainがリモートをtrackすることと、origin/mainのようなremote-tracking branchは別物であり、checkoutはブランチの切り替えとファイルの復元を1つのコマンドにまとめている
  • Gitを理解するには、個々のコマンドよりも、ブランチ、参照、コミット、インデックス、リモート追跡状態が噛み合う内部モデルをあわせて見る必要がある

HEAD、ブランチ、detached HEAD

  • Gitにおけるheadsはブランチを指し、内部的にはブランチは.git/refs/headsディレクトリに保存される
    • 公式Git用語集ではbranchをその上にあるすべてのコミット、headを最新コミットとして区別しているが、同じ対象を見る2つの方法と捉えられる
  • HEADは現在のブランチを指し、.git/HEADに保存される
    • headはブランチで、HEADは現在のブランチ」という表現自体が、Git用語の混乱をよく示している
  • detached HEAD stateは、HEADがブランチではなくコミットIDを直接指している状態
    • タグをcheckoutすると、タグはブランチではないためdetached HEAD状態になることがある
    • この状態ではgit pullは動作せず、git pushも特別な方法でない限り動作しない
    • git commitgit mergegit rebasegit cherry-pickは可能だが、新しいコミットがどのブランチにもつながらず、見つけにくくなることがある
    • 新しいブランチを作るか既存ブランチへ切り替えると、detached HEAD状態から抜けられる

mergeとrebaseで変わるourstheirs

  • コンフリクト解決でgit checkout --ours file.txtは「ours」側のファイルを選ぶが、mergeとrebaseの基準点は互いに異なる
  • mergeでは現在のブランチがours、マージされて入ってくるブランチがtheirs
    • 例: git checkout merge-into-oursの後にgit merge from-theirsを実行すると、現在のブランチがoursになる
  • rebaseでは現在のブランチがtheirs、rebase対象ブランチがours
    • 例: git checkout theirsの後にgit rebase oursを実行すると、対象ブランチがoursになる
  • この違いは、git rebase mainが内部的には現在ブランチのコミット群をmainブランチのコピーへ繰り返しマージする方式と関係している
  • ourstheirsは、ourstheirsという用語を短く説明している
    • VSCodeの「current change」と「incoming change」も同じように混乱しやすい

リモート状態メッセージとfast-forward

  • Your branch is up to date with 'origin/main'は、現在のmainがリモートの最新状態であるという意味ではない
    • 最後にgit fetchまたはgit pullした時点のorigin/mainとローカルブランチが同じ、という意味
    • 最後のfetchが5日前なら、「5日前の時点で」最新状態ということになる
  • Gitはreflogに直近のfetch時点の情報を保存するが、状態メッセージには「最後のfetch基準」の時刻は表示されない
  • can be fast-forwardedは、リモートブランチがローカルブランチより先に進んでおり、ローカルに別コミットがないためポインタだけを前へ進められるという意味
    • 例: mainA-B-Corigin/mainA-B-C-D-Eなら、git pull後はどちらもA-B-C-D-Eになる
    • この場合、マージコンフリクトの可能性はない
  • fast-forwardできない場合は、両方のブランチが分岐している状態
    • 例: mainorigin/mainにはないXコミットがあり、origin/mainにはmainにないDEがあるなら、fast-forwardはできない
    • このときGitは、2つのブランチがdiverged状態で、それぞれ異なる数のコミットを持つと表示する

コミット選択構文: ^~.....

  • HEAD^HEAD~はいずれも1つ前のコミットを指す
    • HEAD^^^HEAD~~~HEAD~3はいずれも3つ前のコミットを指す
  • merge commitのように親が複数あるコミットでは、HEAD^2HEAD^3の意味が変わる
    • HEAD^は1番目の親
    • HEAD^2は2番目の親
    • HEAD^3は3番目の親
    • HEAD~3は親の親の親を意味するため、HEAD^3とは異なる
  • git log main..testgit log main...testも互いに異なる動作をする
    • main..testtestにだけあるコミットを表示する
    • test..mainmainにだけあるコミットを表示する
    • main...testは両側のどちらか一方にだけあるコミットをまとめて表示する
  • git diffでは、.....git logとは異なって解釈される
    • git diff test..maintest側の変更とmain側の変更の両方を含めて比較する
    • git diff test...mainは共通祖先とmain側の先端を比較し、片側のdiffだけを表示する
  • ドット表記の混乱についてはpain in dotsでも追加で扱っている

reference、refspec、tree-ish

  • Gitでreferenceは複数の意味で使われる
    • mainv0.2のようなブランチやタグ
    • 現在のブランチを指すHEAD
    • HEAD^^^のようにコミットIDとして解釈される表現
  • Git文書ではHEAD^^^のような表現を厳密には「revision parameters」と呼ぶが、日常的にはreferenceのように扱われることがある
  • symbolic referenceは、HEADのように別の参照を指す参照を意味する
    • HEADはGitの中核コマンドの動作に大きく影響する中心概念
  • .git/configのremote設定にはrefspecが入る
    • 例: +refs/heads/main:refs/remotes/origin/main
    • デフォルトのgit clonegit remote addが作る値をそのまま使う場合が多い
  • tree-ishは、git checkout THING .THINGになり得る対象を指す
    • コミットID
    • mainHEAD^^v0.3.2のようなコミット参照
    • main:./docsのようなコミット内のサブディレクトリ
    • 実務上は「コミットまたはコミット参照」程度に理解してよい場合が多い

index、staged、cached

  • indexstagedcachedはいずれも**.git/index**に関連する
    • .git/indexは、git addで変更をstageするときに使われるファイル
  • 同じ対象を指す表現が複数のコマンドに散らばっている
    • git diff --cached
    • git rm --cached
    • git diff --staged
    • .git/index
  • ただし、--index--cachedフラグが常に同じ意味とは限らない
    • GitのリードメンテナーであるJunio Hamanoの記事が細かな違いを説明している
  • indexは性能上の理由でuntracked filesも列挙するが、一般にstaging areaがuntracked filesを含むとは見なされない

resetrevertrestore

  • resetrevertrestoreは似た単語だが、Gitで影響を与える対象は異なる
  • git revert COMMITは、現在のブランチにCOMMITの反対の処理を行う新しいコミットを作る
    • COMMITが3行を追加していたなら、新しいコミットはその3行を削除する
  • git reset --hard COMMITは、現在のブランチをCOMMIT時点へ強制的に戻す
    • COMMIT以降の新しい変更を消す、非常に危険な操作
  • git restore --source=COMMIT PATHは、PATHのファイル群をCOMMIT当時の状態へ戻す
    • 他のファイルやコミット履歴は変更しない
  • git reset --hardgit restore .は単独で使うと概ね似た動作をするが、git reset --hard COMMITgit restore --source COMMIT .はまったく異なる動作をする

track、remote-tracking branch、untracked files

  • Gitはtrackという単語を3通りに使う
    • git statusUntracked files:は、Gitが管理しておらずコミットに含まれないファイルを意味する
    • origin/mainのようなremote-tracking branchは、最後にgit pullまたはgit fetchした時点でリモートoriginmainが指していたコミットIDを持つローカル参照
    • “branch foo set up to track remote branch bar from origin”のように、ローカルブランチがリモートブランチをtrackするよう設定されることがある
  • mainがリモートをtrackするブランチだという話と、origin/mainがremote-tracking branchだという話は別物
  • mainは実際のブランチなので、コミットを作ったりmergeしたりできる
    • .git/configでリモートmainをtrackするよう設定されると、git pullgit pushを便利に使える
  • origin/mainは「remote-tracking branch」という名前でも通常のブランチではない
    • 直接コミットすることはできない
    • git pullまたはgit fetchでリモートの最新main状態を取得するときだけ更新される

checkout、reflog、merge/rebase/cherry-pick

  • git checkoutは互いに異なる2つの作業を1つのコマンドに含んでいる
    • git checkout BRANCHはブランチを切り替える
    • git checkout file.txtfile.txtのunstaged変更を捨てる
  • Gitはこの混乱を減らすため機能をgit switchgit restoreに分けたが、既存のcheckoutも引き続き使える
  • reflogreference logであり、referenceが過去に何を指していたかの記録
    • referenceはブランチ、タグ、HEADを含む包括的な用語
    • reflogは、重要なブランチを誤って削除した場合のような非常にまずいGit状況から抜け出す助けになることがある
  • merge、rebase、cherry-pickはコミットを統合する方式が異なる
    • mergeは2つのブランチを統合する新しいコミットを1つ作る
    • rebaseは現在ブランチのコミットを対象ブランチの上へ1つずつコピーする
    • cherry-pickはrebaseに似ているが構文が異なり、現在ブランチへコミットをコピーする
  • git rebase --onto main otherbranch mybranchは、mybranchの一部コミットだけをmain上へ移す形で使える
    • 例示構造でFGだけをmain上へrebaseしたいときに使える
    • 3つのブランチ名が入るため、覚えにくい構文

commitとその他の紛らわしい用語

  • commitはGitで動詞としても名詞としても使われる
    • 動詞: 「頻繁にcommitせよ」
    • 名詞: mainの最新commit
  • Git commitは3つの方法で見ることができる
    • すべてのファイルの現在状態のスナップショット
    • 親コミットとの差分であるdiff
    • それ以前のすべてのコミットのhistory
  • コマンドごとにcommitの扱い方は異なる
    • git showはcommitをdiffのように扱う
    • git logはcommitをhistoryのように扱う
    • git restoreはcommitをsnapshotのように扱う
  • blobtreeoriginupstreamdownstreamfetchpullの関係、stashworktreesubtreesubmoduleも紛らわしいGit用語に含まれる
  • GitHub用語では、「pull request」、「squash and merge」、「rebase and merge」が混乱しやすい例に挙げられる

繰り返される混乱の3つの軸

  • 特に紛らわしいGit用語は3つに圧縮できる
    • headはブランチで、HEADは現在のブランチ
    • “remote tracking branch”と“branch that tracks a remote”は別物
    • indexstagedcachedはいずれも同じ対象を指す
  • 2012年のthe most confusing git terminologyも、Git用語がCVSやSubversionの用語とどうつながるかを扱っている
  • Git用語の混乱は一部の特殊機能ではなく、中核機能全般で繰り返される
  • 特にブランチを論じるとき、「tracking」が複数の方法で使われるため、初見では違いに気づきにくい
  • 一部の説明にはGitのまれな隅の機能が含まれており、誤りがあるかもしれないという但し書きが残されている

1件のコメント

 
GN⁺ 2023-11-03
Hacker Newsのコメント
  • これらの用語はほとんど覚えにくかったが、すべてが腑に落ちるようになった鍵は、Gitの多くのものが結局はコミットを指すポインタだという点だった
    ブランチ名、HEAD、タグはすべてポインタである。HEADは今見ているコミットを指し、my-featureのようなブランチ名はそのブランチの最新コミットを指す。maingit checkout -b my-featureを実行すると、mainmy-featureHEADがすべてmainの最新コミットを指すようになる
    my-branchでコミットするたびに、HEADmy-branchは新しいコミットを指すように移動する。detached HEADとは、HEADが指すコミットをどのブランチも指していない状態を意味する。タグとブランチの違いは、タグは特定のコミットを指して動かない点である
    もう一つ混乱したのは、多くのコマンドが一貫していないように見える理由が、Gitがデフォルト引数を仮定するためだということだ。git checkout file.txtgit checkout HEAD -- file.txtと同じで、my-branchにいるときのgit rebase maingit rebase main my-branchと同じである。ただし後者の形式は別のブランチからでも実行できる
    最後に、すべてがこじれたとき、どんな変な状態からでも抜け出す助けになるコマンドはgit reflogである。HEADが指していたすべてのコミットを表示してくれる。それでもGitがこうした混乱を認め、より意外性が少なく、より単純なインターフェースのコマンドを作っているのは喜ばしい
    • Gitがこの概念に付けた名前を使うなら、「コミットポインタ」はrefsである。だからreflogという名前になっている
      また、Gitを使う多くの人があまり思い浮かべない点として、取得してきたリモートのブランチやタグのrefもすべてrefなので、ref名を使える場所ならどこでも参照できるということがある
      例えば「このブランチに何があろうと構わない。fast-forwardもマージもrebaseもせず、ただローカルブランチをリモートの内容で上書きせよ」と言いたいなら、こうすればよい: git checkout foo, git reset --hard origin/foo
    • 正確には、HEADポインタがコミットを直接指すのではなく、通常はmy-branchのようなブランチポインタを指し、そのブランチポインタがコミットを指す。detached HEAD状態は例外である
      my-branchでコミットするとき、HEADmy-branchの両方が新しいコミットへ移動するのではなく、HEADは引き続きmy-branchポインタを指し、my-branchだけが新しいコミットを指すように移動する。もちろんHEADをたどってmy-branchを経由しコミットへ行けば、新しいコミットに到達する
      detached HEADとは、HEADがブランチポインタではなくコミットを直接指している状態である。detached HEAD状態でも、HEADとブランチポインタの両方が最新コミットを指すことはある。git log --decorateを見ると、通常の(HEAD -> my-branch)の代わりに、最新コミットに(HEAD, my-branch)のように表示される
    • この説明はよい。ただ、時々ぶつかるさらに紛らわしい部分は、タグには2種類あるという点である。ほとんどのタグは述べられているように、コミットオブジェクトを指す軽量タグであり、それ以上のものではない
      一方、注釈付きタグはメッセージ、タイムスタンプ、SHAなどを含められる。これらはれっきとしたGitオブジェクトで、コミットオブジェクトとかなり似た振る舞いをするが、通常はやはり別のGitオブジェクト、多くの場合コミットを参照する
    • 私のように過度に慎重なタイプなら、衝突の多い厄介な手順や複数の移動を始める前に、git rev-parse HEADを実行して、その文字列を安全な場所にコピーしておくのもよい
      最後に正常だと確信していた状態を正確に印しておいたという安心感がある。混沌の中へ何段階か進んだ地点ではなく、最後の正常な状態へたいてい戻れる。もちろんgit gcgit filter-branchのようなものを不用意に使っていないことが前提である
    • この説明は、Git内部のツリー構造がすべてを支配していることもよく示している
      冗談のようで冗談ではないが、Gitを使うには本当にコンピュータサイエンスの学位が必要だという話は間違ってはいない。Gitのデフォルト引数は非常に一貫性のないユーザー体験を生むという点には同意する
      git checkout .git reset --hard/softgit cleanupはいずれもものすごく似て見えるが、実際には非常に異なる。それでもPerforceやSVNよりはやはりGitのほうがよい。昔はMercurialのほうを少し好んでいたが、今でもそうかは分からない
      git reflogこそ王であり、真実を示してくれる
  • Gitがまた一つの根拠のない技術的流行のおかげでバージョン管理戦争に「勝利」してから10年ほど経つが、今でも些細であるべき作業で苦労している
    Linus Torvaldsが、人々がより親切なレイヤーを重ねずにGitを直接使い始めたことに驚いた、という文章を読んだ記憶がある。業界に自己反省ができるなら、Gitを選んだのは間違いだったと認め、別のバージョン管理システムへ移行していただろう。テキスト保存がすべてのツールに膨大な時間を浪費せずに済んだはずだ
    • Gitをテキスト保存ツールとは見ていない。複数人が同じ「テキスト」に加える変更を、かなり精密かつ安定して調整しながら、誰の進行も妨げないツールである。Wordで一度やってみれば違いは大きい
      もちろん、もっと良くはなり得る。それでもGitが受ける憎悪にはいつも驚かされる。Gitはすごいツールであり、非開発者向けに作られたWordやGoogle Docsのようなツールよりはるかに先を行っている
      私がSubversionが王だった時代を覚えているほど年を取っているからかもしれない
    • 強く同意する
      毎週、同僚たちがGitで混乱しているのを見て、オンラインで助けを求める投稿を見て、ここでの記事のように単純であるべきことを改めて説明しようとする文章をまた見る
      コーディングしている間に、SVN、TFVC、Perforceなどで自分が作っためちゃくちゃな状態を元に戻すために何時間も無駄にしている人は思い出せない
      ツールは人生を楽にするために存在する。それができないなら、時間をかける価値はない
    • 2013年の時点でも、Gitが勝つかどうかはまったく明確ではなかった。darcs、Mercurial、svn、さらにはcvsのユーザーもかなり多く、当時のツールはたいていそれらすべてに対応するプラグインを備えていた

いまでも、そういう多様性があってほしいと思う。Git の単一文化は、Git を置き換えようとするものがまず Git を再実装しなければならない、という意味でもある。だから ASCII や Scroll Lock キーのように、ほぼ永遠に Git に縛られることになる

  • Git は正当な理由があって勝ったし、それ以前のものより明らかに優れていた。今では jQuery のように Git をけなすのが流行かもしれないが、登場当時は明確な改善だった
  • 似た問題、つまり私たちがなぜ Git に閉じ込められうるのかについて、最近記事を書いた
    核心的な懸念は GitHub があまりに優れているため、GitHub が Git を文字管理エンジンから分離し、他のエンジンも許可しない限り、私たちは Git に縛られ続けるという点
    https://trolololo.xyz/github
    https://news.ycombinator.com/item?id=38098109
  • Git の用語は、多くが動作を理解したあとなら筋が通るが、実装の詳細を知らなかったり、他の用語と並べて見たりすると、ほとんど意味をなさない臨床例のようなもの
    漏れのある抽象化のような用語
    • 部分的には正しいが、Git の別々の部分を別々の人が作り、それぞれ別に名前を付けたからでもある。Linus はもともとこれを cache と呼んでいて、最もコンピュータサイエンス的な用語だった
      その後 Junio が、分散バージョン管理システムにより特化した index に変えたようで、多くのユーザーはこれを staging area と呼んだ。いまではこの用語の進化が Git の UI と内部に化石のように残っている
    • 筋が通っているのではなく、単に雑な命名だ。よい名前を付けるには努力が必要だが、Git の初期開発者たちはそれをしなかった
    • 基本的な利用のために OS、IDE、コンパイラの内部を知らなければならないと想像してみると、実質的に悪い API 設計
  • “Your branch is up to date with origin/master” は、毎日聞く最大の嘘だ。だから毎回ただ pull している
    • 厳密には正しい。origin/master は origin にある master ではなく、最後に把握していた origin/master が指していたコミットを意味する。この値は fetch すると更新され、pull は自動で fetch する
      ls -l .git/refs/remotes/origin/master を見れば、origin/master が自分のシステム上にあるファイルにすぎないことが分かる。いつ変わったかも見られる。魔法のように更新されるわけではない
      git fetch origin を実行して変更があれば、タイムスタンプと内容が変わる。cat .git/refs/remotes/origin/master で確認できる
      Git の基礎は非常に単純で、中心的なデータ構造といくつかの操作は 1 日で実装できる。これを知っておくのは本当に価値がある。どういうわけか Git は、かなり単純な中核の上に非常に複雑なユーザーインターフェースを作り上げてしまった
    • そのメッセージは、Git が明示的に指示されない限り、インターネットやリモートリポジトリに接続して更新を確認しない、という意味にすぎない。この性質自体は望ましいと思う。ただしメッセージはもっと明確にできる
    • “Your branch is up to date with the local copy of origin/master, last updated on XXXX-XX-XX:XX:XX:XX” くらいであるべきだった
    • 私はローカルの master ブランチを消してしまう。たいてい master には push できないし、そもそも master で直接作業したくない。なら、ローカルに変更可能な master ブランチを置いておく理由がない
      だから origin/master だけ更新すればよく、それには fetch で十分だ
    • git fetch && git pull
      3 年後に引退して、この壊れたゴミから足を洗える日を待っている
  • Git が出たほぼ初期から使ってきたのに、今週になってようやく porcelain を知った。git status の出力をパースするプロジェクトがあるのだが、--porcelain フラグを付けると本当に役立った。より簡潔で、プログラムからパースしやすい出力を生成してくれる
    どれほど多くのコマンドに、より機械可読な出力があるのか気になり、git-scm の “Git Internals - Plumbing and Porcelain” ページまで見ることになった。要約すると、Git は最初から洗練されたバージョン管理システムというより、バージョン管理を扱うツール群として書かれていた
    初期から Git を使っていた多くの人は、この低レベルコマンドでバージョン管理作業を学び、そのワークフローを他の人にも広めた。これが plumbing 層だ。その後 Git はより磨かれた層を作り、それを porcelain と呼んでいる
    まだどのコマンドがどちらの層に属するのかは完全には分かっていないが、ここ数年で推奨されるようになった新しいワークフローを理解する助けになった。自分のワークフローを変えるときにも、よりよく判断できるようになった
    https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Po...
    • Linus が内部構造である plumbing には満足していたが、UI である porcelain には手を入れる必要があると考えていたことの明確なサインだ。もともと porcelain はすべて plumbing を呼び出すスクリプトだった
      残念ながら、高速化のためにその UI をハードコードしたが、改善はしなかった
  • Julia の文章の声が本当に良い。ベテラン開発者にとっても非常に価値のある内容を書きながら、この分野に入ったばかりの人にも親しみやすく、排除しないトーンを保っている
  • 「Git ストックホルム症候群」にかかっているようだ。これらの用語はそこまで混乱するものではないが、他のシステムをほとんど学ばず、2011 年ごろから Git に深く縛られていたことが大きいのだと思う
    振り返ると、始めた当初はものすごく混乱していたはずなのに、格好つけるためにそうではないふりをしていた気がする
    • Fossil バージョン管理システムの CLI を見て、自分にストックホルム症候群があると分かった。あまりに直感的だった
    • Git を使う前は cvs と svn を使っていたが、svn と比べると Git のほうがずっと理解しやすかった
    • 私が使ったことのある他のバージョン管理は Plastic SCM だけだが、ストックホルム症候群かどうかに関係なく Git のほうがずっと良い
  • あわせて見るとよいもの: Git staging area は文字どおり誰もが同意している用語だ

https://news.ycombinator.com/item?id=28143078
“Your branch is up to date with ‘origin/main’” は、実は少し誤解を招く表現です。main ブランチが最新だという意味に受け取れますが、実際にはそうではありません。
ただし、ここにあまり「厳密に言えば」を付け加える必要はありません。二人の将軍問題が当てはまるのではないでしょうか。本当に細かく突き詰めると、まさにこの瞬間に「最新」かどうかは分かりません。status の前に fetch しても同じです。
では、合理的な期待値は何でしょうか。自分のコンピュータ上のオブジェクトデータベース内にある ref が、最後に確認した時点で、遠くにある別のオブジェクトデータベースの ref と同じだった、という程度です。
単純な status 呼び出しがネットワーク越しの fetch をしたり、今使っているツールの基本原理を毎回思い出させたりすることはできません。個人的にはそう思います。ところが驚くことに、StackOverflow の回答に付いた多くのコメントは、まさにその両方が必要だと考えています。
“is up to date with the origin’s main as of your last fetch 5 days ago” のような文言なら、もう少し合理的です。最後に fetch してからどれくらい経ったかだけを知らせるものだからです。
[1] https://stackoverflow.com/questions/27828404/why-does-git-st...
もう一つ。ORIG_HEADFETCH_HEAD のように「origin の head」と関係があるのだと思っていました。しかし違いました。この「pseudoref」は rebase のような、より複雑な書き換えの前に保存地点として使われるもののようで、reflog ができる前に実装されたものです。おそらく “original head” という意味なのでしょう。

  • Git にはかなり好意的ですが、あのメッセージの文言については筆者に同意します。ただし、提案されている代案には同意しません。

    “Your branch is up to date with ‘origin/main’” は技術的には正しいものの、up to date という表現はざっと読むと二つのことを示唆します。mainorigin/main と同じであり、origin/main も最新である、つまりコマンド中に更新されたか、自動的に最新状態に保たれていて直近の同期に成功している、という印象です。

    これは CPU の 1 サイクルの間だけ真でなければならない機械判読用の信号ではなく、ユーザーに見える状態メッセージです。ネットワーク理論まで持ち出さなくても、そのような解釈は合理的です。

    “Up to date” は、空間的というより時間的に追いついているという意味に近いです。ローカルブランチの位置は「このブランチがどこを指しているか」という空間に近く、リモート ref の状態は「自分が最後にリモート ref をいつ更新したか」という時間に近いものです。もちろん非常に主観的で、どちらも両方の意味になり得ます。

    より良い文言としては、“Your branch matches origin/main”、“Your branch’s head is the same as origin/main”、“Your branch is pointing to the same commit as origin/main” のようなものがあり得ます。明確さと冗長さの折衷です。ここに筆者の提案のように “remote ref last updated 5 days ago” を括弧付きで加えることもできます。

  • status 呼び出しがネットワーク越しに fetch するのはうっとうしい、という意見には同意しません。私は、ネットワーク操作は明示的であるべきで、暗黙的であってはならないと思います。git status を実行するときは、ファイルシステム上にある自分のリポジトリの状態を知りたいのです。

    他のリモートで何が起きているかを知りたければ、自分で fetch してから比較すればよいです。

  • 期待するのは、status コマンドを開始した時点を基準に最新の情報を得ることです。情報をターミナルに出力している間に競合状態が起きる可能性はありますが、ごく小さな時間枠です。

    概して、これは fetch を望んでいるという意味です。二人の将軍問題はリモート側にだけ適用されます。リモートは、クライアントがデータを受け取ったという確認を受け取れなければ、しばらく再送を続けることができます。それはクライアントの問題ではありません。

    クライアントがサーバーからデータを受け取れなければ、正しい動作は停止したままタイムアウト後にエラーを出力することでしょうし、現在の fetch はおそらくそうするはずです。

  • Git を学ぶ上で最も価値のある段階は、正しいメンタルモデルを持つことです。すべてはグラフです。
    このツールはよく共有されていますが、ここにもよく合います。Git の内部モデルとさまざまなコマンドの効果を視覚化してくれます。
    https://learngitbranching.js.org/
    このツールを 10 分使って学んだことは、それ以前のおよそ 10 年の Git 経験より多かったです。強くおすすめします。

  • これ以上付け加えたくはありませんが、Bitbucket の人たちが pull request という用語を作ったのも問題です。
    “pull” は、リモートの変更をローカルリポジトリへマージする動作です。ユーザーが実際に依頼しているのは、サーバーに対して、自分のリモート上の変更をどこかのブランチへマージしてほしいということです。GitLab の “merge request” が正しい表現です。
    誰かが fetch したところ、コミットが消えたのを見たことがあります。リポジトリが時間的に後戻りしたため、隠れていたのです。Git はそうした詳細を status に隠しているので、git-promptpowerline-shell を使わなければ、暗闇の中で作業しているようなものです。

    • git-request-pull(1) に残っている元々の考え方は、私たち全員が kernel.org/git や redhat.com/git のような場所にそれぞれ Git リポジトリを置き、別々のサーバー、組織、ドメインにあるリポジトリから pull してほしいとメールで依頼する、というものでした。

      GitHub は Git の request-pull コマンドから着想を得ましたが、それをある GitHub リポジトリから別の GitHub リポジトリへマージするものとして再解釈しました。

    • サーバーの視点では、ユーザーがリモートでサーバーがローカルです。だからユーザーがサーバーに、自分の側から pull するよう依頼しているのです。

      少なくとも私はずっとそう読んでいました。

    • Git の本では、この用語の由来と、なぜ筋が通っているのかを説明しています。

      要するに、サーバーに対する依頼ではなく、他の人に対する依頼です。プロジェクトに貢献したい変更を見てもらうために、相手に自分のブランチを pull してほしいと依頼するのです。

[0] https://git-scm.com/book/en/v2/Distributed-Git-Contributing-...

  • 「pull request」はGitHubが普及させたもので、悪くない用語だと思う。実際に別のリポジトリからpullするのか、同じリポジトリ内で「merge request」を行うのかという区別は、あまり興味深いものではない。「merge request」はGitLab特有の用語のように感じる
  • これはいつも理解するのが最も難しかった。当然、pull requestではなくpush requestやmerge requestであるべきではないかと思う