Сквош мій останній X здійснює разом за допомогою Git


3586

Як я можу скріплювати свої останні X зобов’язання разом в одну команду за допомогою Git?


12
Схожий питання: stackoverflow.com/questions/7275508 / ...
koppor


2
@matt TortoiseGit - ваш інструмент. Він надає єдину функцію "Об'єднати до однієї фіксації", яка автоматично викликатиме всі кроки у фоновому режимі. На жаль, доступний лише для Windows. Дивіться мою відповідь нижче.
Маттіас М

1
Для Защемлення ДО першої фіксації побачити це - stackoverflow.com/questions/1657017 / ...
goelakash

1
розміщувати сквош одну потребу зробити зусилля натискної stackoverflow.com/questions/10298291 / ...
vikramvi

Відповіді:


2092

Використовуйте git rebase -i <after-this-commit>та замініть "підбір" на другому та наступних фіксаціях на "сквош" або "фіксація", як описано в посібнику .

У цьому прикладі <after-this-commit>є або хеш SHA1, або відносне місце розташування від HEAD поточної гілки, з якого коміти аналізуються для команди rebase. Наприклад, якщо користувач бажає переглянути 5 команд з поточної HEAD в минулому, це команда git rebase -i HEAD~5.


260
Це, я думаю, відповідає на це питання трохи краще stackoverflow.com/a/5201642/295797
Roy Truelove

92
Що означає <after-this-commit>?
2540625

43
<after-this-commit>є фіксація X + 1, тобто батько найдавнішої комісії, яку ви хочете зробити сквош.
joozek

339
Я знайшов цю відповідь занадто лаконічною, щоб зрозуміти її однозначно. Приклад допоміг би.
Ian Ollmann

53
Різниця між цим rebase -iпідходом і reset --softполягає в тому, rebase -iщо я дозволяє утримувати автора прихильності, в той час як reset --softдозволяє мені повторно. Іноді мені потрібно розряджати запити на тягнення, зберігаючи інформацію про автора. Іноді мені потрібно скинути програмне забезпечення у власних програмах. В будь-якому разі підносить обидва чудові відповіді
zionyx

3826

Ви можете зробити це досить легко без git rebaseабо git merge --squash. У цьому прикладі ми стискаємо останні 3 коміти.

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

git reset --soft HEAD~3 &&
git commit

Якщо ви хочете почати редагувати нове повідомлення про фіксацію з приєднанням існуючих повідомлень про фіксацію (тобто подібним до того, з чого git rebase -iрозпочнеться список інструкцій вибору / сквош / сквош /… / сквош ), тоді вам потрібно витягнути ці повідомлення та пройти їх git commit:

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

Обидва ці способи розрізають останні три зобов’язання в одну нову команду однаковим чином. М'яке скидання просто перенаправляє HEAD до останнього комітету, який ви не хочете робити сквош. Ні індекс, ні робоче дерево не торкаються м'якого скидання, залишаючи індекс у бажаному стані для вашої нової фіксації (тобто він уже має всі зміни від комісій, які ви збираєтеся "викинути").


163
Га! Мені подобається цей метод. Саме він закриває дух проблеми. Шкода, що для цього потрібно стільки вуду. Щось подібне слід додати до однієї з основних команд. Можливо git rebase --squash-recent, чи навіть git commit --amend-many.
Адріан Ратнапала

13
@ABB: Якщо у вашій гілці є набір "вище за течією", ви, можливо, зможете використовувати її branch@{upstream}(або просто @{upstream}для поточної гілки; в обох випадках останню частину можна скоротити @{u}; див. Gitrevision ). Це може відрізнятися від вашої "останньої поштовху" (наприклад, якщо хтось інший штовхнув щось, що склалося на вашому останньому натиску, а потім ви це зробили), але здається, що це може бути близьким до того, що ви хочете.
Кріс Джонсен

104
Це свого роду вимагало від мене, push -fале в іншому випадку це було чудово, дякую.
2rs2ts

39
@ 2rs2ts git push -f звук небезпечний. Слідкуйте лише за тим, щоб скинути місцеві коміти. Ніколи не торкайтеся натиснутих комітетів!
Маттіас М

20
Мені також потрібно скористатися git push --forceзгодом, щоб взяти на себе зобов’язання
Зах Сосьє,

