19

これは多くのシナリオにとって悪い考えであることを理解しています。私はGitを学び、実験しています。この演習では、コードが損なわれることはありません。

次のような構造を作成しました。

* [cf0149e] (HEAD, branch_2) more editing
* [8fcc106] some edit
|
|  * [59e643e] (branch_2b) branch 2b
| /
|/
|  * [0f4c880] (branch_2_a) branch 2a
| /
|/
*  [a74eb2a] checkout 1
*  [9a8dd6a] added branch_2 line
|
|
| * [bb903de] (branch_3) branch 3
|/
|
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

ここで、このグラフを調べて、さまざまなコミットの名前を変更して、意味を成したいと思います。

例えば:

    * | [a74eb2a] checkout 1
    * | [9a8dd6a] added branch_2 line

renamed to:

    * | [a74eb2a] branch 2 commit 2
    * | [9a8dd6a] branch 2 commit 1

ご了承ください:

[cf0149e] (HEAD, branch_2) more editing
[59e643e] (branch_2b) branch 2b
[0f4c880] (branch_2_a) branch 2a

はすべて次のものから分岐しています。

[a74eb2a] checkout 1

私は実験しました

git rebase -i 328454f

次に、変更したいコミットで「選択」を「編集」に変更し、その後実行します

git commit --amend -m "the new message" 

リベースプロセスが継続したため。

このアプローチの問題は、最後のコミットの後、git rebase --continueたまたまいたブランチで 2 つの新しいコミット (名前を変更したかった 2 つの重複) が発生することです。たとえば、HEAD が「branch_2」にある間にリベースを実行すると、グラフは次のようになります。

* [cf0149e] (HEAD, branch_2) more editing
* [8fcc106] some edit
* [3ff23f0] branch 2 commit 2
* [2f287a1] branch 2 commit 1
|  
|    * [59e643e] (branch_2b) branch 2b
|   /
|  /
| |  * [0f4c880] (branch_2_a) branch 2a
| | /
| |/
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
|
| * [bb903de] (branch_3) branch 3
|/
|
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

つまり、まったく同じコード状態を表す 2 つのコミット セットができました。

私がやりたかったのは、コミットメッセージを変更することだけでした。

また、最初のメッセージの名前を「テスト」から「初期バージョン」などに変更したいと考えています。git commit --amend -m "Initial version"そのコミットをチェックアウトすると、ヘッドレスモードになってしまうので、それを行うことができないようです。

私は何を間違っていますか?確かにそう難しいことはありません。

編集:
これは、私が試したアプローチです。もちろん、歴史を書き換えます。したがって、非常に特殊なケース以外では、これは悪い考えです。手順は次のとおりです。

変更するブランチをチェックアウトします。パッチ ファイルを作成します。

git format-patch HEAD~x   // Where x is how far back from HEAD you need to patch

パッチ ファイルを編集して、コミット メッセージを変更します。ここでヘッドをリセットします。

git reset --hard HEAD~x   // Same x as before

パッチを適用します。

git am 000*

新しいコミットは、新しい SHA1 で作成されます。ブランチが修正されたメッセージで新しいコミットを参照する必要がある場合は、git rebaseそれらを移動するために使用する必要があります。

私自身の例を使用すると、パッチ手順を適用した後、次のようになりました。

* [7761415] (HEAD, branch_2) branch 2 commit 4
* [286e1b5] branch 2 commit 3
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [59e643e] (branch_2b) branch 2b
| | * [0f4c880] (branch_2_a) branch 2a
| |/
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

だから、私は自分の branch_2 コミットに適切なラベルを付けました。ここで、branch_2a を移動して、分岐するようにします。[53d638c] branch 2 commit 2

チェックアウト branch_2a

git checkout branch_2a

リベース

git rebase 53d638c

今私が持っています:

* [fb4d1c5] (HEAD, branch_2a) branch 2a
| * [7761415] (branch_2) branch 2 commit 4
| * [286e1b5] branch 2 commit 3
|/
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [59e643e] (branch_2b) branch 2b
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

