取り消し - git rebase-i 使い方




なぜ私がやっていることがコミットを潰しているときにgit-rebaseが私にマージ競合を与えるのですか? (4)

私たちは400以上のコミットを持つGitリポジトリを持っています。最初の数十は試行錯誤が多いです。 私たちは、これらのコミットを単一のコミットに多く縮小して、クリーンアップする必要があります。 当然、git-rebaseはやり方のようです。 私の問題は、マージ競合で終わることであり、これらの競合は解決しにくいということです。 なぜ私はコミット(削除や並べ替えではない)を壊すだけなので、私はすべての競合があるはずである理由を理解していません。 おそらく、これはgit-rebaseがいかにsquackをしているかを完全には理解していないことを示しています。

私が使っているスクリプトの修正版です:

repo_squash.sh(これは実際に実行されるスクリプトです):

rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
GIT_EDITOR=../repo_squash_helper.sh git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a

repo_squash_helper.sh(このスクリプトはrepo_squash.shによってのみ使用されます):

if grep -q "pick " $1
then
#  cp $1 ../repo_squash_history.txt
#  emacs -nw $1
  sed -f ../repo_squash_list.txt < $1 > $1.tmp
  mv $1.tmp $1
else
  if grep -q "initial import" $1
  then
    cp ../repo_squash_new_message1.txt $1
  elif grep -q "fixing bad import" $1
  then
    cp ../repo_squash_new_message2.txt $1
  else
    emacs -nw $1
  fi
fi

repo_squash_list.txt:(このファイルはrepo_squash_helper.shによってのみ使用されます)

# Initial import
s/pick \(251a190\)/squash \1/g
# Leaving "Needed subdir" for now
# Fixing bad import
s/pick \(46c41d1\)/squash \1/g
s/pick \(5d7agf2\)/squash \1/g
s/pick \(3da63ed\)/squash \1/g

私はあなたの想像力に "新しいメッセージ"のコンテンツを残します。 最初は、 "--strategy theirs"オプションなしでこれを行いました(つまり、ドキュメントが正しく再帰的であると理解していれば再帰的な戦略が使われているかわかりません)仕事。 また、repo_squash_helper.shでコメントアウトされたコードを使用して、sedスクリプトが動作する元のファイルを保存し、それに対してsedスクリプトを実行して、実行していたことを確認していたことを指摘しておきます(そうだった)。 繰り返しますが、なぜ私は紛争が起こるのかも知らないので、どの戦略を使用するかはそれほど重要ではないようです。 アドバイスや洞察力があれば助けになるでしょうが、大部分は私がこの不満を募らせたいと思っています。

Jefromiとの議論からの追加情報で更新されました:

大規模な「本当の」リポジトリに取り組む前に、テストリポジトリで同様のスクリプトを使用しました。 非常にシンプルなリポジトリであり、テストはきれいに機能しました。

失敗したときのメッセージは次のとおりです。

Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir

これは、最初のスカッシュコミット後の最初の選択です。 git statusを実行すると、きれいな作業ディレクトリが得られます。 私がgit rebase --continueならば、もう少しコミットしても似たようなメッセージが表示されます。 私がそれをやり直すと、数十回のコミットの後にもう一つの非常によく似たメッセージが出ます。 私がもう一度やり直すなら、今度は約百回のコミットを経て、このメッセージを得ます:

Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply f1de3bc... Incremental

git status実行すると、次のようになります。

# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   repo/file_A.cpp
# modified:   repo/file_B.cpp
#
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified:      repo/file_X.cpp
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted:    repo/file_Z.imp