738

Можна використовувати git merge --squashдля цього, що трохи елегантніше, ніж git rebase -i. Припустимо, ви перебуваєте на майстру, і ви хочете збити останні 12 передач в одну.

ПОПЕРЕДЖЕННЯ. Спочатку переконайтеся, що ви git statusвиконуєте свою роботу - переконайтеся, що вона чиста (оскільки git reset --hardвикинете поетапні та нестандартні зміни)

Тоді:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

Документаціяgit merge описує --squashваріант більш докладно.


Оновлення: єдина реальна перевага цього методу перед більш простим, git reset --soft HEAD~12 && git commitзапропонованим Крісом Джонсоном у своїй відповіді, - це те, що ви отримуєте повідомлення про фіксацію, яке популяризується кожним повідомленням про фіксацію, яке ви стискаєте.


16
Ви кажете, що це "елегантніше", ніж git rebase -iви, але не даєте причини. Орієнтовно - це це, тому що мені здається, що насправді все навпаки, і це хакер; ви не виконуєте більше команд, ніж потрібно, тільки щоб змусити git mergeвиконувати одну з речей, git rebaseспеціально розроблених для цього?
Марк Амері

76
@ Марк Амери: Є різні причини, які я сказав, що це більш елегантно. Наприклад, це не передбачає зайвого нерестування редактора, а потім пошуку та заміни рядка у файлі "завдання". Використання git merge --squashтакож простіше використовувати в сценарії. По суті, міркування полягали в тому, що для цього вам взагалі не потрібна "інтерактивність" git rebase -i.
Марк Лонгейр

12
Ще одна перевага полягає в тому, що git merge --squashменша ймовірність створювати конфлікти злиття перед переміщенням / видаленням / перейменовуванням у порівнянні з повторним використанням, особливо якщо ви з’єднуєтесь з місцевою гілкою. (відмова від відповідальності: ґрунтуючись лише на одному досвіді, виправте мене, якщо це не так у загальному випадку!)
Cheezmeister

2
Я завжди дуже неохочий, коли справа доходить до жорстких перезавантажень - я б використовував тимчасовий тег замість того, HEAD@{1}щоб просто бути на безпечній стороні, наприклад, коли ваш робочий процес на годину переривається відключенням електроенергії тощо.
Тобіас Кіенцлер,