branch_2b で同じ手順を実行すると、次のようになります。

* [ca9ff6c] (HEAD, branch_2b) branch 2b
| * [fb4d1c5] (branch_2a) branch 2a
|/
| * [7761415] (branch_2) branch 2 commit 4
| * [286e1b5] branch 2 commit 3
|/
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

これはまさに私が探していたものです。乱雑ではありません。繰り返しますが、非常に特殊なケース以外でやりたいことではありません。私の場合、Git を学ぶために遊んでいるだけなので、上記は実際のコード リポジトリには影響しません。必要に応じてこれを実行できることを知っておくと便利です。

次に、最初のコミットの名前を変更します。

4

1 に答える 1

22

実際にコミットを変更することはできません。電子メール行の名前のスペルからコミット タイムスタンプの正確な秒まで、あらゆる場所で最も小さな 1 ビットの変更を行うと、新しい別の SHA1 を持つ新しい別のコミットが取得されます (SHA1 は "trueコミットは 4 つのタイプのオブジェクトのうちの 1 つであり、git データベース内の各「オブジェクト」のいわば「名前」です)。

コミットの不変部分の 1 つは「親コミット」です。これは、最新のコミットから最も古いコミットまで逆方向にチェーンを構築します。

したがって、チェーン内の各コミットは、元のコミットと同じ内容/効果を持ち、対話中に行った変更をプラスまたはマイナスしてgit rebase -i新しいコミットチェーンを作成します。すべてが完了すると、古いコミット チェーンの末尾からラベル (いわば付箋) が削除され、新しいコミット チェーンの末尾に貼り付けられます。変更/リベースする最も古いコミットのコピーを作成することから始めます。これには、リベースされた親があります(元のチェーンと同じ親コミットでも、別の親でもかまいません。新しいコミットであるため、どちらの方法でも問題ありません)。次に、古いチェーンの次のもののコピーを作成しますが、新しいチェーンを指します。古いチェーンの最後まで繰り返します。

これが、他のすべてのブランチがリベースされたブランチから独立した理由です。古いコミット ID を使用しているため、そうする必要があります。新しいリベースされたブランチから分岐させたい場合は、それらのそれぞれに移動してリベースする必要があります。

強力なスイス軍のチェーンソーのようなコマンドがありgit filter-branch、これを使用して、非常に大規模な一連の「多くのコミットをやり直し、元のコミットと (ほとんど) 同じ内容のすべての新しいコミットを作成する」ことができます。git rebaseいわばステロイドで、この目的に使用できます。(--allすべてのブランチに影響を与えるために実行します。)もちろん、実際にはすべてのコミットをやり直すため、元のリポジトリとは基本的に無関係なリポジトリになります。

親がないため、最初のコミットを書き直すのは困難です (不可能ではありません) rebase1 (filter-branchできます。) これらは SHA1 ID を変更し、レポを複製した人はそれらを使用しているか、それらに依存しているため、通常、このようなコミットをいじることができない機能です。他の誰もコミットの特定のセットに依存していないことがわかっている場合は、rebase好きなようにできますが、最初のコミットに戻ることはありません。これは、リポジトリの共有部分にあるためです。「最初のコミット」に至るまでの全体がすべてあなた自身のプライベートなものであることは非常にまれです(もちろん「決して」ではありません)。


1これを書いてからgit rebase、最初のコミットのようにルート コミットをコピーすることを学びました。2 従来のgit rebase構文を使用すると、ルートの親コミットに名前を付ける必要があり、もちろん親はありません (これがグラフの「ルート」になります)。したがって、このケースをカバーする引数としてrebase使用します。--root

2複数のルートを持つことができます。git checkout --orphanたとえば、 の後に を使用git commitして、新しいルート コミットを作成します。git ソース自体は複数のルートを持つ git リポジトリに保持されていますが、これらがあるのは少し珍しいことです。

于 2012-05-25T05:30:15.173 に答える