{
  "title": "バージョン管理(Git)",
  "excerpt": "バージョン管理を_適切に_使用する方法と、それを活用して災害から身を守り、他者と協力し、問題のある変更を素早く見つけて分離する方法。もう`rm -rf; git clone`は不要です。もうマージコンフリクトもありません(少なくとも減ります)。もう大量のコメントアウトされたコードブロックもありません。もうコードを壊した原因を見つける方法で悩むこともありません。もう「あぁ、動いていたコードを削除してしまったのでは?!」と心配することもありません。",
  "content_html": "<p>バージョン管理システム(VCS)は、ソースコード(または他のファイルやフォルダのコレクション)への変更を追跡するために使用されるツールです。名前が示すように、これらのツールは変更の履歴を維持するのに役立ちます。さらに、コラボレーションを促進します。VCSは、フォルダとその内容への変更を一連のスナップショットで追跡します。各スナップショットは、トップレベルディレクトリ内のファイル/フォルダの全体的な状態をカプセル化します。VCSは、各スナップショットを作成した人、各スナップショットに関連付けられたメッセージなどのメタデータも維持します。</p>\n\n<p>なぜバージョン管理は便利なのでしょうか?一人で作業している場合でも、プロジェクトの古いスナップショットを見たり、特定の変更が行われた理由のログを保持したり、並行する開発ブランチで作業したりすることができます。他の人と作業する場合、他の人が何を変更したかを確認したり、並行開発での競合を解決したりするための非常に貴重なツールです。</p>\n\n<p>最新のVCSでは、次のような質問に簡単に(そしてしばしば自動的に)答えることもできます:</p>\n\n<ul>\n<li>このモジュールを書いたのは誰ですか?</li>\n<li>この特定のファイルのこの特定の行はいつ編集されましたか?誰によって?なぜ編集されたのですか?</li>\n<li>過去1000回のリビジョンで、特定のユニットテストがいつ/なぜ動作しなくなったのですか?</li>\n</ul>\n\n<p>他のVCSも存在しますが、<strong>Git</strong>はバージョン管理の事実上の標準です。この<a href=\"https://xkcd.com/1597/\">XKCDコミック</a>は、Gitの評判を捉えています:</p>\n\n<p><img src=\"https://imgs.xkcd.com/comics/git.png\" alt=\"xkcd 1597\"></p>\n\n<p>Gitのインターフェースは抽象化が漏れているため、Gitをトップダウン(インターフェース/コマンドラインインターフェースから始める)で学ぶと、多くの混乱を招く可能性があります。一握りのコマンドを暗記して、それらを魔法の呪文のように考え、何か問題が起きたときは上記のコミックのアプローチに従うことは可能です。</p>\n\n<p>Gitは確かに醜いインターフェースを持っていますが、その基礎となる設計とアイデアは美しいものです。醜いインターフェースは<em>暗記</em>しなければなりませんが、美しい設計は<em>理解</em>することができます。このため、Gitのボトムアップの説明を提供し、データモデルから始めて、後でコマンドラインインターフェースをカバーします。データモデルが理解されれば、コマンドが基礎となるデータモデルをどのように操作するかという観点から、コマンドをよりよく理解できます。</p>\n\n<h1>Gitのデータモデル</h1>\n\n<p>バージョン管理には、多くのアドホックなアプローチがあります。Gitには、履歴の維持、ブランチのサポート、コラボレーションの実現など、バージョン管理のすべての優れた機能を可能にする、よく考えられたモデルがあります。</p>\n\n<h2>スナップショット</h2>\n\n<p>Gitは、トップレベルディレクトリ内のファイルとフォルダのコレクションの履歴を、一連のスナップショットとしてモデル化します。Gitの用語では、ファイルは「blob」と呼ばれ、単なるバイトの集まりです。ディレクトリは「tree」と呼ばれ、名前をblobまたはtreeにマッピングします(したがって、ディレクトリは他のディレクトリを含むことができます)。スナップショットは、追跡されているトップレベルのtreeです。たとえば、次のようなtreeがあるとします:</p>\n\n<pre><code>&lt;root&gt; (tree)\n|\n+- foo (tree)\n|  |\n|  + bar.txt (blob, contents = \"hello world\")\n|\n+- baz.txt (blob, contents = \"git is wonderful\")\n</code></pre>\n\n<p>トップレベルのtreeには2つの要素が含まれています。tree「foo」(それ自体が1つの要素、blob「bar.txt」を含む)と、blob「baz.txt」です。</p>\n\n<h2>履歴のモデル化:スナップショットの関連付け</h2>\n\n<p>バージョン管理システムはスナップショットをどのように関連付けるべきでしょうか?1つの単純なモデルは、線形の履歴を持つことです。履歴は時系列のスナップショットのリストになります。多くの理由から、Gitはこのような単純なモデルを使用しません。</p>\n\n<p>Gitでは、履歴はスナップショットの有向非巡回グラフ(DAG)です。これは難しい数学用語のように聞こえるかもしれませんが、怖がる必要はありません。これが意味するのは、Gitの各スナップショットが「親」のセット、つまりそれに先行するスナップショットを参照するということです。線形履歴の場合のように単一の親ではなく親のセットである理由は、スナップショットが複数の親から派生する可能性があるためです。たとえば、2つの並行する開発ブランチを結合(マージ)する場合などです。</p>\n\n<p>Gitはこれらのスナップショットを「commit」と呼びます。コミット履歴を視覚化すると、次のようになります:</p>\n\n<pre><code>o &lt;-- o &lt;-- o &lt;-- o\n            ^\n             \\\n              --- o &lt;-- o\n</code></pre>\n\n<p>上記のASCIIアートでは、<code>o</code>は個々のコミット(スナップショット)に対応しています。矢印は各コミットの親を指しています(「後に来る」ではなく「前に来る」関係です)。3番目のコミットの後、履歴は2つの別々のブランチに分岐します。これは、たとえば、2つの別々の機能が互いに独立して並行して開発されていることに対応する可能性があります。将来、これらのブランチはマージされて、両方の機能を組み込んだ新しいスナップショットを作成し、次のような新しい履歴を生成する可能性があります。新しく作成されたマージコミットは太字で示されています:</p>\n\n<pre class=\"highlight\">\n<code>\no &lt;-- o &lt;-- o &lt;-- o &lt;---- <strong>o</strong>\n            ^            /\n             \\          v\n              --- o &lt;-- o\n</code>\n</pre>\n\n<p>Gitのコミットは不変です。これは、間違いを修正できないという意味ではありません。コミット履歴への「編集」は、実際には完全に新しいコミットを作成し、参照(以下を参照)が新しいコミットを指すように更新されるだけです。</p>\n\n<h2>データモデル、疑似コードとして</h2>\n\n<p>Gitのデータモデルを疑似コードで書き下すと、理解しやすいかもしれません:</p>\n\n<pre><code>// ファイルはバイトの集まり\ntype blob = array&lt;byte&gt;\n\n// ディレクトリには名前付きファイルとディレクトリが含まれる\ntype tree = map&lt;string, tree | blob&gt;\n\n// コミットには親、メタデータ、トップレベルのtreeがある\ntype commit = struct {\n    parents: array&lt;commit&gt;\n    author: string\n    message: string\n    snapshot: tree\n}\n</code></pre>\n\n<p>これは、クリーンでシンプルな履歴のモデルです。</p>\n\n<h2>オブジェクトとコンテンツアドレッシング</h2>\n\n<p>「オブジェクト」は、blob、tree、またはcommitです:</p>\n\n<pre><code>type object = blob | tree | commit\n</code></pre>\n\n<p>Gitデータストアでは、すべてのオブジェクトは<a href=\"https://en.wikipedia.org/wiki/SHA-1\">SHA-1ハッシュ</a>によってコンテンツアドレス指定されます。</p>\n\n<pre><code>objects = map&lt;string, object&gt;\n\ndef store(object):\n    id = sha1(object)\n    objects[id] = object\n\ndef load(id):\n    return objects[id]\n</code></pre>\n\n<p>Blob、tree、commitはこのように統一されています:それらはすべてオブジェクトです。他のオブジェクトを参照する場合、ディスク上の表現に実際にそれらを<em>含む</em>のではなく、ハッシュによってそれらへの参照を持ちます。</p>\n\n<p>たとえば、<a href=\"#snapshots\">上記</a>のディレクトリ構造例のtree(<code>git cat-file -p 698281bc680d1995c5f4caaf3359721a5a58d48d</code>を使用して視覚化)は、次のようになります:</p>\n\n<pre><code>100644 blob 4448adbf7ecd394f42ae135bbeed9676e894af85    baz.txt\n040000 tree c68d233a33c5c06e0340e4c224f0afca87c8ce87    foo\n</code></pre>\n\n<p>tree自体には、その内容へのポインタ、<code>baz.txt</code>(blob)と<code>foo</code>(tree)が含まれています。<code>git cat-file -p 4448adbf7ecd394f42ae135bbeed9676e894af85</code>でbaz.txtに対応するハッシュによってアドレス指定された内容を見ると、次のようになります:</p>\n\n<pre><code>git is wonderful\n</code></pre>\n\n<h2>参照</h2>\n\n<p>現在、すべてのスナップショットはSHA-1ハッシュで識別できます。これは不便です。なぜなら、人間は40文字の16進数文字列を覚えるのが得意ではないからです。</p>\n\n<p>この問題に対するGitの解決策は、SHA-1ハッシュの人間が読める名前で、「参照」と呼ばれます。参照はコミットへのポインタです。不変であるオブジェクトとは異なり、参照は可変です(新しいコミットを指すように更新できます)。たとえば、<code>master</code>参照は通常、開発のメインブランチの最新のコミットを指します。</p>\n\n<pre><code>references = map&lt;string, string&gt;\n\ndef update_reference(name, id):\n    references[name] = id\n\ndef read_reference(name):\n    return references[name]\n\ndef load_reference(name_or_id):\n    if name_or_id in references:\n        return load(references[name_or_id])\n    else:\n        return load(name_or_id)\n</code></pre>\n\n<p>これにより、Gitは長い16進数文字列の代わりに、「master」のような人間が読める名前を使用して、履歴内の特定のスナップショットを参照できます。</p>\n\n<p>1つの詳細は、新しいスナップショットを取得するときに、それが何に対して相対的であるかを知るために(コミットの<code>parents</code>フィールドをどのように設定するか)、履歴内の「現在どこにいるか」という概念が必要になることがよくあります。Gitでは、その「現在どこにいるか」は「HEAD」と呼ばれる特別な参照です。</p>\n\n<h2>リポジトリ</h2>\n\n<p>最後に、Git<em>リポジトリ</em>とは何か(大まかに)を定義できます:それはデータ<code>objects</code>と<code>references</code>です。</p>\n\n<p>ディスク上で、Gitが保存するのはオブジェクトと参照だけです:それがGitのデータモデルのすべてです。すべての<code>git</code>コマンドは、オブジェクトを追加したり、参照を追加/更新したりすることによって、コミットDAGの何らかの操作にマッピングされます。</p>\n\n<p>コマンドを入力するときは常に、コマンドが基礎となるグラフデータ構造に対してどのような操作を行っているかを考えてください。逆に、コミットDAGに特定の種類の変更を加えようとしている場合、たとえば「コミットされていない変更を破棄し、'master'参照をコミット<code>5d83f9e</code>を指すようにする」場合、おそらくそれを行うコマンドがあります(たとえば、この場合は<code>git checkout master; git reset --hard 5d83f9e</code>)。</p>\n\n<h1>ステージングエリア</h1>\n\n<p>これは、データモデルとは直交する別の概念ですが、コミットを作成するためのインターフェースの一部です。</p>\n\n<p>上記で説明したスナップショットを実装する1つの方法は、作業ディレクトリの<em>現在の状態</em>に基づいて新しいスナップショットを作成する「スナップショットの作成」コマンドを持つことです。一部のバージョン管理ツールはこのように機能しますが、Gitは違います。クリーンなスナップショットが必要であり、現在の状態からスナップショットを作成することが常に理想的であるとは限りません。たとえば、2つの別々の機能を実装し、2つの別々のコミットを作成したいシナリオを想像してください。最初のコミットは最初の機能を導入し、次のコミットは2番目の機能を導入します。または、コードのあちこちにデバッグ用のprint文を追加し、バグ修正も行ったシナリオを想像してください。print文をすべて破棄しながら、バグ修正をコミットしたいとします。</p>\n\n<p>Gitは、「ステージングエリア」と呼ばれるメカニズムを通じて、次のスナップショットに含める変更を指定できるようにすることで、このようなシナリオに対応します。</p>\n\n<h1>Gitコマンドラインインターフェース</h1>\n\n<p>情報の重複を避けるため、以下のコマンドを詳細に説明しません。詳細については、強く推奨される<a href=\"https://git-scm.com/book/en/v2\">Pro Git</a>を参照してください。</p>\n\n<h2>基本</h2>\n\n<p><code>git init</code>コマンドは、新しいGitリポジトリを初期化し、リポジトリのメタデータは<code>.git</code>ディレクトリに保存されます:</p>\n\n<pre><code class=\"language-console\">$ mkdir myproject\n$ cd myproject\n$ git init\nInitialized empty Git repository in .git\n$ git status\nOn branch master\nNo commits yet\nnothing to commit (create/copy files and use \"git add\" to track)\n</code></pre>\n\n<p>この出力をどのように解釈すればよいでしょうか?「No commits yet」は基本的に、バージョン履歴が空であることを意味します。それを修正しましょう。</p>\n\n<pre><code class=\"language-console\">$ echo \"hello, git\" &gt; hello.txt\n$ git add hello.txt\n$ git status\nOn branch master\nNo commits yet\nChanges to be committed:\n  (use \"git rm --cached &lt;file&gt;...\" to unstage)\n        new file:   hello.txt\n$ git commit -m 'Initial commit'\n[master (root-commit) 4515d17] Initial commit\n 1 file changed, 1 insertion(+)\n create mode 100644 hello.txt\n</code></pre>\n\n<p>これで、ファイルをステージングエリアに<code>git add</code>し、その変更を<code>git commit</code>して、シンプルなコミットメッセージ「Initial commit」を追加しました。<code>-m</code>オプションを指定しなかった場合、Gitはテキストエディタを開いてコミットメッセージを入力できるようにします。</p>\n\n<p>これで空でないバージョン履歴ができたので、履歴を視覚化できます。履歴をDAGとして視覚化することは、リポジトリの現在の状態を理解し、Gitデータモデルの理解と結びつけるのに特に役立ちます。</p>\n\n<p><code>git log</code>コマンドは履歴を視覚化します。デフォルトでは、グラフ構造を隠したフラット化されたバージョンを表示します。<code>git log --all --graph --decorate</code>のようなコマンドを使用すると、リポジトリの完全なバージョン履歴がグラフ形式で視覚化されて表示されます。</p>\n\n<pre><code class=\"language-console\">$ git log --all --graph --decorate\n* commit 4515d17a167bdef0a91ee7d50d75b12c9c2652aa (HEAD -&gt; master)\n  Author: Subramanya N &lt;subramanyanagabhushan@gmail.com&gt;\n  Date: Tue Dec 21 22:18:36 2020 -0500\n      Initial commit\n</code></pre>\n\n<p>これは、単一のノードしか含まれていないため、あまりグラフのようには見えません。さらにいくつかの変更を加え、新しいコミットを作成し、もう一度履歴を視覚化してみましょう。</p>\n\n<pre><code class=\"language-console\">$ echo \"another line\" &gt;&gt; hello.txt\n$ git status\nOn branch master\nChanges not staged for commit:\n  (use \"git add &lt;file&gt;...\" to update what will be committed)\n  (use \"git checkout -- &lt;file&gt;...\" to discard changes in working directory)\n        modified:   hello.txt\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n$ git add hello.txt\n$ git status\nOn branch master\nChanges to be committed:\n  (use \"git reset HEAD &lt;file&gt;...\" to unstage)\n        modified:   hello.txt\n$ git commit -m 'Add a line'\n[master 35f60a8] Add a line\n 1 file changed, 1 insertion(+)\n</code></pre>\n\n<p>今度は、履歴を再度視覚化すると、グラフ構造の一部が表示されます:</p>\n\n<pre><code>* commit 35f60a825be0106036dd2fbc7657598eb7b04c67 (HEAD -&gt; master)\n| Author: Subramanya N &lt;subramanyanagabhushan@gmail.com&gt;\n| Date:   Tue Dec 21 22:26:20 2020 -0500\n|     Add a line\n* commit 4515d17a167bdef0a91ee7d50d75b12c9c2652aa\n  Author: Subramanya N &lt;subramanyanagabhushan@gmail.com&gt;\n  Date: Tue Dec 21 22:18:36 2020 -0500\n      Initial commit\n</code></pre>\n\n<p>また、現在のHEADと現在のブランチ(master)が表示されていることに注意してください。</p>\n\n<p><code>git checkout</code>コマンドを使用して、古いバージョンを見ることができます。</p>\n\n<pre><code class=\"language-console\">$ git checkout 4515d17  # 以前のコミットハッシュ;あなたのものは異なります\nNote: checking out '4515d17'.\nYou are in 'detached HEAD' state. You can look around, make experimental\nchanges and commit them, and you can discard any commits you make in this\nstate without impacting any branches by performing another checkout.\nIf you want to create a new branch to retain commits you create, you may\ndo so (now or later) by using -b with the checkout command again. Example:\n  git checkout -b &lt;new-branch-name&gt;\nHEAD is now at 4515d17 Initial commit\n$ cat hello.txt\nhello, git\n$ git checkout master\nPrevious HEAD position was 4515d17 Initial commit\nSwitched to branch 'master'\n$ cat hello.txt\nhello, git\nanother line\n</code></pre>\n\n<p>Gitは、<code>git diff</code>コマンドを使用して、ファイルがどのように進化したか(差分、またはdiff)を表示できます:</p>\n\n<pre><code class=\"language-console\">$ git diff 4515d17 hello.txt\ndiff --git c/hello.txt w/hello.txt\nindex 94bab17..f0013b2 100644\n--- c/hello.txt\n+++ w/hello.txt\n@@ -1 +1,2 @@\n hello, git\n +another line\n</code></pre>\n\n<ul>\n<li><code>git help &lt;command&gt;</code>: gitコマンドのヘルプを取得</li>\n<li><code>git init</code>: 新しいgitリポジトリを作成し、データは<code>.git</code>ディレクトリに保存されます</li>\n<li><code>git status</code>: 何が起こっているかを教えてくれます</li>\n<li><code>git add &lt;filename&gt;</code>: ファイルをステージングエリアに追加</li>\n<li><code>git commit</code>: 新しいコミットを作成\n<ul>\n<li><a href=\"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\">良いコミットメッセージ</a>を書きましょう!</li>\n<li><a href=\"https://chris.beams.io/posts/git-commit/\">良いコミットメッセージ</a>を書くさらなる理由!</li>\n</ul></li>\n<li><code>git log</code>: 履歴のフラット化されたログを表示</li>\n<li><code>git log --all --graph --decorate</code>: 履歴をDAGとして視覚化</li>\n<li><code>git diff &lt;filename&gt;</code>: ステージングエリアに対して行った変更を表示</li>\n<li><code>git diff &lt;revision&gt; &lt;filename&gt;</code>: スナップショット間のファイルの差分を表示</li>\n<li><code>git checkout &lt;revision&gt;</code>: HEADと現在のブランチを更新</li>\n</ul>\n\n<h2>ブランチとマージ</h2>\n\n<p>ブランチを使用すると、バージョン履歴を「フォーク」できます。独立した機能やバグ修正を並行して作業するのに役立ちます。<code>git branch</code>コマンドを使用して新しいブランチを作成できます。<code>git checkout -b &lt;branch name&gt;</code>はブランチを作成してチェックアウトします。</p>\n\n<p>マージはブランチの反対です:フォークされたバージョン履歴を結合できます。たとえば、機能ブランチをmasterにマージして戻します。<code>git merge</code>コマンドがマージに使用されます。</p>\n\n<ul>\n<li><code>git branch</code>: ブランチを表示</li>\n<li><code>git branch &lt;name&gt;</code>: ブランチを作成</li>\n<li><code>git checkout -b &lt;name&gt;</code>: ブランチを作成して切り替え\n<ul>\n<li><code>git branch &lt;name&gt;; git checkout &lt;name&gt;</code>と同じ</li>\n</ul></li>\n<li><code>git merge &lt;revision&gt;</code>: 現在のブランチにマージ</li>\n<li><code>git mergetool</code>: マージコンフリクトの解決を支援する便利なツールを使用</li>\n<li><code>git rebase</code>: パッチのセットを新しいベースにリベース</li>\n</ul>\n\n<h2>リモート</h2>\n\n<ul>\n<li><code>git remote</code>: リモートをリスト</li>\n<li><code>git remote add &lt;name&gt; &lt;url&gt;</code>: リモートを追加</li>\n<li><code>git push &lt;remote&gt; &lt;local branch&gt;:&lt;remote branch&gt;</code>: オブジェクトをリモートに送信し、リモート参照を更新</li>\n<li><code>git branch --set-upstream-to=&lt;remote&gt;/&lt;remote branch&gt;</code>: ローカルブランチとリモートブランチの対応を設定</li>\n<li><code>git fetch</code>: リモートからオブジェクト/参照を取得</li>\n<li><code>git pull</code>: <code>git fetch; git merge</code>と同じ</li>\n<li><code>git clone</code>: リモートからリポジトリをダウンロード</li>\n</ul>\n\n<h2>元に戻す</h2>\n\n<ul>\n<li><code>git commit --amend</code>: コミットの内容/メッセージを編集</li>\n<li><code>git reset HEAD &lt;file&gt;</code>: ファイルをアンステージ</li>\n<li><code>git checkout -- &lt;file&gt;</code>: 変更を破棄</li>\n</ul>\n\n<h1>高度なGit</h1>\n\n<ul>\n<li><code>git config</code>: Gitは<a href=\"https://git-scm.com/docs/git-config\">高度にカスタマイズ可能</a>です</li>\n<li><code>git clone --depth=1</code>: バージョン履歴全体なしのシャロークローン</li>\n<li><code>git add -p</code>: インタラクティブなステージング</li>\n<li><code>git rebase -i</code>: インタラクティブなリベース</li>\n<li><code>git blame</code>: どの行を誰が最後に編集したかを表示</li>\n<li><code>git stash</code>: 作業ディレクトリへの変更を一時的に削除</li>\n<li><code>git bisect</code>: 履歴のバイナリサーチ(たとえば、リグレッション用)</li>\n<li><code>.gitignore</code>: 意図的に追跡されないファイルを無視するように<a href=\"https://git-scm.com/docs/gitignore\">指定</a></li>\n</ul>\n\n<h1>その他</h1>\n\n<ul>\n<li><strong>GUI</strong>: Gitには多くの<a href=\"https://git-scm.com/downloads/guis\">GUIクライアント</a>があります。個人的には使用せず、代わりにコマンドラインインターフェースを使用しています。</li>\n<li><strong>シェル統合</strong>: シェルプロンプトの一部としてGitステータスを表示するのは非常に便利です(<a href=\"https://github.com/olivierverdier/zsh-git-prompt\">zsh</a>、<a href=\"https://github.com/magicmonty/bash-git-prompt\">bash</a>)。<a href=\"https://github.com/ohmyzsh/ohmyzsh\">Oh My Zsh</a>のようなフレームワークに含まれていることがよくあります。</li>\n<li><strong>エディタ統合</strong>: 上記と同様に、多くの機能を備えた便利な統合があります。<a href=\"https://github.com/tpope/vim-fugitive\">fugitive.vim</a>はVimの標準的なものです。</li>\n<li><strong>ワークフロー</strong>: データモデルといくつかの基本的なコマンドを教えましたが、大規模なプロジェクトで作業する際に従うべきプラクティスについては説明していません(そして、<a href=\"https://nvie.com/posts/a-successful-git-branching-model/\">多くの</a><a href=\"https://www.endoflineblog.com/gitflow-considered-harmful\">異なる</a><a href=\"https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow\">アプローチ</a>があります)。</li>\n<li><strong>GitHub</strong>: GitはGitHubではありません。GitHubには、<a href=\"https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests\">プルリクエスト</a>と呼ばれる、他のプロジェクトにコードを貢献する特定の方法があります。</li>\n<li><strong>他のGitプロバイダー</strong>: GitHubは特別ではありません:<a href=\"https://about.gitlab.com/\">GitLab</a>や<a href=\"https://bitbucket.org/\">BitBucket</a>など、多くのGitリポジトリホストがあります。</li>\n</ul>\n\n<h1>リソース</h1>\n\n<ul>\n<li><a href=\"https://git-scm.com/book/en/v2\">Pro Git</a>は<strong>強く推奨される読み物</strong>です。第1章から第5章を読めば、Gitを熟練して使用するために必要なことのほとんどを学ぶことができます。データモデルを理解した今なら。後の章には、興味深い高度な内容があります。</li>\n<li><a href=\"https://ohshitgit.com/\">Oh Shit, Git!?!</a>は、一般的なGitの間違いから回復する方法についての短いガイドです。</li>\n<li><a href=\"https://eagain.net/articles/git-for-computer-scientists/\">Git for Computer Scientists</a>は、これらの講義ノートよりも疑似コードが少なく、派手な図が多い、Gitのデータモデルの短い説明です。</li>\n<li><a href=\"https://jwiegley.github.io/git-from-the-bottom-up/\">Git from the Bottom Up</a>は、好奇心旺盛な人のための、データモデルだけでなくGitの実装の詳細についての詳細な説明です。</li>\n<li><a href=\"https://smusamashah.github.io/blog/2017/10/14/explain-git-in-simple-words\">How to explain git in simple words</a></li>\n<li><a href=\"https://learngitbranching.js.org/\">Learn Git Branching</a>は、Gitを教えるブラウザベースのゲームです。</li>\n</ul>",
  "source_hash": "sha256:1882fed561269610d4ac35bc5a461efe73f8481070dd58a45db04a8899598a98",
  "model": "claude-sonnet-4-5-20250929",
  "generated_at": "2026-01-02T04:09:45.076118+00:00"
}