うわー、これは厳しいものでした! :-)
スクリプトを使用して、問題を再現しました。スクリプトがあり、それすべてについて非常に奇妙な何かはしかしだったので、最初、私はこれを残して、リベース段階をトリミング(若干変更さ):
#!/bin/sh
set -e
if [ -d testing_git ]; then
echo test dir testing_git already exists - halting
exit 1
fi
mkdir testing_git
cd testing_git
git init
touch main.txt
git add .
git commit -m "initial commit"
# setup B branch
git checkout -b B
echo hello > test.txt
git add .
git commit -m "added test.txt"
# setup master
git checkout master
echo hello > test.txt
git add .
git commit -m "added test.txt"
rm test.txt
git add .
git commit -m "remove test.txt"
かつて私はこれを取得し、コミットを検査、実行します。
$ git log --graph --decorate | sed 's/@/ /'
* commit 249e4893ea7458f45fe5cdc496ddc0292a3f03ef (HEAD -> master)
| Author: Chris Torek <chris.torek gmail.com>
| Date: Thu May 5 20:28:02 2016 -0700
|
| remove test.txt
|
* commit a132dc9e3939b5338f7c784c58da9c83f4902c8d (B)
| Author: Chris Torek <chris.torek gmail.com>
| Date: Thu May 5 20:28:02 2016 -0700
|
| added test.txt
|
* commit 81c4d9be82094fdb4c88ed0a53bdbd5c3dfd7a5a
Author: Chris Torek <chris.torek gmail.com>
Date: Thu May 5 20:28:02 2016 -0700
initial commit
master
の親コミットはブランチB
のコミットであり、4つではなく3つのコミットがあることに注意してください。スクリプトが4つのコマンドを実行すると、どのようになりますかgit commit
コマンド?
は今
[edit]
$ sh testrebase.sh
[snip output]
$ cd testing_git && git log --oneline --decorate --graph --all
* cddbff1 (HEAD -> master) remove test.txt
* c4ac1b2 added test.txt
| * fefc150 (B) added test.txt
|/
* 8c07bb6 initial commit
おっと、今は4つのコミット、および適切な枝を持っている...のは、右git checkout master
後、スクリプトにsleep 2
を追加してみましょうし、それを再実行すると何が起こるかを参照してください!
最初のスクリプトで3回コミットしたのはなぜですか?sleep 2
を追加すると4回コミットされますか?
答えはコミットのIDにあります。各コミットには一意のID(おそらく!)があり、コミットの内容のチェックサムです。ここでは、コミット-branch B
に最初の頃だったものです:
$ git cat-file -p B | sed 's/@/ /'
tree c3cd0188a6a1490204e25547986e49b0b445dec8
parent 81c4d9be82094fdb4c88ed0a53bdbd5c3dfd7a5a
author Chris Torek <chris.torek gmail.com> 1462505282 -0700
committer Chris Torek <chris.torek gmail.com> 1462505282 -0700
added test.txt
我々は、tree
、parent
、2(名前、電子メール、タイムスタンプ)は、著者とコミッタのためのトリプル空白行を持っている、とログメッセージ。親は最初のマスターブランチにコミットされ、ツリーは、我々が(その内容で)test.txt
を追加したときに我々が作った木です。
次に、をブランチでコミットするようになると、gitは新しいファイルから新しいツリーを作成しました。このツリーは、ビットごとの私達はちょうどブランチB
にコミットしたものと同一であったので、それは同じ固有ID(覚えて、そこにそのツリーのコピーは1つだけのレポでありますので、これは正しい動作です)を得ました。そして、それは私の名前と電子メールやタイムスタンプいつものように、ログメッセージと共に新しいコミットオブジェクトを作りました。しかし、このコミットはちょうどブランチB
で行ったコミットと少し似ていたので、前と同じIDを得て、ブランチmaster
がそのコミットを指し示すようにしました。言い換えれば
、我々はをコミット再使用。 (master
がB
と同じコミットを指摘するように)私達はちょうど別の枝にそれを作りました。
sleep 2
を追加すると、タイムスタンプが新しいコミットに変更されました。今(B
とmaster
で)2つのコミットは、もはやビットごとに同一ではない:
$ git cat-file -p B | sed 's/@/ /' > bx
$ git cat-file -p master^ | sed 's/@/ /' > mx
$ diff bx mx
3,4c3,4
< author Chris Torek <chris.torek gmail.com> 1462505765 -0700
< committer Chris Torek <chris.torek gmail.com> 1462505765 -0700
---
> author Chris Torek <chris.torek gmail.com> 1462505767 -0700
> committer Chris Torek <chris.torek gmail.com> 1462505767 -0700
異なるタイムスタンプ=異なるコミット=はるかに賢明なセットアップ。
実際にリベースを実行しても、ファイルは削除されました。
これは仕様であることが判明しました。 git rebase
を実行すると、セットアップコードはチェリーピッキングのすべてのコミットを単に一覧表示するのではなく、代わりにを使用して削除する必要があるコミットを見つけます。
ことがtest.txt
を追加コミットを上流にあるので、Gitはちょうどそれを完全に削除します。ここでの仮定は、あなたが誰かに上流にそれを送った、彼らはすでにそれを取ったことで、再びそれを取る必要はありません。
はさんが再びを再生スクリプトを変更してみましょう - そして、私たちはアップのでmaster
への変更は異なり、--cherry-pick --right-only
を経由して、リストから削除されないこと、物事をスピードアップ、この時間をsleep 2
を取ることができるようになります。我々はまだ同じ一行でtest.txt
が追加されますが、我々はまた、その中にmain.txt
コミットを変更します:
# setup master
git checkout master
echo hello > test.txt
echo and also slight difference >> main.txt
git add .
git commit -m "added test.txt"
我々は先に行くとだけでなく、最終的なgit checkout B
とgit rebase master
ラインをオンにし、そして今回ができ、作品をリベース
$ git log --oneline --decorate --graph --all
* c31b13a (HEAD -> B) added test.txt
* da2ca52 (master) remove test.txt
* 6972019 added test.txt
* 0f0d2e8 initial commit
$ ls
main.txt test.txt
このリベースはこれを実現しませんでした。それは私が期待したものではありません(他の答えが指摘されているにもかかわらず、はと書かれています)、それは「rebaseはちょうどチェリーピックを繰り返している」ということは間違いではないことを意味します。コミットを破棄するケース。
実際に、非対話型リベースするためには、この驚くべきビットを使用:
git format-patch -k --stdout --full-index --cherry-pick --right-only \
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
"$revisions" ${restrict_revision+^$restrict_revision} \
>"$GIT_DIR/rebased-patches"
$revisions
はmaster...B
に、この場合には、膨張あります。
--cherry-pick --right-only
のオプションは、git format-patch
には記載されていません。それらのためにgit rev-list
のドキュメントを見ることを知っている必要があります。
対話型リベースは、異なる手法を使用しますが、まだアップストリームにあるコミットを選択します。 rebase
をrebase -i
に変更した場合、リベース命令は、pick
行の代わりにnoop
行で構成されています。
質問を正しく読んでいない場合は、そうではありません。これを再現するスクリプトがありますか? (空のディレクトリを作成し、 'git init'を実行し、ファイルを作成し、コミットし、ブランチを作成し、さらにファイルを作成し、最後に' git rebase'を実行して問題を表示します) – torek
確かに、本当に素早く書く。 – maxbart
このスクリプトをコピーして貼り付けることができます。http://pastebin.com/mPcmGCT5 edit:ディレクトリを作成するためのスクリプトを更新しました(testing_gitを作成します) – maxbart