8
@BT: Знищено ваше зобов’язання? :( Я не впевнений, що ви маєте на увазі під цим. Все, що ви зробили, ви легко зможете повернутися до рефлогу git. Якщо ви не працювали, але файли були інсценовані, ви все одно зможете отримати їх вміст назад, хоча це буде більше роботи . Однак, якщо ваша робота навіть не була поставлена, проте, я боюся, що зробити це мало, тому відповідь говорить наперед: "Спочатку перевірте, чи статус git чистий (оскільки скидання git - Hard викине поетапні та нестандартні зміни) ".
Марк Лонгейр

218

Я рекомендую уникати, git resetколи це можливо - особливо для Git-новачків. Якщо вам справді не потрібно автоматизувати процес на основі ряду комітетів, існує менш екзотичний спосіб ...

  1. Покладіть комбіновані коміти на робочу гілку (якщо їх ще немає) - використовуйте для цього gitk
  2. Ознайомтеся з цільовою гілкою (наприклад, 'master')
  3. git merge --squash (working branch name)
  4. git commit

Повідомлення про фіксацію буде популярним на основі сквош.


4
Це найбезпечніший метод: не застосовується скидання м'якого / жорсткого (!!) або відхилення!
TeChn4K

14
Було б чудово, якби розширити на (1).
Адам

2
@Adam: В основному це означає використовувати інтерфейс GUI, gitkщоб позначити рядок коду, який ви сквош, а також позначити основу, на яку потрібно сквош. У звичайному випадку обидві ці мітки вже будуть існувати, тому крок (1) можна пропустити.
nobar

3
Зауважте, що цей метод не відзначає робочу гілку як повністю об'єднану, тому її видалення вимагає примусового видалення. :(
Kyrstellaine

2
Для (1) я знайшов git branch your-feature && git reset --hard HEAD~Nнайзручніший спосіб. Однак він знову включає скидання git, чого ця відповідь намагалася уникнути.
eis

132

На основі відповіді Кріса Джонсена ,

Додати глобальний псевдонім "сквош" від bash: (або Git Bash для Windows)

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

... або за допомогою командного рядка Windows:

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Тепер ви ~/.gitconfigповинні містити цей псевдонім:

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Використання:

git squash N

... Що автоматично з’єднує останні Nвчинення, включно.

Примітка. Отримане повідомлення про фіксацію - це комбінація всіх скорочених комісій, по порядку. Якщо ви цим не задоволені, ви завжди можете git commit --amendїх змінити вручну. (Або відредагуйте псевдонім відповідно до ваших смаків.)


6
Цікаво, але я б краще скористатись самим повідомленням про скошене скоєння як описовий підсумок моїх декількох комісій, ніж для того, щоб це було автоматично введено для мене. Тож я б краще вказав git squash -m "New summary."і Nвизначив автоматично як кількість непущених комітетів.
Acumenus

1
@ABB, Це звучить як окреме запитання. (Я не думаю, що це саме те, що просили ОП; я ніколи не відчував потреби в цьому в своєму робочому процесі на сквош з git.)
EthanB

3
Це досить солодко. Особисто мені хотілося б версії, яка використовує повідомлення про фіксацію з першого з об'єднаних комітетів. Було б добре для таких речей, як виправлення пробілів.
лінійка

@funroll Погоджено. Просто скидання останнього msg - це звичайна потреба для мене. Ми мусимо це спромогти ...
Стів Клей

2
@ABB ви можете використовувати git commit --amendдля подальшої зміни повідомлення, але цей псевдонім дозволяє вам добре почати те, що повинно бути в повідомленні про фіксацію.
тире

131

Завдяки цій зручній публікації в блозі я виявив, що ви можете скористатися цією командою, щоб скинути останні 3 коміти:

git rebase -i HEAD~3

Це зручно, оскільки працює навіть у місцевому відділенні, де немає інформації про відстеження / віддаленого репо.

Команда відкриє інтерактивний редактор баз даних, який потім дозволяє перевпорядкувати, сквош, повторно змінити тощо, як правило.


Використання інтерактивного редактора баз даних:

Інтерактивний редактор бази даних показує три останні коміти. Це обмеження визначалося HEAD~3при виконанні команди git rebase -i HEAD~3.

Найновіша фіксація, HEADвідображається спочатку в рядку 1. Рядки, що починаються з а, - #це коментарі / документація.

Відображена документація досить чітка. У будь-якому заданому рядку ви можете змінити команду pickна команду на ваш вибір.

Я вважаю за краще використовувати команду, fixupоскільки ця команда "стискає" зміни комісії в команді на рядку вище та відкидає повідомлення комісії.

Оскільки фіксація за рядком 1 є HEAD, у більшості випадків ви залишаєте це як pick. Ви не можете використовувати squashабо fixupяк немає іншого зобов’язання, щоб скосити комітку.

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

інтерактивний редактор бази даних


Практичний щоденний приклад

Нещодавно я здійснив нову функцію. З тих пір я здійснив два виправлення помилок. Але тепер я виявив помилку (чи, можливо, лише орфографічну помилку) у новій функції, яку я вчинив. Як надокучливо! Я не хочу, щоб нова комісія, що забруднює мою історію фіксації!

Перше, що я роблю, - це виправити помилку і зробити нове зобов’язання з коментарем squash this into my new feature!.

Потім я запускаю git logабо gitkотримую команду SHA нової функції (у цьому випадку 1ff9460).

Далі я запускаю інтерактивний редактор ребатів git rebase -i 1ff9460~. ~Після фіксації ША каже редактор , щоб включити цей Комміт в редакторі.

Далі я переміщую комітку, що містить fix ( fe7f1e0), під функцію фіксації функції та змінюю pickна fixup.

Закриваючи редактор, виправлення потрапить у фіксацію функції, і моя історія фіксування буде виглядати приємно і чисто!

Це добре працює, коли всі комісії локальні, але якщо ви спробуєте змінити будь-які комісії, які вже натиснуті на пульт, ви можете справді створити проблеми іншим розробникам, які перевірили ту саму гілку!

введіть тут опис зображення


5
чи потрібно вибирати верхню, а решту? Ви повинні відредагувати свою відповідь, щоб пояснити, як детальніше використовувати інтерактивний редактор
баз даних

2
Так, залиште pickв рядку 1. Якщо ви виберете squashабо виконуватимете fixupкоманду в рядку 1, git покаже повідомлення, що говорить "помилка: не можна" виправити "без попереднього зобов'язання". Тоді ви дасте можливість виправити це: "Ви можете виправити це за допомогою" git rebase --edit-todo ", а потім запустіть" git rebase --continue "." або ви можете просто перервати і почати спочатку: "Або ви можете перервати ребазу за допомогою" git rebase --abort "."
br3nt

56

Якщо ви використовуєте TortoiseGit, ви можете виконувати функцію Combine to one commit:

  1. Відкрийте контекстне меню TortoiseGit
  2. Виберіть Show Log
  3. Позначте відповідні комісії у вікні журналу
  4. Виберіть Combine to one commitу контекстному меню

Поєднайте коміти

Ця функція автоматично виконує всі необхідні одиничні дії git. На жаль, доступно лише для Windows.


Наскільки мені відомо, це не спрацює для об'єднань.
Торкіл Холм-Якобсен

1
Хоча це не коментується жодними іншими, це працює навіть для комітетів, яких немає в ГОЛАДІ. Наприклад, моя потреба полягала в тому, щоб скасувати деякі WIP-зобов’язання, які я зробив з більш здоровим описом, перш ніж натискати. Працювали красиво. Звичайно, я все ще сподіваюся, що зможу навчитися робити це командами.
Чарльз Роберто Канато

55

Для цього можна скористатися наступною командою git.

 git rebase -i HEAD~n

n (= 4 тут) - кількість останніх комітів. Потім у вас є наступні варіанти,

pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....

Оновіть на зразок нижче pickоднієї комісії, а squashінші - до останнього

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

Для детальної інформації натисніть посилання


48

Виходячи з цієї статті, я знайшов цей спосіб простішим для моєї справи.

Моя гілка «dev» випередила «походження / dev» на 96 комітетів (тому ці комісії ще не були висунуті у віддалений).

Мені хотілося розкласти ці зобов’язання до одного, перш ніж натиснути на зміни. Я вважаю за краще повернути гілку до стану "origin / dev" (це дозволить залишити всі зміни з 96 комісій без змін), а потім здійснити зміни відразу:

git reset origin/dev
git add --all
git commit -m 'my commit message'

1
Тільки те, що мені було потрібно. Збиваємо коміти з моєї гілки функцій, і тоді я збираю вишневий вибір, який здійснюють у свого господаря.
Девід Віктор

1
Це не скорочує попередні зобов’язання!
ІгорГанапольський

Ви могли б трохи додати це @igorGanapolsky?
Трудольф

3
@trudolf Це насправді не сквош (збір окремих зобов’язань на сквош). Це більше, щоб здійснити всі ваші зміни одразу.
ІгорГанапольський

15
так, отже, це розбиває всі ваші зобов’язання в одне ціле. поздоровлення!
трудольф

45

У галузі, яку ви хочете об'єднати, виконайте наступні дії:

git rebase -i HEAD~(n number of commits back to review)

приклад:

git rebase -i HEAD~1

Це відкриє текстовий редактор, і ви повинні переключити "вибирати" перед кожною коміткою на "сквош", якщо ви хочете, щоб ці зобов'язання були об'єднані разом. З документації:

p, забрати = використовувати накласти

s, сквош = використовувати фіксацію, але увімкнути попередній коміт

Наприклад, якщо ви хочете об'єднати всі комітети в одну, 'pick' - це перше зобов’язання, яке ви зробили, а всі майбутні (розміщені нижче першого) слід встановити на 'squash'. Якщо ви використовуєте vim, використовуйте : x у режимі вставки, щоб зберегти та вийти з редактора.

Потім продовжити ребауз:

git rebase --continue

Більш детально про цей та інші способи переписати історію фільмів дивіться у цій корисній публікації


2
Будь ласка, поясніть, що робить --continueта vim :x.
not2qubit

Ребазація відбудеться в блоках, коли вона проходить через коміти у вашій гілці, після того, як ви git addвстановите правильну конфігурацію у своїх файлах, яку ви використовуєте git rebase --continueдля переходу до наступного комітету та початку злиття. :xце одна команда, яка збереже зміни файлу при використанні vim дивіться це
aabiro

32

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

Крок 0: журнал git

Подивіться, де ви знаходитесь git log. Найголовніше - знайти хеш-код першої комісії, який ви не хочете робити сквош. Тож тільки:

введіть тут опис зображення

Крок 1: відновлення git

Виконай git rebase -i [your hash], у моєму випадку:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

Крок 2: виберіть / розімніть те, що ви хочете

У моєму випадку я хочу розігнати все, що було вперше вчасно. Впорядкування відбувається від першого до останнього, точніше так, як і в git log. У моєму випадку я хочу:

введіть тут опис зображення

Крок 3: Відкоригування повідомлень

Якщо ви вибрали лише одну фіксацію, а решту скосили, ви можете скорегувати одне повідомлення про фіксацію:

введіть тут опис зображення

Це воно. Коли ви збережете це ( :wq), ви закінчите. Погляньте на це git log.


2
Було б добре бачити кінцевий результат, наприклад,git log
Тимофі Л.Ж. Стюарт

2
Ні, дякую. Саме так я втрачаю свої зобов'язання.
Axalix

@Axalix Ви видалили всі свої лінії? Ось так ви втрачаєте свої зобов'язання.
a3y3

31

Процедура 1

1) Визначте короткий хеш для фіксації

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

