Як я можу повернути декілька Git-комітів (вже надісланих) до опублікованого сховища?


77

Нове у git, і вже заплутане.

Я здійснив деякі зміни на віддаленій машині розробника. Мені потрібно відновити стару версію, але продовжуйте робити "поганий прогрес", щоб продовжувати працювати в окремій гілці;

Я думав зробити це так:

  1. Створіть локальну гілку з назвою: "перевірена річ"
  2. Повернути місцеве сховище до стану, де він працював (сподіваємось, значущі коміти допоможуть) ;
  3. Натисніть на пульт

  4. закінчити тести на випробуваній речі

  5. Об’єднайте "перевірену річ" у розробник
  6. Натисніть на пульт

Між кроками 3 і 5 інші розробники можуть виконувати дії та натискати, і я боюся, що це може призвести до "трагедії злиття". У будь-якому випадку, чи може це бути правильним шляхом?

ОНОВЛЕННЯ:

Основна проблема тут полягає в 2)

Тут, за темою: "Розбиття роботи на гілку теми" http://learn.github.com/p/undoing.html

Вони пропонують:

  1. $ git тест гілки
  2. $ git reset --hard a6b4c974

Роблячи це, інші розробники все ще могли:

$ git commit (на гілці розробника)

і я можу оформити замовлення, щоб протестувати та розробити це до часу злиття .

Незважаючи на всі ваші варіанти, це схоже на гарний підхід для наслідування. Однак не зазначено, чи можна це робити після того, як ми натискаємо?

Будь ласка, зверніть увагу на наступне: Оскільки я вніс ці зміни і заплутав усе, ніхто досі не працював над сховищем . Отже, якщо я поверну робочий каталог, ніхто цього не помітить.


Як коментар ви не можете скасувати коміт злиття, перевірте цю корисну відповідь stackoverflow.com/a/1470452/6891549 та git checkout -f A -. рішення
Родріго

Відповіді:


147

Проблема

Існує ряд робочих процесів, якими ви можете скористатися. Головне - не ламати історію в опублікованій гілці, якщо ви не спілкувались з усіма, хто міг споживати гілку і готові робити операцію на клонах усіх. Краще цього не робити, якщо ви можете цього уникнути.

Рішення для опублікованих філій

Ваші окреслені кроки мають гідність. Якщо вам потрібно, щоб гілка dev відразу була стабільною, зробіть це так. У вас є ряд інструментів для налагодження за допомогою Git, які допоможуть вам знайти потрібну точку гілки, а потім ви зможете повернути всі коміти між вашим останнім стабільним комітом та HEAD.

Або поверніть коміти по черзі, у зворотному порядку, або використовуйте <first_bad_commit>..<last_bad_commit>діапазон. Хеші - це найпростіший спосіб вказати діапазон коміту, але є й інші позначення. Наприклад, якщо ви натиснули 5 поганих комітів, ви можете повернути їх за допомогою:

# Revert a series using ancestor notation.
git revert --no-edit dev~5..dev

# Revert a series using commit hashes.
git revert --no-edit ffffffff..12345678

Це буде застосовувати зворотні патчі до вашого робочого каталогу послідовно, працюючи назад до вашого відомого-доброго коміту. З прапором --no-edit зміни у вашому робочому каталозі будуть автоматично зафіксовані після застосування кожного сторнутого виправлення.

Перегляньте man 1 git-revertдодаткові параметри та man 7 gitrevisionsрізні способи вказівки комітів, які потрібно скасувати.

Крім того, ви можете розгалужити свою ГОЛОВУ, виправити речі такими, якими вони повинні бути, і повторно об’єднати. Тим часом ваша збірка буде порушена, але це може мати сенс у деяких ситуаціях.

Зона небезпеки

Звичайно, якщо ви абсолютно впевнені, що ніхто не витягував з сховища після вашого поганого натискання, і якщо пульт - це просто сховище , тоді ви можете зробити фіксацію не вперед.

git reset --hard <last_good_commit>
git push --force

Це призведе до того, що перезапис не зміниться у вашій системі та на хості, що перебуває вгорі, але ваші погані коміти зникнуть із історії, доступної безпосередньо, і не поширюватимуться на тяги. Ваші старі зміни будуть висіти, доки сховища не будуть обрізані, але лише ніндзя Git зможуть бачити або відновлювати коміти, зроблені вами помилково.


Якщо люди вже клонували погані поштовхи і отримували повідомлення - що їм робити, щоб мати таку саму гілку, як у пульті дистанційного керування? Чи повинні вони робити те саме git reset --hard <last_good_commit>?
andrybak

5
Якщо ви використовуєте теги <first_bad_commit><last_bad_commit><last_good_commit><last_bad_commit>
комітів

32

Якщо ви вже перенесли речі на віддалений сервер (і у вас є інші розробники, які працюють над тією ж віддаленою гілкою), важливо пам’ятати, що ви не хочете переписувати історію

Не використовуйте git reset --hard

Вам потрібно скасувати зміни, інакше будь-яка перевірка, яка має в історії видалені коміти, додасть їх назад у віддалене сховище наступного разу, коли вони натиснуть; і будь-яка інша каса притягне їх до наступного потягу після цього.

