http://www.kernel.org/pub/software/scm/git/docs/git-rebase.htmlからわかるように、リベースされたブランチは別のブランチに「移動」されています。
ただし、テスト中に見たものは、リベースされたブランチからのコミットが履歴に残っているため、事実上複製されていることを示しています.
おそらく、何かが足りないか、リベースの目的を完全に理解していないか、またはその両方です。
私が見ているのが意図した動作である場合、なぜそうなのですか?
http://www.kernel.org/pub/software/scm/git/docs/git-rebase.htmlからわかるように、リベースされたブランチは別のブランチに「移動」されています。
ただし、テスト中に見たものは、リベースされたブランチからのコミットが履歴に残っているため、事実上複製されていることを示しています.
おそらく、何かが足りないか、リベースの目的を完全に理解していないか、またはその両方です。
私が見ているのが意図した動作である場合、なぜそうなのですか?
つまりrebase
、ツリーのある部分から別の開始点にコミットを適用する方法です。それらの変更をコピーすることはできますが、移動はしません。
git コミットは不変であることに注意してください。一度何かにハッシュが含まれると、変更されることはありません。つまり、いくつかの変更を別の変更の上にリベースすると、ハッシュは必然的に異なるため、git は古いものと新しいものの両方を保持します。
ただし、ブランチ名が古いコミットを指していない場合 (例では「add file2」)、数週間後に git の自動ガベージ コレクターが古いコミットをリポジトリから削除します。(なぜ 2 週間なのですか? そうすれば、気が変わった場合は、 から古いコミットを取得できますgit reflog
。) 一般に、これは良いことです。誤ってデータを失うことは難しくなりますが、ファイルが非常に巨大な場合git prune
とを組み合わせて使用git gc
すると、冗長なデータを削除できます。
ここには 2 つの別個の現象があります。
gitk から投稿したスクリーンショットは、まだ古いコミットを示しています。それが gitk の仕組みです。Ctrl+を押して再読み込みすると (マウス ユーザーの場合は [ファイル] > [更新] ではF5なくF5[ファイル] > [再読み込み])、関連性がなくなったため、古いコミットが表示されなくなります。
Git には、コミットを作成する多くの操作があります。さらに、ファイル ストアにファイルまたはツリー オブジェクトを作成します。これらのオブジェクトの多くが使用されなくなったという事実は関係ありません。
これには多くの利点があります。あなたの例では、リベースが悪い考えであると判断した場合、古いコミットがまだ存在し、回復できることを意味します。便利な構文もあります。最後に移動する前にtopic@{1}
指していたコミットを参照します。topic
ここでは、リベースの直前になります。
Git のオブジェクト モデルは、この種の点で優れています。このような余分なコミットを横たわっていても、余分なスペースはほとんど必要ありません。あなたが説明しているようなリベースの場合、古いブランチを保持するのにかかる費用はせいぜい数百バイトだと思います。
もちろん、それは時間の経過とともに加算されます。そのgit gc
ため(特定のコマンドによって時々自動的に実行されます)が実行されますgit prune
。そしてgit prune
、古くて関連性がなくなったコミットとオブジェクトを探して、それらを消去します。
これは、リベースが機能していないことを意味するものではなく、リベースの「移動」コミットのアイデアが単純化されているということです。リベースが実際に行うことは、各コミットとその親の間の違いを新しいブランチに適用し、古いブランチの各コミットに対してそれらの違いを持つ新しいコミットを作成することです。次に、ブランチ履歴を見ると、それらのコミットが移動されたかのようにブランチを更新します。
Rebase は履歴を書き換えるコマンドです。しかし、git のおかげで履歴が失われることはありません。git ガベージ コレクターがこれらのダングリング コミットをクリアするまで、ロールバックできます。
...the rebased branch is 'moved' on to another one.
That's one way of putting it, but not an entirely accurate one.
The best way to think about a git repo is to think of it as a composition of two things: a directed, acyclic graph of immutable commits, each representing a version of your software (or whatever's in the repo), and a set of branch pointer variables (master and so on).
Let's say you start with a repo with three commits that looks like this:
a--> b
\-> c
ここで、起点/マスターブランチ ポインターは を指しb
、マスターブランチ ポインターは を指しc
ます。ここには、a
、 、b
およびの 3 つの異なるバージョンのソフトウェアがありますc
。
次に にリベースすることにした場合c
、b
次のようなレポになります。
a--> b--> c'
\-> c
masterブランチ ポインタがを指すように変更されましたc'
。「このコミットをプッシュする」と、コミットc'
が元のリポジトリに送信され、元のリポジトリのマスターブランチ ポインターが を指すように変更されc'
、それに一致するように元/マスターブランチ ポインターが変更されます。
は、まだ存在する とはc'
別のコミットであり、ソフトウェアの 4 つのバージョンがあることに気付くでしょう。c
コミットは、c'
道徳的に同じ変更を加えます(または、競合を適切に編集したと仮定して、そうするb
ことがc
望まれます)。a
c
それを指しているブランチポインターがなくなったため(実際にはreflogの外側)、通常のgit操作中のある時点でガベージコレクションが行われます。
(Git はまた、ソフトウェアのさまざまな [完全な] バージョンをすべて個別にチェックアウトした場合よりも少ないスペースに格納するために、いくつかの凝った圧縮トリックを実行しますが、それは本当に必要なことではなく、考えるべきことでもありません。 )
カジュアルな話では、この操作を「マスターブランチの変更」と呼んでいますが、実際には、新しいブランチを作成し、マスターが参照するものを古いブランチから新しいブランチに変更することです。