Тут навіть git log --onelineможна використовувати короткі хеші.

2) Якщо ви хочете скосити (злити) останні два фікси

# git rebase -i deab3412 

3) Це відкриває nanoредактор для об'єднання. І це виглядає як нижче

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4) Перейменувати слово pickв squashякий присутній перед тим abcd1234. Після перейменування воно повинно бути як нижче.

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5) Тепер збережіть і закрийте nanoредактор. Натисніть ctrl + oта натисніть, Enterщоб зберегти. А потім натисніть, ctrl + xщоб вийти з редактора.

6) Потім nanoредактор знову відкриється для оновлення коментарів, при необхідності оновить його.

7) Тепер його вдало вдав, ви можете перевірити це, перевіривши журнали.

# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....

8) Тепер натисніть на репо. Зауважте, щоб додати +знак перед назвою філії. Це означає вимушений поштовх.

# git push origin +master

Примітка. Це засновано на використанні git on ubuntushell. Якщо ви використовуєте різні os ( Windowsабо Mac), то вищезгадані команди однакові, крім редактора. Ви можете отримати іншого редактора.

Порядок 2

  1. Спочатку додайте необхідні файли для фіксації
git add <files>
  1. Тоді фіксуємо, використовуючи --fixupваріант, і OLDCOMMITслід, на якому нам потрібно об'єднати (скріш) цей коміт.
