Відповіді:
Використання 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
Для отримання додаткової інформації: ознайомтеся з розділом "Історія повторного щеплення" в документах