これはちょうどピックの結果であったので、「両方の変更された」ビットは私にとって奇妙に聞こえる。 また、「矛盾」を見ると、1つの行には[タブ]文字で開始するバージョンと、4つのスペースで始まる1つのバージョンがあります。 これは私の設定ファイルをどのようにセットアップしたかで問題になるかもしれないように聞こえましたが、その中には何も並べていません。 (私はcore.ignorecaseがtrueに設定されていることに気付きましたが、明らかにgit-cloneは自動的にそれを行いました。元のソースがWindowsマシン上にあることを考えると、私は完全に驚いていません。

私は手動でfile_X.cppを修正すると、直後に別の競合が発生して失敗します。今回は、あるバージョンが存在するはずと思っているファイル(CMakeLists.txt)と、そうでないと考えられるバージョンの間で失敗します。 私がこのファイルを(私がしている)欲しいと言ってこの矛盾を修正した場合、後でいくつかのコミットが起こります(この同じファイル内に別の矛盾があります)。 それでも、紛争の約25%に過ぎません。

私はまた、このプロジェクトがsvnリポジトリで開始されたことが非常に重要かもしれないので、指摘しておきます。 その初期の履歴は、おそらくそのsvnリポジトリからインポートされました。

アップデート#2:

私がrepo_squash.shを変更することにしたのは、(Jefromiのコメントの影響を受けた)綱引きの時でした。

rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a

そして、私は元のエントリーをそのまま受け入れました。 つまり、「rebase」は何かを変えてはいけません。 以前と同じ結果で終わった。

アップデート#3:

あるいは、戦略を省略し、最後のコマンドを次のように置き換えた場合、

git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a

私はもはや "何もコミットしない" rebaseの問題は発生しませんが、私はまだ他の葛藤が残っています。

問題を再現するおもちゃのリポジトリで更新する:

test_squash.sh(実際に実行したファイルです):

#========================================================
# Initialize directories
#========================================================
rm -rf test_squash/ test_squash_clone/
mkdir -p test_squash
mkdir -p test_squash_clone
#========================================================

#========================================================
# Create repository with history
#========================================================
cd test_squash/
git init
echo "README">README
git add README
git commit -m"Initial commit: can't easily access for rebasing"
echo "Line 1">test_file.txt
git add test_file.txt
git commit -m"Created single line file"
echo "Line 2">>test_file.txt 
git add test_file.txt 
git commit -m"Meant for it to be two lines"
git checkout -b dev
echo Meaningful code>new_file.txt
git add new_file.txt 
git commit -m"Meaningful commit"
git checkout master
echo Conflicting meaningful code>new_file.txt
git add new_file.txt 
git commit -m"Conflicting meaningful commit"
# This will conflict
git merge dev
# Fixes conflict
echo Merged meaningful code>new_file.txt
git add new_file.txt
git commit -m"Merged dev with master"
cd ..

#========================================================
# Save off a clone of the repository prior to squashing
#========================================================
git clone test_squash test_squash_clone
#========================================================

#========================================================
# Do the squash
#========================================================
cd test_squash
GIT_EDITOR=../test_squash_helper.sh git rebase -i [email protected]{7}
#========================================================

#========================================================
# Show the results
#========================================================
git log
git gc
git reflog
#========================================================

test_squash.helper.sh(test_sqash.shで使用):

# If the file has the phrase "pick " in it, assume it's the log file
if grep -q "pick " $1
then
  sed -e "s/pick \(.*\) \(Meant for it to be two lines\)/squash \1 \2/g" < $1 > $1.tmp
  mv $1.tmp $1
# Else, assume it's the commit message file
else
# Use our pre-canned message
  echo "Created two line file" > $1
fi

PS:はい、私はemacsをフォールバックエディタとして使用しているのを見たときにあなたが苦しんでいることを知っています。

PPS:リベース後に既存のリポジトリのすべてのクローンを吹き飛ばす必要があることはわかっています。 (「リポジトリが公開された後にリポジトリを再構築しない」という行に沿って)

PPPS:これに賞金を追加する方法を教えてもらえますか? 私は編集モードかビューモードかにかかわらず、この画面のどこにでもオプションを表示していません。


そうです、私は答えを捨てるのに十分な自信を持っています。 多分それを編集する必要がありますが、私はあなたの問題が何であるかを知っていると信じています。

あなたのおもちゃのレポ・テスト・ケースにはマージがあります。悪化すると、競合とマージします。 そして、あなたはマージの向こう側にリベースしています。 -pを指定しないと( -i完全に動作しません)、マージは無視されます。 これは、rebaseが次のコミットをチェリーピックアップしようとするときに、競合解決で何をしたとしてもそこはないので、そのパッチが適用されない可能性があることを意味します。 ( git cherry-pickは元のコミットと現在のコミットと共通の祖先の間で3方向マージを行うことでパッチを適用できるので、これはマージの競合とみなされます)

残念ながら、コメントに書いてあるとおり、 -i-p (マージを保存する)はうまくいっていません。 私はその編集/編集作業を知っており、その並べ替えはしません。 しかし、私スカッシュでうまく動作すると信じています。 これは文書化されていませんが、以下で説明するテストケースでは機能しました。 あなたのケースは、より複雑なやり方であれば、それはまだ可能ですが、あなたが望むことをするのに苦労するかもしれません。 (物語の道徳:合併前に rebase -iクリーンアップする)

ですから、A、B、Cを一緒に盛り上げたいという非常に単純なケースがあるとしましょう。

- o - A - B - C - X - D - E - F (master)
   \             /
    Z -----------

今、私が言ったように、Xに矛盾がなければ、 git rebase -i -pは期待通りに動作します。

競合がある場合は、状況が少し難しくなります。 それはうまくやっていくでしょうが、マージを再作成しようとすると、再び衝突が起こります。 それらをもう一度解決してインデックスに追加してから、 git rebase --continuegit rebase --continueて移動してください。 (もちろん、オリジナルのマージコミットからバージョンをチェックすることで、それらを再度解決できます。)

repoでrerere有効にした場合( rerere.enabledがtrueに設定されている場合)、これは簡単ですrerere.enabledは、最初に競合が発生したときから利用できるようになります。それが正常に機能していることを確認し、ファイルをインデックスに追加して、続行します。 (あなたは一歩rerere.autoupdatererere.autoupdateにして、それをあなたのために追加するので、マージも失敗しません)。 しかし、あなたはレレレを有効にしていないと推測しているので、自分で紛争解決を行わなければなりません。*

*あるいは、 rerere-train.shスクリプトをgit-contribから試すことができます。 rerere-train.shスクリプトは、既存のマージコミットから「rerereデータベースを準備する」ことを試みます.-基本的に、すべてのマージコミットをチェックアウトし、それらをマージしようとします。マージが失敗した場合、結果を取得してgit-rerereます。 これは時間がかかることがあります。実際に使用したことはありませんが、非常に役に立ちます。


対話型のリベースで使用される場合、 -Xオプションと戦略オプションは無視されます。

commit db2b3b820e2b28da268cc88adff076b396392dfe (2013年7月、1.8.4以上)を参照してください。

対話型リベースのマージオプションを無視しない

マージ戦略とそのオプションはgit rebaseで指定できますが、 -- interactiveでは完全に無視されます。

サインオフ:Arnaud Fontaine

これは、 -Xと戦略が対話型のrebaseとプレーンなrebaseで動作するようになり、初期のスクリプトがよりうまく動作できることを意味しています。


私はシンプルだけど似たような問題にぶち壊っていました.1)ローカルブランチ上のマージ競合を解決しました.2)コミットの数を増やしていました.3)リベースしてマージ競合を起こしたいと思っていました。

私のために、 git rebase -p -i master働いた。 それは元の紛争解決のコミットを維持し、私は他の人を一番上に押しつぶすことができました。

誰かを助けることを願って!


私は同様の要件、すなわち私の開発ブランチのintermeiateコミットを捨てて、私はこの手順が私のために働いていたことを発見しました。
私の働く支店で

git reset –hard mybranch-start-commit
git checkout mybranch-end-commit . // files only of the latest commit
git add -a
git commit -m”New Message intermediate commits discarded”

私たちは最新のコミットをブランチのコミットに結びました! マージ競合の問題はありません! 私の学習実習では、この段階でこの結論に至りました。その目的のためのより良いアプローチがありますか?







git-checkout