git commit --fixup=OLDCOMMIT

Тепер це створює новий комітет поверх HEAD за допомогою fixup1 <OLDCOMMIT_MSG>.

  1. Потім виконайте команду нижче, щоб об'єднати (скоштувати) нову команду OLDCOMMIT.
git rebase --interactive --autosquash OLDCOMMIT^

Тут ^мається на увазі попереднє зобов’язання OLDCOMMIT. Ця rebaseкоманда відкриває інтерактивне вікно в редакторі (vim чи nano), на якому нам не потрібно нічого робити, просто збереження та вихід достатньо. Оскільки варіант, переданий цьому, автоматично перемістить останню фіксацію на наступну стару фіксацію та змінить операцію на fixup(еквівалентну сквош). Потім база даних продовжується і закінчується.

Процедура 3

  1. Якщо потрібно додати нові зміни до останнього, --amendслід скористатися засобом git-commit.
    # git log --pretty=oneline --abbrev-commit
    cdababcd Fix issue B
    deab3412 Fix issue A
    ....
    # git add <files> # New changes
    # git commit --amend
    # git log --pretty=oneline --abbrev-commit
    1d4ab2e1 Fix issue B
    deab3412 Fix issue A
    ....  

Тут --amendоб'єднуються нові зміни до останнього комісії cdababcdта генерується новий ідентифікатор комісії1d4ab2e1

Висновок

  • Перевага 1-ї процедури полягає в розбиванні декількох команд і переупорядкуванні. Але ця процедура буде складною, якщо нам потрібно об'єднати виправлення з дуже давнім коміксом.
  • Таким чином, 2-а процедура допомагає легко злити об'єкт із дуже старим.
  • І третя процедура є корисною у випадку, коли потрібно видалити нові зміни на останнє виконання.