Якщо ви не натискали зміни на пульт, ви можете використовувати

git reset --hard <hash>

Якщо є підштовхнули зміни, але впевнені , що ніхто не витягнув їх , ви можете використовувати

git reset --hard
git push -f

Якщо ви внесли зміни, і хтось втягнув їх у свою касу, ви все одно можете це зробити, але іншому члену команди / касі потрібно буде співпрацювати:

(you) git reset --hard <hash>
(you) git push -f

(them) git fetch
(them) git reset --hard origin/branch

Але загалом це перетворюється на безлад. Отже, повернення:

Комітети на видалення є останніми

Це, мабуть, найпоширеніший випадок, ви щось зробили - виштовхнули їх, а потім зрозуміли, що вони не повинні існувати.

Спочатку вам потрібно визначити коміт, до якого ви хочете повернутися, це можна зробити за допомогою:

git log

просто шукайте коміт перед вашими змінами та зверніть увагу на хеш коміту. Ви можете обмежити журнал найбільш повторними комітами, використовуючи -nпрапор:git log -n 5

Потім скиньте свою гілку до стану, який ви хочете бачити вашим іншим розробникам:

git revert  <hash of first borked commit>..HEAD

Останній крок - створити свою власну локальну гілку, повторно застосувавши скасовані зміни:

git branch my-new-branch
git checkout my-new-branch
git revert <hash of each revert commit> .

Продовжуйте працювати, my-new-branchпоки не закінчите, а потім об’єднайте його з основною галуззю розвитку.

Комітети для видалення змішуються з іншими комітами

Якщо коміти, які ви хочете скасувати, не всі разом, можливо, найпростіше повернути їх окремо. Знову використовуючи git logзнайдіть коміти, які ви хочете видалити, а потім:

git revert <hash>
git revert <another hash>
..

Потім знову створіть свою гілку для продовження роботи:

git branch my-new-branch
git checkout my-new-branch
git revert <hash of each revert commit> .

Знову ж таки, зламайте і об’єднайтеся, коли закінчите.

У вас має вийти історія комітів, яка виглядає приблизно так my-new-branch

2012-05-28 10:11 AD7six             o [my-new-branch] Revert "Revert "another mistake""
2012-05-28 10:11 AD7six             o Revert "Revert "committing a mistake""
2012-05-28 10:09 AD7six             o [master] Revert "committing a mistake"
2012-05-28 10:09 AD7six             o Revert "another mistake"
2012-05-28 10:08 AD7six             o another mistake
2012-05-28 10:08 AD7six             o committing a mistake
2012-05-28 10:05 Bob                I XYZ nearly works

Кращий спосіб®

Тим більше, що тепер, коли ви усвідомлюєте небезпеку кількох розробників, що працюють в одній галузі, розгляньте можливість використання гілок функцій завжди для своєї роботи. Все це означає працювати у гілці, поки щось не закінчено, і лише потім об’єднувати це з основною гілкою. Також розгляньте можливість використання таких інструментів, як git-flow, для послідовної автоматизації створення гілок.


+1 за git resetбез прапора --hard, але я не думаю, що більшість людей зрозуміють, що ви скидаєте як робочий каталог, так і HEAD, що призведе до ! [rejected] master -> master (non-fast-forward)натискання. Крім того, якщо багато файлів було змінено, це створює по-справжньому широкий патч, навіть якщо ви примушуєте коміт.
Тодд А. Джейкобс,

@ AD7Six: Дякуємо за додаток, зареєстрований торговою маркою Better Way. Насправді, гілки настільки дешеві, що я, мабуть, відразу після цього дурного кроку. Знаючи, що ніхто інший, як не торкається сховища, крім мене, оскільки я зробив всю цю халепу, може допомогти застосувати підхід до оновлення мого запитання?
MEM

@CodeGnome ти маєш рацію, так давно я опинився в цій позиції:]
AD7six

@MEM не має значення, якщо ніхто інший не оновлював репо - важливо, чи хтось оновив свою оплату з моменту вчинення "помилок". Тому що, якщо вони мають, коли вони витягують, їх замовлення все одно міститиме коміти, які ви примусово видалили, об’єднані з будь-якими новими змінами. Якщо ви використовуєте git reset --hardу своїй гілці функцій, коли ви об’єднаєте її, ви, швидше за все, отримаєте обмеження, оскільки видалені коміти отримують повторне застосування - в основному, ні, це, мабуть, не є гарною ідеєю робити це.
AD7six,

-1
git revert HEAD -m 1

У наведеному вище рядку коду. "Останній аргумент представляє"

  • 1 - повертає один коміт. 2 - повертає останні коміти. n - повертає останні n комітів

або

git reset --hard siriwjdd


1
викидає помилку: коміт a17dc423957324b2b537c1caa9d9476c9b443f48 не має батьківського n, де n є останнім аргументом
MyrionSC2

Код працює, якщо HEAD є комітом злиття. Але опис того, що це робить, є неправильним. -mна самому ділі визначає , який батько робить це з м ainline галузі. -m 2поверне вас до версії на кінчику вашої гілки функцій і відкине всі зміни основної лінії, оскільки ваша гілка функції відхилилася від основної.
Хаїм Лейб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.