私はバージョン管理に不慣れです。「コミット」とは、作業中の新しい「現在の」バージョンを更新しながら、基本的にバックアップを作成することを理解しています。
私が理解していないのは、ステージングが実際的な観点から何であるかです。ステージングは名前だけで存在するものですか、それとも目的を果たしますか?あなたがコミットすると、とにかくすべてをコミットするでしょう、そうですか?
編集:用語がわかりにくいかもしれません。「ステージング」ファイルは「追跡」ファイルと同じですか?
私はバージョン管理に不慣れです。「コミット」とは、作業中の新しい「現在の」バージョンを更新しながら、基本的にバックアップを作成することを理解しています。
私が理解していないのは、ステージングが実際的な観点から何であるかです。ステージングは名前だけで存在するものですか、それとも目的を果たしますか?あなたがコミットすると、とにかくすべてをコミットするでしょう、そうですか?
編集:用語がわかりにくいかもしれません。「ステージング」ファイルは「追跡」ファイルと同じですか?
回答:
コミットすると、インデックス(「ステージングされた」ファイル)の変更のみがコミットされます。これには多くの用途がありますが、最も明白なのは、作業上の変更を小さな自己完結型の部分に分割することです。機能の実装中にバグを修正した可能性があります。あなたはできるgit add
だけのファイル(またはことをgit add -p
、ファイルの一部だけを追加するために!)とは、他のすべてをコミットする前にそのバグ修正をコミットします。あなたが使用しているgit commit -a
場合はadd
、コミットの直前にすべてのものを強制しています。-a
ステージングファイルを利用する場合は使用しないでください。
また、--cached
多くのコマンドを使用して、ステージングされたファイルを中間作業コピーとして扱うこともできます。たとえば、git diff --cached
はステージがどのように異なるかを示しHEAD
、他の実際の変更を混ぜずにコミットしようとしていることを確認できます。
git reset --hard
。
git diff --staged
たら、変更したファイルと変更したファイルを確認し、他の変更を開始します。ステージングの実用的な目的の1つは、ファイルコミットの論理的な分離です。
ステージングを使用すると、引き続きファイル/作業ディレクトリの編集を行うことができ、準備が整ったと思われるときに部分的にコミットすることができるため、論理的に関連のない編集には別々のステージを使用できます。
あなたが4ファイルがあるとしfileA.html
、fileB.html
、fileC.html
とfileD.html
。あなたはすべての4つのファイルに変更を加え、コミットする準備ができているが、中に変更fileA.html
し、fileB.html
論理的に関連している(例えば、両方のファイルで同じ新機能の実装)しばらくの変化fileC.html
とfileD.html
独立したファイルに、以前に論理的に無関係です。ファイルfileA.html
を最初にステージングしfileB.html
てコミットできます。
git add fileA.html
git add fileB.html
git commit -m "Implemented new feature XYZ"
次に、次のステップで残りの2つのファイルに変更をステージングしてコミットします。
git add fileC.html
git add fileD.html
git commit -m "Implemented another feature EFG"
git commit -m "Implemented new feature XYZ" fileA.html fileB.html
git addコマンドがなくても問題なく動作します。私はステージングがコンセプトではないSubversionの世界から来ているので、Gitステージングの有用性について確信がありません
gitコマンドの使用と、Githubのリポジトリでログファイルが維持されていると想像するadd
と、理解しやすいcommit
でしょう。私の典型的なプロジェクトのログファイルは次のようになります。
---------------- Day 1 --------------------
Message: Complete Task A
Index of files changed: File1, File2
Message: Complete Task B
Index of files changed: File2, File3
-------------------------------------------
---------------- Day 2 --------------------
Message: Correct typos
Index of files changed: File3, File1
-------------------------------------------
...
...
...and so on
私は通常、1日をgit pull
リクエストで開始し、リクエストで終了しgit push
ます。したがって、1日のレコード内のすべてが、それらの間で発生することに対応します。毎日、いくつかのファイルを変更する必要がある1つ以上の論理的なタスクが完了しています。そのタスク中に編集されたファイルは、インデックスにリストされます。
これらの各サブタスク(ここではタスクAとタスクB)は個別のコミットです。このgit add
コマンドは、「変更されたファイルのインデックス」リストにファイルを追加します。このプロセスは、ステージングとも呼ばれます。git commit
コマンドレコードは/変更やカスタムメッセージとともに対応するインデックスリストを確定します。
変更するのはリポジトリのローカルコピーだけであり、Githubのローカルコピーは変更しないことに注意してください。この後、「git push」を実行した場合にのみ、これらの記録されたすべての変更を、コミットごとのインデックスファイルとともに、メインリポジトリ(Github)に記録します。
例として、架空のログファイルの2番目のエントリを取得するには、次のようにします。
git pull
# Make changes to these files
git add File3 File4
# Verify changes, run tests etc..
git commit -m 'Correct typos'
git push
一言で言えば、git add
そしてgit commit
あなたが体系的論理サブ変化にメインリポジトリに変更を打破することができます。他の回答やコメントが指摘しているように、もちろんそれらにはもっと多くの用途があります。ただし、これは最も一般的な使用法の1つであり、Svnのような他の一般的なものとは異なり、Gitが多段階のリビジョン管理システムであることの背後にある推進原則です。
ステージング領域は、より柔軟にコミットを作成するのに役立ちます。作成とは、コミットを論理ユニットに分割することを意味します。保守可能なソフトウェアが必要な場合、これは非常に重要です。これを達成する最も明白な方法:
単一の作業ディレクトリで複数の機能/バグに取り組みながら、意味のあるコミットを作成することができます。すべてのアクティブな作業を含む単一の作業ディレクトリがあることも非常に便利です。(これは、変更がファイルと重複しない限り、ステージング領域なしで実行できます。また、重複するかどうかを手動で追跡する責任も追加されます)
あなたはここでより多くの例を見つけることができます: インデックスの使用
そして最良の部分は、このワークフローのリストで利点が止まることはありません。独自のワークフローが登場した場合、ステージング領域が役立つことはほぼ確実です。
展開するにはベン・ジャクソンの答えは罰金、密接に元の質問で見てみましょうです。(わざわざタイプの質問をする理由については彼の回答を参照してください。これは何が起こっているかについての詳細です。)
私はバージョン管理に不慣れです。「コミット」とは、作業中の新しい「現在の」バージョンを更新しながら、基本的にバックアップを作成することを理解しています。
これではありませんかなり右。バックアップとバージョン管理は確かに関連しています。正確には、意見の問題であるいくつかの事柄にどれほど強く依存しているかですが、意図があったとしても、確かにいくつかの違いがあります。すべてのストレージメディアなどを含む建物全体)。バージョン管理は通常、よりきめの細かいやり取りのために設計されており、バックアップにはない機能を提供します。通常、バックアップはしばらく保存され、その後「古すぎる」として破棄されます。重要なのは、より新しいバックアップです。バージョン管理は通常、コミットされたすべてのバージョンを永久に保存します。
私が理解していないのは、ステージングが実際的な観点から何であるかです。ステージングは名前だけで存在するものですか、それとも目的を果たしますか?あなたがコミットすると、とにかくすべてをコミットするでしょう、そうですか?
はいといいえ。ここでのGitのデザインは多少独特です。個別のステージング手順を必要としないバージョン管理システムが存在します。たとえば、Mercurialは、それ以外の点では使用法がGitによく似ていますがhg add
、まったく新しいファイルを導入する最初の手順以外に、別の手順は必要ありません。Mercurialでは、hg
いくつかのコミットを選択するコマンドを使用し、作業を行ってからを実行hg commit
すれば完了です。Gitではgit checkout
、1を使用してから1を実行しgit add
、次にを実行し、次にを実行しますgit commit
。なぜ余分なgit add
ステップですか?
ここでの秘密は、Gitがさまざまに呼ぶ、インデックス、ステージング領域、またはときどき(ごく最近では)キャッシュです。これらはすべて同じものの名前です。
編集:用語がわかりにくいかもしれません。「ステージング」ファイルは「追跡」ファイルと同じですか?
いいえ、ただしこれらは関連しています。追跡されたファイルはGitリポジトリのインデックスに存在するものです。インデックスを正しく理解するには、コミットの理解から始めるのがよいでしょう。
Gitバージョン2.23以降、のgit switch
代わりに使用できますgit checkout
。この特定のケースでは、これら2つのコマンドはまったく同じことを行います。新しいコマンドはgit checkout
、あまりにも多くのものでいっぱいになったために存在しています。彼らは、2つの別々のコマンドに出し分け、しまったgit switch
とgit restore
のGitを使用することが簡単かつ安全にするために、。
Gitでは、コミットにより、Gitが認識しているすべてのファイルの完全なスナップショットが保存されます。(Gitはどのファイルを知っていますか?次のセクションで説明します。)これらのスナップショットは、読み取り専用、Git専用、圧縮、および重複排除された特別な形式で保存され、通常はGit自体のみが読み取ることができます。 。(そこより多くのものがそれぞれよりもコミットしていただこのスナップショットが、それは我々がここでカバーするすべてです。)
重複除外はスペースの節約に役立ちます。通常、変更するのは数個のファイルのみで、その後新しいコミットを作成します。だから、ほとんどのファイルのコミット以前のファイルをコミットすると、ほとんど同じです。これらのファイルを直接再利用するだけで、Gitは多くのスペースを節約します。1つのファイルのみを変更した場合、新しいコミットは1つの新しいコピーのスペースのみを使用します。それでも圧縮されます(実際には後で発生しますが、場合によっては非常に圧縮.git
されます)。通常の日常のファイルに展開されると、ディレクトリは実際に含まれているファイルよりも小さくなります。コミットされたファイルは常にフリーズされるため、重複除外は安全です。誰も変更できないので、コミットがお互いのコピーに依存しても安全です。
ただし、保存されたファイルは、常に凍結されたGit専用の特別な形式であるため、Gitは各ファイルを通常の日常のコピーに展開する必要があります。この通常のコピーはGitのコピーではありません。それは、あなたのコピーであり、あなたが意図したとおりに行うことができます。あなたがそうするように言ったとき、Gitはこれらに書き込むだけなので、あなたはあなたのコピーを扱うことができます。これらの使用可能なコピーは、作業ツリーまたは作業ツリーにあります。
これは、特定のコミットをチェックアウトすると、自動的に各ファイルの2つのコピーが存在することを意味します。
現在のcommitには、Gitが常時フリーズされたGit化されたコピーがあります。このコピーは変更できません(もちろん、別のコミットを選択したり、新しいコミットを作成したりすることはできます)。
作業ツリーに通常形式のコピーがあります。コンピューターのコマンドを使用して、これに対してやりたいことをすべて実行できます。
他のバージョン管理システム(上記のMercurialを含む)は、これらの2つのコピーとともにここで停止します。作業ツリーのコピーを変更してコミットするだけです。Git ...はしません。
これら2つのコピーの間に、Git はすべてのファイルの3番目のコピー2を保存します。この3番目のコピーは凍結された形式ですが、コミットの凍結されたコピーとは異なり、変更できます。変更するには、を使用しますgit add
。
git add
指令手段は、ファイルのインデックスコピーが作業ツリーのコピーを一致させます。つまり、Gitに次のように伝えています。更新された作業ツリーのコピーを圧縮し、重複を排除して、新しいコミットに凍結する準備をすることで、インデックスにある凍結フォーマットの重複排除されたコピーを置き換えます。 を使用しない場合git add
でも、インデックスには現在のコミットからの固定形式のコピーが保持されます。
を実行するとgit commit
、Gitはすぐにインデックスにあるものをパッケージ化して、新しいスナップショットとして使用します。すでにフリーズ形式であり、事前に重複が排除されているので、Gitは多くの追加作業を行う必要はありません。
これは、追跡されていないファイルのすべてについても説明します。追跡されていないファイルとは、作業ツリーにはあるが、現在 Gitのインデックスにはないファイルです。この状態でファイルがどのように巻き取られるかは関係ありません。多分あなたはあなたのコンピュータの他の場所からあなたの作業ツリーにそれをコピーしました。たぶんここで作りたてだろう。Gitのインデックスにコピーがあった可能性がありますが、そのコピーをで削除しましたgit rm --cached
。何らかの方法で、作業ツリーにここにコピーがありますが、Gitのインデックスにはコピーがありません。ここで新しいコミットを行うと、そのファイルは新しいコミットには含まれません。
注git checkout
最初に埋めあなたがチェックアウトコミットからGitリポジトリのインデックス。したがって、インデックスはコミットと一致し始めます。Gitは、この同じソースから作業ツリーにも入力します。したがって、最初は3つすべてが一致します。作業ツリーとgit add
それらのファイルを変更すると、インデックスと作業ツリーが一致します。次に実行するgit commit
と、Gitはインデックスから新しいコミットを作成し、3つすべてが再び一致します。
Gitはインデックスから新しいコミットを作成するため、次のように表現できます。Gitのインデックスは、作成する次のコミットを保持します。 これは、競合するマージ中にGitのインデックスが果たす拡張された役割を無視しますが、とりあえずそれを無視したいと思います。:-)
これですべてですが、それでもまだかなり複雑です!Gitのインデックスの内容を正確に確認する簡単な方法がないため、これは特にトリッキーです。3 しかし、そこにある何かはかなり便利だ方法で、起こっているのがわかりますGitのコマンドは、そのコマンドがありますgit status
。
2技術的には、これは実際にはまったくコピーではありません。代わりに、それはGit化されたファイルへの参照であり、事前重複排除されたものすべてです。ここには、モード、ファイル名、ステージング番号、Gitを高速化するためのいくつかのキャッシュデータなど、さらに多くのものがあります。しかし、あなたはGitリポジトリの低レベルcommands-のいくつかの作業に入る場合を除きgit ls-files --stage
、およびgit update-index
特定の-あなただけのコピーとして考えることができます。
3git ls-files --stage
コマンドを使用すると、Gitリポジトリのインデックス内のすべてのファイルの名前とステージング番号が表示されますが、通常これは非常に便利な、とにかくではありません。
git status
このgit status
コマンドは、実際には2つの個別のgit diff
コマンドを実行することで機能します(また、どのブランチにいるかを通知するなど、他の便利な機能も実行します)。
1つ目git diff
は、現在のコミット(いつまでもフリーズされている)をGitのインデックスにあるものと比較します。同じファイルの場合、Gitは何もしません。異なるファイルの場合、Gitはこのファイルがコミット用にステージングされていることを通知します。これは、すべての新しいファイルを-場合は含まれコミットはありません。sub.py
それには、しかし、インデックスがない持っているsub.py
ことで、このファイルを追加、および任意の削除されたファイルされ、だった(とされる)そのコミットではなくではありませんもうインデックス(git rm
おそらく)。
2番目git diff
は、Gitのインデックス内のすべてのファイルを作業ツリー内のファイルと比較します。同じファイルの場合、Gitは何もしません。異なるファイルの場合、Gitはこのファイルがコミット用にステージングされていないことを通知します。最初のdiffとは異なり、この特定のリストにはまったく新しいファイルは含まれません。ファイルuntracked
が作業ツリーに存在するが、Gitのインデックスには存在しない場合、Gitはそれを追跡されていないファイルのリストに追加するだけです。4
最後に、これらの追跡されていないファイルをリストに蓄積すると、それらのファイルの名前もgit status
アナウンスされますが、特別な例外があります。ファイルの名前がファイルにリストされている場合、この最後のリストは抑制されます。 追跡されたファイル(Gitのインデックスにあるファイル)を一覧表示しても、ここでは効果がないことに注意してください。ファイルはインデックスに含まれているため、たとえに一覧表示されていても、比較され、コミットされます。無視ファイルは、「追跡されていないファイル」の問題を抑制するだけです。5.gitignore
.gitignore
.gitignore
4の短いバージョンを使用する場合git status
— git status -s
—追跡されていないファイルはそれほど分離されていませんが、原理は同じです。このようにファイルを蓄積するとgit status
、ディレクトリ名を出力するだけで、追跡されていない一連のファイルの名前を要約することもできます。完全なリストを取得するには、git status -uall
またはを使用しますgit status -u
。
5ファイルをリストすると、en-masse は追跡されていないファイルのような多くのファイル操作を追加しgit add .
たり、git add *
スキップしたりします。を使用git add --force
して、通常はスキップされるファイルを追加できるため、この部分は少し複雑になります。他のいくつかの通常はマイナーな特別なケースがあり、それらはすべてこれに追加されます。ファイル.gitignore
はより適切に呼び出される.git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-them
か、同様に扱いにくいかもしれません。しかし、.gitignore
それはばかげているので、そうです。
git add -u
、git commit -a
などここで知っておくと便利なショートカットがいくつかあります。
git add .
更新されたすべてのファイルが現在のディレクトリとサブディレクトリに追加されます。これはを尊重.gitignore
するので、現在追跡されていないファイルがから不満がない場合、ファイルはgit status
自動的に追加されません。
git add -u
更新されたすべてのファイルを作業ツリーの任意の場所に自動追加します。6 これは追跡されたファイルにのみ影響します。作業ツリーのコピーを削除した場合は、インデックスコピーも削除されることに注意してください(git add
これにより、インデックスが作業ツリーと一致するようになります)。
git add -A
git add .
作業ツリーの最上位から実行するようなものです(ただし脚注6を参照)。
これらに加えて、を実行することができますgit commit -a
。これは、running and thenとほぼ同じ7です。つまり、Mercurialで便利なのと同じ動作が得られます。git add -u
git commit
私は通常、git commit -a
パターンに反対することをお勧めします。git status
頻繁に使用し、出力をよく見て、状況が期待したものと異なる場合は、その理由を理解します。を使用git commit -a
すると、ファイルを誤って変更し、コミットするつもりがなかった変更をコミットするのは簡単です。しかし、これは主に好み/意見の問題です。
6 GitバージョンがGit 2.0より前のバージョンである場合は、ここで注意してください。git add -u
現在のディレクトリとサブディレクトリでのみ機能するため、最初に作業ツリーの最上位レベルに上る必要があります。git add -A
オプションでは、同様の問題があります。
7実際には追加のインデックスを作成し、その別のインデックスを使用してコミットを実行するため、ほぼ同等と言えgit commit -a
ます。コミットが機能すれば、実行と同じ効果が得られますgit add -u && git commit
。コミットが機能しない場合、つまりGitに多くの方法のいずれかでコミットをスキップさせた場合、git add
Gitが一時的な追加インデックスをスローしてメインインデックスの使用に戻るため、後でファイルが削除されません。。
git commit --only
ここで使用すると、さらに複雑な問題が発生します。この場合、Gitは3番目のインデックスを作成し、特にプリコミットフックを使用する場合は、非常に扱いにくいものになります。これは、別のgit add
操作を使用するもう1つの理由です。
@Ben Jacksonと@Tapashee Tabassum Urmiによって言及されているように、ステージを使用してコミットを小さくすることのポイントがわかりますが、その目的で使用することもありますが、主にコミットを大きくするために使用します!これが私のポイントです:
いくつかの小さなステップを必要とする小さな機能を追加したいとします。小さなステップのために別のコミットをして、タイムラインをあふれさせても意味がありません。ただし、各ステップを保存し、必要に応じて戻りたいのですが、
小さなステップを重ねるだけで、コミットする価値があると感じたときにコミットします。このようにして、不要なコミットをタイムラインから削除し、最後のステップを元に戻す(チェックアウト)ことができます。
これを行う他の方法(git履歴を簡略化する)は、好みに応じて使用できると思います。