Він оновлює лише останні два зобов’язання, навіть якщо я скинув на себе зобов’язання Id до 6-ої останньої комісії, не знаю, чому
Карлос Лю

Навіть ви можете змінити порядок здійснення фіксації. Це чудово працює.
rashok

29

Щоб розбити останні 10 комірок на 1 одиночний комітет:

git reset --soft HEAD~10 && git commit -m "squashed commit"

Якщо ви також хочете оновити віддалену гілку зі скошеним комітом:

git push -f

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

27

Якщо ви знаходитесь на віддаленій гілці (називається feature-branch), клонованій із золотого сховища ( golden_repo_name), то ось методика розбивати ваші комісії на одне:

  1. Оформити золоту репо

    git checkout golden_repo_name
    
  2. Створіть з нього нову гілку (золоте репо) наступним чином

    git checkout -b dev-branch
    
  3. Кабачкові злиття з місцевим відділенням, яке ви вже маєте

    git merge --squash feature-branch
    
  4. Введіть свої зміни (це буде єдине зобов’язання, яке входить у відділ розробки)

    git commit -m "My feature complete"
    
  5. Перемістіть гілку у ваше місцеве сховище

    git push origin dev-branch
    

Оскільки я просто скорочував ~ 100 коміт (для синхронізації svn гілок через git-svn), це набагато швидше, ніж інтерактивне відновлення!
шавлія

1
Читаючи вниз, я бачу коментар @ Кріса, що саме я раніше робив (перезавантажувати програмне забезпечення ...) - дуже погано, що stackoverflow вже не ставить відповідь із сотнями оновлень у верхній частині ...
мудрець

1
погоджуюся з тобою @sage, сподіваємось, що вони можуть це зробити колись у майбутньому
Sandesh Kumar

Це правильний шлях. Підхід до бази даних хороший, але його слід використовувати лише для сквош, як крайнього рішення.
Аксалікс

20

Що може бути справді зручним:
скажіть, скажіть, хеш, який ви хочете зробити скріш d43e15.

Тепер використовуйте

git reset d43e15
git commit -am 'new commit name'

2
Це. Чому більше людей не використовують це? Це набагато швидше, ніж звільнення та скорочення окремих доручень.
a3y3

17

Це супер-пупер безглуздо, але якось круто, тому я просто кину його на ринг:

GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

Переклад: надайте новий "редактор" для git, який, якщо ім'я файлу для редагування git-rebase-todo(інтерактивне запит на оновлення), змінює всі, крім першого "вибору", на "сквош", інакше породжує vim - так що, коли вам буде запропоновано щоб відредагувати стиснене повідомлення про фіксацію, ви отримаєте vim. (І, очевидно, я скорочував останні п’ять комітетів на foo, але ви можете змінити це, як би вам не подобалось.)

Я, мабуть, зробив те, що запропонував Марк Лонгейр .


7
+1: це весело та повчально, оскільки мені зовсім не очевидно, що ви можете розмістити щось складніше, ніж ім’я програми у змінній середовища GIT_EDITOR.
Марк Лонгейр

16

Якщо ви хочете скосити кожну комісію в одну команду (наприклад, при першому випуску проекту публічно), спробуйте:

git checkout --orphan <new-branch>
git commit

14

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

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

Тоді ви готові здійснити всі зміни.


14

2020 Просте рішення без поновлення:

git reset --soft HEAD~2

git commit -m "new commit message"

git push --force

2 означає, що два останні коміти будуть розбиті. Ви можете замінити його будь-яким номером


12

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

git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}

4
Мене розчарувало розчарування з приводу того, що вони роблять сквош, і як це нерозумно складно - просто effing використовує останнє повідомлення і розбиває їх на один вчинок! Чому так важко ???? Цей один лайнер робить це для мене. Дякую від щирого серця.
Локане

12

якщо ви, наприклад, хочете скасувати останні три комісії на одну команду у відділенні (віддаленому сховищі), наприклад: https://bitbucket.org

Що я і зробив

  1. git reset - програмна голова ~ 3 &&
  2. git фіксувати
  3. git push origin (назва гілки) - сила

