Відповіді:
Використання git rebase
. Це узагальнена команда "взяти фіксацію (-ла) та спланувати її / їх на іншій батьківській (базовій)" команді в Git.
Однак слід знати кілька речей:
Оскільки присвоєння SHA залучає своїх батьків, коли ви змінюєте батька певного комітету, його SHA змінюватиметься - як і SHAs усіх зобов’язань, які вступають після нього (новіші за нього) у лінію розвитку.
Якщо ви працюєте з іншими людьми, і ви вже підштовхнули спірний комітет до місця, де вони його перетягнули, модифікація комісії, ймовірно, погана ідея ™. Це пов’язано з №1, і, таким чином, виникає плутанина у сховищах інших користувачів, коли вони намагатимуться з’ясувати, що сталося через те, що ваші SHA більше не відповідають їхнім "цим" комісіям. (Докладні відомості див. У розділі "ВІДНОСНЕННЯ З ВИПУСКУВАННЯ СКОРОСТІ" на пов'язаній сторінці підручника.)
Це означає, що якщо ви зараз перебуваєте у відділенні з деякими зобов’язаннями, які потрібно перейти до нового батька, це виглядатиме приблизно так:
git rebase --onto <new-parent> <old-parent>
Це перемістить все після того, як<old-parent>
у поточній гілці <new-parent>
замість цього буде сидіти .
--onto
використовується! Документація для мене завжди була абсолютно незрозумілою, навіть після прочитання цієї відповіді!
git rebase --onto <new-parent> <old-parent>
Мені сказати, що про те, як використовувати rebase --onto
всі інші запитання та документи, які я прочитав, не вдалося.
rebase this commit on that one
або git rebase --on <new-parent> <commit>
. Використовувати старий батьків тут не має сенсу.
git rebase <new-parent>
.
Якщо виявиться, що вам потрібно уникати повторного використання наступних комітетів (наприклад, тому, що перезапис історії буде неможливим), ви можете використовувати заміну git (доступну в Git 1.6.5 і пізніших версіях).
# …---o---A---o---o---…
#
# …---o---B---b---b---…
#
# We want to transplant B to be "on top of" A.
# The tree of descendants from B (and A) can be arbitrarily complex.
replace_first_parent() {
old_parent=$(git rev-parse --verify "${1}^1") || return 1
new_parent=$(git rev-parse --verify "${2}^0") || return 2
new_commit=$(
git cat-file commit "$1" |
sed -e '1,/^$/s/^parent '"$old_parent"'$/parent '"$new_parent"'/' |
git hash-object -t commit -w --stdin
) || return 3
git replace "$1" "$new_commit"
}
replace_first_parent B A
# …---o---A---o---o---…
# \
# C---b---b---…
#
# C is the replacement for B.
Якщо встановлена вищезамінна заміна, будь-які запити на об’єкт B фактично повернуть об’єкт C. Вміст C точно такий же, як і вміст B, за винятком першого батька (ті самі батьки (крім першого), те саме дерево, те ж повідомлення про здійснення).
Заміни активні за замовчуванням, але їх можна ввімкнути, скориставшись --no-replace-objects
опцією git (перед назвою команди) або встановивши GIT_NO_REPLACE_OBJECTS
змінну середовища. Заміни можна розділити натисканням refs/replace/*
(крім звичайного refs/heads/*
).
Якщо вам не подобається зміна комісій (зроблено з sed вище), тоді ви можете створити свою заміну заміни, використовуючи команди вищого рівня:
git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -
Велика різниця полягає в тому, що ця послідовність не поширює додаткових батьків, якщо B є об'єктом об'єднання.
refs/replace/
ієрархію оновлень. Якщо вам потрібно зробити це один раз, ви можете зробити це git push yourremote 'refs/replace/*'
у вихідному сховищі та git fetch yourremote 'refs/replace/*:refs/replace/*'
в сховищах призначення. Якщо вам потрібно зробити це кілька разів, ви можете замість цього додати ці параметри до remote.yourremote.push
змінної конфігурації (у вихідному сховищі) та remote.yourremote.fetch
змінної конфігурації (у сховищах призначення).
git replace --grafts
. Не впевнений, коли це було додано, але вся його мета - створити заміну, яка є такою ж, як виконувати B, але з вказаними батьками.
Зауважте, що для зміни коміту в Git потрібно змінити всі зобов’язання, що слідують за ним. Це відлякує, якщо ви опублікували цю частину історії, і хтось, можливо, побудував би свою роботу над історією, яка була до змін.
Альтернативним рішенням, git rebase
згаданим у відповіді Амбер, є використання механізму трансплантатів (див. Визначення Git-трансплантатів у Глосарії Git та документація .git/info/grafts
файлу в документації щодо макета Git Repository ), щоб змінити батьківську команду, переконайтесь, що вона зробила виправлення з деяким переглядачем історії ( gitk
, git log --graph
тощо), а потім скористайтеся git filter-branch
(як описано в розділі "Приклади" своєї сторінки), щоб зробити його постійним (а потім видаліть трансплантат і, можливо, видаліть оригінали, підкріплені резервним git filter-branch
кодом, або повторно сховивши сховище):
echo "$ commit-id $ graft-id" >> .git / info / grafts git filter-branch $ graft-id..HEAD
ПРИМІТКА !!! Це рішення відрізняється від перебазування рішення в тому , що git rebase
б перебазуватися / пересадки зміни , в той час як трансплантати на основі рішення просто Reparent фіксацій як , не беручи до уваги відмінності між старим батьком і новим батьком!
git replace
витіснили щеплення щеплення (якщо припустити, що у вас git 1.6.5 або пізнішої версії).
git-replace
+ git-filter-branch
? У моєму тесті, filter-branch
здавалося, поважають заміну, випустивши все дерево.
git filter-branch
переписує історію з повагою до трансплантатів та замін (але в переписаній історії заміни будуть суперечливими); натискання призведе до зміни, які не рухаються вперед. git replace
створює замінні передавальні заміни; натискання буде швидким вперед, але вам потрібно буде натиснути, refs/replace
щоб перенести заміни, щоб виправити історію. HTH
Щоб уточнити наведені відповіді та безсоромно підключити власний сценарій:
Це залежить від того, хочете ви "перезавантажити" чи "повторно". Перебазуватися , як це було запропоновано Амбер , рухається навколо подивитися відмінності . Reparent , як це було запропоновано Якуба і Кріс , рухається навколо знімків з цілого дерева. Якщо ви хочете переглянути його, я пропоную використовувати, git reparent
а не робити роботу вручну.
Припустимо, у вас є зображення ліворуч, і ви хочете, щоб воно виглядало як зображення праворуч:
C'
/
A---B---C A---B---C
Як скидання, так і повторна редагування створюють однакову картину, але визначення C'
відрізняється. З git rebase --onto A B
, C'
не буде містити жодних змін, внесених користувачем B
. З git reparent -p A
, C'
буде ідентичним C
(крім того B
, що не буде в історії).
reparent
сценарієм; це прекрасно працює; настійно рекомендується . Я також рекомендую створити нову branch
мітку та до checkout
цієї гілки перед викликом (оскільки мітка гілки буде переміщуватися).
Однозначно @ відповідь Якуба допомогла мені кілька годин тому, коли я намагався точно так само, як і ОП.
Однак git replace --graft
зараз простіше рішення щодо трансплантатів. Крім того, головна проблема з цим рішенням полягала в тому, що фільтр-гілка змусив мене звільнити кожну гілку, яка не була об'єднана у гілку HEAD. Тоді, git filter-repo
робив роботу ідеально та бездоганно.
$ git replace --graft <commit> <new parent commit>
$ git filter-repo --force
Для отримання додаткової інформації: ознайомтеся з розділом "Історія повторного щеплення" в документах