У меня есть существующий репозиторий с корнем R
, затем несколько десятков коммитов, включая множественные слияния, до X
, а затем линейная история до Y
. Я хотел бы объединить все от R
до X
в один коммит и принудительно нажать его. Как я могу сделать это без особых усилий, связанных с повторным разрешением слияний?
В качестве альтернативы эту проблему можно сформулировать как изменение корневого коммита с R
на X
и отсечение графика перед X
.
Вот иллюстрация, упрощающая граф фиксации:
R ---- I want to squash from here...
|
A
|\
B C
| |
D E
| |\
F G H
| |/
I J
|\ \
K L M
|/ |
N /
|/
O
|
X ---- to here.
|
P
|
Q
|
Y
Сокращение всего с помощью обычной перебазировки потребует повторного разрешения нескольких коммитов слияния. Я знаю о git rerere, но не знаю, как его использовать в этой ситуации. Он не был включен при совершении всего этого.
Это можно сделать с помощью перебазирования, но гораздо проще сделать вручную.
git checkout --orphan temp X
# now you are on a brand new branch with no history, and your working tree is just like X
git commit -m "Single shot"
# now let's carry over X up to Y
git cherry-pick X..Y
Если вам нравится результат, установите свою ветку здесь и живите долго и счастливо.
@RomainValeri Разве это не раздавит не ту половину коммитов? Обратите внимание, что представленный график «перевернут» (по сравнению с тем, что показал бы git log --graph
).
Если я этого не сделал, значит, у @RomainValeri есть причина :-) График перевернут. Корень — R, а кончик — Y. Как дела?
@eftshift0 Это то, чего мне не хватало. Д'о. Направление диаграммы. Извините за шум.... ^^
Замечательная идея. Однако, похоже, это не работает как есть: option orphan requires a value
и -b, -B and --orphan are mutually exclusive
. Тем не менее, я поэкспериментирую с этой идеей.
Виноват. Попробуйте без -b
.
Все еще не работает, потому что --orphan
требует значения — вероятно, оно должно идти сразу после checkout
. Обновлено: git checkout --orphan X temp
работает!
По какой-то причине я получаю конфликты (конфликт слияния, изменение/удаление, переименование/переименование) при выборе вишни X..Y
из осиротевшей ветки, которая начинается с X
. Есть идеи, где я накосячил? Должно было быть (X+1)..Y
?
Нет... потому что X будет отброшен, это должно быть X..Y
. Вы уверены, что это прямая линия от X
до Y
?
В чем проблема с конфликтами?
Работает! Большое спасибо. Ранее конфликты, вероятно, были вызваны некоторым оставшимся мусором при экспериментировании со старым репо, таким как ветка с тем же именем, что и хэш коммита, но на свежем клоне все работает правильно.
Тем не менее, вот как сделать то, что просят, полностью бесконфликтно:
git status # Make sure you start with no untracked files.
git branch new_branch1 commit_R
git branch new_branch2 commit_Y
git switch new_branch1
git diff HEAD commit_X | git apply - # This makes the worktree identical to X, in
# effect "fast-forward merge" of R..X
git add .
git commit -m "Some message describing the R..X changes"
git rebase --onto new_branch1 commit_X new_branch2
git branch --delete new_branch1
# Now you're done, new_branch2 is now R'--X'--P'--Q'--Y'
# If you want to (forcibly) change the original branch that pointed to Y do the
# following:
git switch the_original_branch
git reset --hard new_branch2
git branch --delete new_branch2
Поскольку new_branch1
и commit_X
имеют одинаковый контент (сама цель команд diff...apply) при выполнении команды rebase, конфликтов не будет, потому что применение P..Y поверх любой из них абсолютно одинаково.
Спасибо за помощь и новый взгляд на проблему! Я понимаю общую идею этих команд, особенно трюк diff | apply
, но это решение не работает, потому что в репо есть некоторые нетекстовые активы: error: cannot apply binary patch to '(...).png' without full index line
и error: (...).png: patch does not apply
. Возможно, для его работы требуется дополнительная настройка. Другой ответ обходит это, создавая осиротевшую ветку, хотя я не знаю, сработает ли ваш ответ с ней вместо трюка diff | apply
.
Ах, бинарные файлы. Да, они не будут созданы с помощью apply
. Чтобы включить такие файлы, выберите коммит(ы), который вводит эти файлы (или последний коммит для изменения файлов) прямо перед выполнением diff | apply
. Возможно, выборки будут включать и текстовые файлы, но это не имеет значения (если это создает конфликты, вы можете выбрать простой выход и просто удалить их). Затем выполните diff | apply
, как описано. Затем вы получите промежуточные коммиты «добавить изображение», которые затем можно уменьшить до R'--I1--I2--I3--X'--P'--Q'--Y'
с помощью интерактивной перебазировки.
Почему бы просто не поставить
reset --soft X
и не повторить? Не могу поверить, что спрашиваю это у самого «мистера Reset Soft» :-D (и это настоящий вопрос, интересно, есть ли разница, которую я упускаю)