3
Будьте обережні, оскільки якщо ви застосовуєте силу, тоді ви не можете отримати попередні зобов’язання, оскільки ви їх видалили
Альберт Руелан

12

⚠️ ПОПЕРЕДЖЕННЯ: "Мої останні X зобов'язання" можуть бути неоднозначними.

  (MASTER)  
Fleetwood Mac            Fritz
      ║                    ║
  Add Danny  Lindsey     Stevie       
    Kirwan  Buckingham    Nicks                                              
      ║         ╚═══╦══════╝     
Add Christine       ║          
   Perfect      Buckingham
      ║           Nicks            
    LA1974══════════╝                                    
      ║                  
      ║                  
    Bill <══════ YOU ARE EDITING HERE
  Clinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)              

У цій дуже скороченій історії сховища https://github.com/fleetwood-mac/band-history ви відкрили запит на приєднання до об'єднання Білла Клінтона в оригінальний ( MASTER) комітет Fleetwood Mac.

Ви відкрили запит на притягнення, і на GitHub ви бачите це:

Чотири коміти:

  • Додати Денні Кірвана
  • Додати Christine Perfect
  • LA1974
  • Білл Клінтон

Думаючи, що ніхто ніколи не потурбується прочитати повну історію сховища. (Насправді є сховище, натисніть на посилання вище!) Ви вирішили зменшити ці комісії. Тож ти йдеш і біжиш git reset --soft HEAD~4 && git commit. Потім ви перейдете git push --forceна GitHub, щоб прибрати свій PR.

А що відбувається? Ви щойно взяли на себе зобов’язання, які дісталися від Фріца до Білла Клінтона. Тому що ви забули, що вчора працювали над версією проекту «Букінгемський Нікс». І git logне відповідає тому, що ви бачите на GitHub.

🐻 МОРАЛ ІСТОРІЇ

  1. Знайти точні файли , які ви хочете отримати в , і git checkoutїх
  2. Знайдіть точне попереднє зобов’язання, яке ви хочете зберегти в історії, і git reset --softце
  3. Зробіть git commitщо перекоси безпосередньо від від до до

1
Це на 100% найпростіший спосіб зробити це. Якщо ваша поточна ГОЛОВА є потрібним станом, ви можете пропустити №1.
Стен

Це єдиний спосіб, який я знаю, що дозволяє переписати історію перших фіксованих файлів.
Vadorequest

9

Якщо вам не байдуже повідомлення про фіксацію проміжних комітетів, ви можете використовувати

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend

7

Якщо ви працюєте з GitLab, ви можете просто натиснути параметр сквош в запиті на об'єднання, як показано нижче. Повідомлення про фіксацію буде назвою Запити на об'єднання.

введіть тут опис зображення


6
git rebase -i HEAD^^

де число ^ 's дорівнює X

(у такому випадку розбийте два останні зобов’язання)


6

На додаток до інших відмінних відповідей, я хотів би додати, як git rebase -iзавжди плутає мене з порядком виконання зобов’язань - старішим до більш новим чи навпаки? Отже, це мій робочий процес:

  1. git rebase -i HEAD~[N], де N - кількість комітетів, які я хочу приєднати, починаючи з останнього . Отже, git rebase -i HEAD~5це означало б "скинути останні 5 приєднань до нового";
  2. редактор вискакує, показуючи список комітетів, які я хочу об'єднати. Тепер вони відображаються у зворотному порядку : старша фіксація зверху. Позначте як "сквош" або "с" всі зобов’язання там, крім першого / старшого : він буде використовуватися як відправна точка. Збережіть і закрийте редактор;
  3. редактор знову з'являється з повідомленням за замовчуванням для нової фіксації: змініть його на свої потреби, збережіть і закрийте. Сквош завершено!

Джерела та додаткові читання: №1 , №2 .


6

А як відповідь на запитання, пов'язане з таким робочим процесом?

  1. багато локальних комітів, змішаних з кількома злиттями ВІД головного ,
  2. нарешті, поштовх до віддаленого,
  3. PR та об'єднати TO master за допомогою рецензента . (Так, розробнику було б простіше merge --squashпісля PR, але команда думала, що це сповільнить процес.)

Я не бачив подібного робочого процесу на цій сторінці. (Це можуть бути мої очі.) Якщо я rebaseправильно розумію , багаторазове злиття потребує декількох конфліктів . Я НЕ хочу навіть думати про це!

