Есть ли способ удалить несколько последовательных коммитов в ветке?
Допустим, история выглядит следующим образом:
A -> B -> C -> D
Теперь я хотел бы удалить изменения, внесенные B иC, поэтому история выглядит следующим образом:
A -> D
Например, типичным сценарием может быть пьяный разработчик, который вносит мусор в коммиты B и C, при этом пишет хорошие вещи в D.
Я придумал довольно плохой (и, вероятно, не очень надежный способ сделать это):
# Get a patch for the good things
# Context lines are set to zero so applying
# the patch won't choke because of missing lines
# that were added by C or D
git format-patch --stdout -U0 revC..revD > CtoD
# Go back in time to last good revision before mayhem
git reset --hard revA
# Apply good things at this point
git apply --stat CtoD
git apply --check CtoD
git apply CtoD
# Add new files from patch
git add <any files that were created in CtoD patch>
# And commit
git commit -a -m "Removed B and C commits. Drunk dev fired"
Этот способ сделать это далеко не идеален.Удаление контекста для diff, вероятно, сделает git-apply choke во многих ситуациях, и файлы должны быть добавлены git-add'ом вручную.Я также мог бы полностью упустить смысл здесь ...
Может кто-нибудь указать мне правильный способ сделать это?
Спасибо за чтение!
РЕДАКТИРОВАТЬ:
Я забыл сказать, что я должен перенести все эти вещи в удаленный репозиторий, поэтому я попробовал предложение rafl, и, пока на клоне все в порядке, невозможно протолкнуть материалк началу координат.
Вот подробный (и длинный, извините!) список того, что было сделано:
##
# Create test environment
##
# all will happen below 'testing', so the mess can easily be wiped out
mkdir testing
cd testing
# Create 'origin' (sandbox_project) and the working clone (work)
mkdir sandbox_project work
# Create origin repos
cd sandbox_project
git init --bare
# Clone it
cd ../work
git clone ../sandbox_project
cd sandbox_project
# Create a few files :
# at each rev (A, B, C, D) we respectively create a fileA .. fileD
# at each rev, we also add a line to fileA
echo "This file was created in A" > fileA
git add .
git commit -a -m 'First revision'
git tag "revA"
for i in B C D; do
echo "This file was created in $i" >> file$i
echo "This change was done in $i" >> fileA
git add file$i
git commit -a -m "revision $i"
git tag "rev$i"
done
# We push changes to origin
git push origin master
Теперь это выглядит так:
$ git log --graph --decorate --pretty=oneline --abbrev-commit
* e3dc9b7 (HEAD, revD, origin/master, master) revision D
* e21fd6a (revC) revision C
* a9192ec (revB) revision B
* a16c9dd (revA) First revision
Я хочу удалить то, что было введено в B и C:
$ git rebase -i HEAD~3
Automatic cherry-pick failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply e3dc9b7... revision D
$ git status
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: fileD
#
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: fileA
#
Конечно, есть проблема с файлом A (проблема, которую я решил с помощью своего решения, применив diff, сгенерированный с -U0Здесь я редактирую файл A, удаляю конфликтующие строки и продолжаю перебазирование:
This file was created in A
<<<<<<< HEAD
=======
This change was done in B
This change was done in C
This change was done in D
>>>>>>> e3dc9b7... revision D
редактируется так:
This file was created in A
This change was done in D
А затем:
$ git add fileA
$ git rebase --continue
# leave mesasge as-is
[detached HEAD e2d4032] revision D
2 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 fileD
Successfully rebased and updated refs/heads/master.
$ git push
To ../sandbox_project
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to '../sandbox_project'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again. See the 'Note about
fast-forwards' section of 'git push --help' for details.
Ну, это выглядит нормально для меня, так как я перебазировал, поэтому я попытался:
$ git pull
Auto-merging fileA
CONFLICT (content): Merge conflict in fileA
Automatic merge failed; fix conflicts and then commit the result.
$ git log --graph --decorate --pretty=oneline --abbrev-commit
* e2d4032 (HEAD, master) revision D
* a16c9dd (revA) First revision
Опять файл A не может быть объединен, так как он имеетревизии B и C Cruft.Здесь мы снова идем, редактируем файл A и удаляем изменения, вводимые с помощью и C.
Затем:
$ git add fileA
$ git commit -a
[master b592261] Merge branch 'master' of ../sandbox_project
$ git push
Counting objects: 9, done.
Compressing objects: 100% (5/5), done.
Unpacking objects: 100% (5/5), done.
Writing objects: 100% (5/5), 674 bytes, done.
Total 5 (delta 0), reused 0 (delta 0)
To ../sandbox_project
e3dc9b7..b592261 master -> master
Выглядит хорошо, но:
$ git log --graph --decorate --pretty=oneline --abbrev-commit
* b592261 (HEAD, origin/master, master) Merge branch 'master' of ../sandbox_project
|\
| * e3dc9b7 (revD) revision D
| * e21fd6a (revC) revision C
| * a9192ec (revB) revision B
* | e2d4032 revision D
|/
* a16c9dd (revA) First revision
В то время как вконец, файл A в порядке, у меня все еще есть файл B и файл C, который я не хотел.И по ходу дела мне пришлось дважды разрешать конфликты слияния для файла A.
Любая подсказка?