Отже, це, здається, працює для нас.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. редагуйте та виконайте багато місцевих дій, регулярно зливайте майстра
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // ставить усі зміни на стадії
  7. git commit 'one message to rule them all'
  8. git push
  9. Рецензент робить PR і зливається для освоєння.

З багатьох думок мені подобається ваш підхід. Це дуже зручно і швидко
Артем Соловев

5

Я вважаю, що більш загальним рішенням є не вказівка ​​комітетів "N", а скоріше гілка / ідентифікатор-фіксація, яку ви хочете скасувати на вершині. Це менш схильне до помилок, ніж підрахунок комітетів до певної фіксації - просто вкажіть тег безпосередньо, або якщо ви дійсно хочете рахувати, можете вказати HEAD ~ N.

У своєму робочому процесі я запускаю філію, і моє перше вчинення цієї гілки підсумовує мету (тобто зазвичай це те, що я буду надсилати як остаточне повідомлення для функції до загальнодоступного сховища.) Отже, коли я закінчу, все Я хочу зробити, це git squash masterповернутися до першого повідомлення, і тоді я готовий просувати.

Я використовую псевдонім:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i

Це дозволить скинути історію, яка пережита, перш ніж це зробити - це дає вам можливість відновити, захопивши старий ідентифікатор комісії з консолі, якщо ви хочете відновити. (Користувачі Solaris відзначають, що він використовує -iопцію GNU sed ; користувачі Mac та Linux мають бути добре з цим.)


Я спробував псевдонім, але не впевнений, чи замінить сед будь-який ефект. Що їм робити?
дощ

Перший sed просто скидає історію на консоль. Другий sed замінює весь 'pick' на 'f' (виправлення) і переписує файл редактора на місце (опція -i). Тож другий виконує всю роботу.
Етан

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

Привіт Етане, я хотів би знати, чи приховає цей робочий процес можливі конфлікти при злитті. Тому, будь ласка, врахуйте, чи є у вас два гілки господар і раб. Якщо у раба є конфлікт з господарем, і ми використовуємо, git squash masterколи нас перевіряють на раба. що це станеться? чи приховатимемо конфлікт?
Серхіо Білелло

@Sergio Це випадок переписування історії, тому у вас, ймовірно, виникнуть конфлікти, якщо ви скасуєте комітети, які вже були висунуті, а потім спробуйте об'єднати / відновити повторно скасовану версію. (Деякі тривіальні випадки можуть уникнути цього.)
Етан

5

Під питанням може бути неоднозначно, що означає "останній".

наприклад, git log --graphвиводить такі (спрощені):

* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

Тоді останніми комісіями часу є H0, злиття, B0. Для їх розкочування вам доведеться перезавантажити об'єднану гілку на фіксацію H1.

Проблема полягає в тому, що H0 містить H1 і H2 (і, як правило, більше комісій перед об'єднанням і після розгалуження), а B0 - ні. Тож вам доведеться керувати змінами з H0, merge, H1, H2, B0 принаймні.

Можна використовувати rebase, але по-різному, ніж в інших згаданих відповідях:

rebase -i HEAD~2

Це покаже вам варіанти вибору (як зазначено в інших відповідях):

pick B1
pick B0
pick H0

Покладіть сквош замість вибору на H0:

pick B1
pick B0
s H0

Після збереження та виходу ребаза застосовуватиме по черзі комісії після H1. Це означає, що він попросить вас вирішити конфлікти знову (де HEAD спочатку буде H1, а потім накопичує комісії під час їх застосування).

Після завершення роботи бази даних ви можете вибрати повідомлення для розбитого H0 та B0:

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

PS Якщо ви просто виконали скидання до BO: (наприклад, використання reset --mixedцього пояснюється детальніше тут https://stackoverflow.com/a/18690845/2405850 ):

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

тоді ви пригнічуєте B0 зміни H0, H1, H2 (втрачаючи цілком зобов'язані зміни після розгалуження і перед об'єднанням.


4

1) скидання git - програмне забезпечення HEAD ~ n

n - Кількість комірок, потрібно сквош

2) git commit -m "повідомлення нового фіксації"

3) походження git push-відділення_файлу - сила

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.