Як скріпати всі git прихильні в один?


480

Як ви стискаєте весь сховище до першого вибору?

Я можу відновитись до першого зобов’язання, але це дозволить мені отримати 2 коміти. Чи є спосіб посилатися на комітет перед першим?


8
"фіксація перед першою"?
innaM

31
@innaM - Це первинна фіксація, яка породила тугу . (Сподівається, що гумор проходить досить добре через переплетення).
ripper234

11
Для тих, хто пізніше піде на це питання, обов’язково скористайтеся більш сучасною відповіддю .
Droogans

1
Пов’язаний, але не дублікат ( --rootнасправді не найкраще рішення для скріплення всіх комітетів, якщо їх дуже багато для сквош): Об’єднайте перші два комітети сховища Git? .

2
Имо: це найкраще з @MrTux: stackoverflow.com/questions/30236694 / ... .
J0hnG4lt

Відповіді:


130

Мабуть, найпростіший спосіб - просто створити нове сховище з поточним станом робочої копії. Якщо ви хочете зберегти всі повідомлення про фіксацію, які ви могли спочатку зробити, git log > original.logа потім відредагувати це для свого початкового повідомлення про фіксацію у новому сховищі

rm -rf .git
git init
git add .
git commit

або

git log > original.log
# edit original.log as desired
rm -rf .git
git init
git add .
git commit -F original.log

62
але ви
втрачаєте

150
Git розвинувся з моменту отримання цієї відповіді. Ні, є більш простий і кращий спосіб: git rebase -i --root. Дивіться: stackoverflow.com/a/9254257/109618
Девід Дж.

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

3
Це жахливе рішення, яке зайво руйнує. Будь ласка, не використовуйте його.
Даніель Каміль Козар

5
також порушить підмодулі. -1
Крум

694

Станом на git 1.6.2 ви можете використовувати git rebase --root -i.

Для кожного вчинення, крім першого, змініть pickна squash.


49
Будь ласка, додайте повний робочий приклад команди, який відповідає на оригінальне запитання.
Джейк

29
Я хотів би прочитати це перед тим, як винести весь сховище, як сказано у прийнятій відповіді: /
Майк Чемберлен

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

20
@Pred Не використовувати squashдля всіх комітів . Першим треба бути pick.
Геерт

14
Якщо у вас багато комісій, важко змінити "вибір" на "сквош" вручну. Використовуйте :% s / pick / squash / g у командному рядку VIM, щоб зробити це швидше.
eilas

314

Оновлення

Я зробив псевдонім git squash-all.
Приклад використання : git squash-all "a brand new start".

[alias]
  squash-all = "!f(){ git reset $(git commit-tree HEAD^{tree} -m \"${1:-A new start}\");};f"

Caveat : не забудьте подати коментар, інакше буде використане повідомлення про фіксацію за замовчуванням "Новий початок".

Або ви можете створити псевдонім за допомогою наступної команди:

git config --global alias.squash-all '!f(){ git reset $(git commit-tree HEAD^{tree} -m "${1:-A new start}");};f'

Один лайнер

git reset $(git commit-tree HEAD^{tree} -m "A new start")

Примітка : тут " A new start" є лише прикладом, сміливо користуйтеся своєю мовою.

TL; DR

Не потрібно сквашувати, використовуйте git commit-treeдля створення сирітської комісії та йдіть з нею.

Поясніть

  1. створити єдиний комітет через git commit-tree

    Що git commit-tree HEAD^{tree} -m "A new start"таке:

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

    Вираз HEAD^{tree}означає дерево, що відповідає об'єкту HEAD, а саме кінчик вашої поточної гілки. див. Дерево-Об'єкти та Об'єкти .

  2. скинути поточну гілку до нового коміту

    Потім git resetпросто скиньте поточну гілку на новостворений об’єкт фіксації.

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

Варіація: Нове репо з шаблону проекту

Це корисно для створення "початкової фіксації" у новому проекті, використовуючи інший сховище як шаблон / архетип / насіння / скелет. Наприклад:

cd my-new-project
git init
git fetch --depth=1 -n https://github.com/toolbear/panda.git
git reset --hard $(git commit-tree FETCH_HEAD^{tree} -m "initial commit")

Це дозволяє уникнути додавання репо-шаблону як віддаленого ( originабо іншим способом) і згортає історію репо-шаблону у ваш початковий запис.


6
Синтаксис ревізії git (HEAD ^ {tree}) пояснюється тут на випадок, якщо хтось інший задумався
Колін Боуерн

1
Це скидає і локальне, і віддалене сховище, або лише одне з них?
aleclarson

4
@aleclarson, це скидає лише поточну гілку в локальному сховищі, використовуйте git push -fдля поширення.
ryenus

2
Я знайшов цю відповідь, шукаючи спосіб запустити новий проект із сховища шаблонів проектів, який не залучав git clone. Якщо додати --hardдо git resetі перемикач HEADз FETCH_HEADв git commit-treeви можете створити початкове зобов'язання після завантаження шаблону репо. Я відредагував відповідь у розділі наприкінці, що демонструє це.
toolbear

4
Ви можете позбутися цього "Caveat", але просто використовуючи${1?Please enter a message}
Елліот Кемерон

172

Якщо все, що ви хочете зробити, це скоротити всі ваші зобов’язання до кореневої комірки, тоді поки

git rebase --interactive --root

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

Ось два швидших та ефективніших рішення, коли ви стискаєте велику кількість комітетів:

Альтернативне рішення №1: осиротілі гілки

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

git checkout --orphan new-master master
git commit -m "Enter commit message for your new initial commit"

# Overwrite the old master branch reference with the new one
git branch -M new-master master

Документація:

Альтернативне рішення №2: програмне скидання

Ще одне ефективне рішення - просто використовувати змішане або м'яке скидання для кореневої комісії <root>:

git branch beforeReset

git reset --soft <root>
git commit --amend

# Verify that the new amended root is no different
# from the previous branch state
git diff beforeReset

Документація:


22
Альтернативне рішення №1: сироти-гілки - скелі!
Томас

8
Альтернативне рішення №1 FTW. Просто додайте, якщо ви хочете натиснути свої зміни на пульт, зробіть це git push origin master --force.
Едді Вербругген

1
не слід забуватиgit push --force
NecipAllef

Не робіть сирітську гілку за кодом (тобто не робіть альтернативного рішення №1 вище), якщо ви збираєтесь натиснути на відкритий запит на притягнення Github !!! Github закриє ваш PR, тому що поточна голова не є нащадком збереженої голови .
Ендрю Маккі

Альтернативне рішення №1 також дозволяє уникнути конфліктів злиття, які можуть статися при
скоєнні сквош

52
echo "message" | git commit-tree HEAD^{tree}

Це створить осиротілу команду з деревом HEAD і видасть її ім'я (SHA-1) на stdout. Тоді просто скиньте туди свою філію.

git reset SHA-1

27
git reset $(git commit-tree HEAD^{tree} -m "commit message")полегшило б це.
ryenus

4
^ ЦЕ! - має бути відповідь. Не зовсім впевнений, чи це був задум автора, але це було моє (потрібна незаймана репо з однією комісією, і це виконає роботу).
честербр

@ryenus, ваше рішення зробило саме те, що я шукав. Якщо ви додасте свій коментар як відповідь, я прийму його.
tldr

2
Причиною, що я сам не запропонував варіант subshell, є те, що він не працюватиме на cmd.exe в Windows.
кусма

У підказках Windows, можливо, доведеться цитувати останній параметр: echo "message" | git commit-tree "HEAD^{tree}"
Бернард

41

Ось як я закінчила це робити, на всякий випадок, якщо це працює для когось іншого:

Пам’ятайте, що завжди робити такі дії, і ніколи не погана ідея створити гілку збереження перед початком роботи.

Почніть з реєстрації

git log --oneline

Перейдіть до першого введення, скопіюйте SHA

git reset --soft <#sha#>

Замініть <#sha#>w / SHA, скопійовані з журналу

git status

Переконайтесь, що все зелене, інакше запустіть git add -A

git commit --amend

Змінити всі поточні зміни до поточного першого зобов’язання

Тепер натисніть цю гілку, і вона замінить те, що там є.


1
Відмінний варіант! Дійсно просто.
doublejr

1
Це більш ніж фантастично! Дякую!
Метт Комарницький

1
Зауважте, що це насправді залишає історію навколо. Ви осиротіли, але все ще є.
Бред

Дуже корисна відповідь ... але ви повинні знати, що після команди зміни ви опинитесь у редакторі vim із його спеціальним синтаксисом. ESC, ENTER,: x ваш друг.
Еріх

Відмінний варіант!
danivicario

36

Я читав щось про використання трансплантатів, але ніколи не досліджував це багато.

У будь-якому випадку, ви можете скріпити ці два останні вручну вручну з чимось таким:

git reset HEAD~1
git add -A
git commit --amend

4
Це насправді відповідь, яку я шукав, бажаю, щоб вона була прийнятою!
Джей

Це така дивовижна відповідь
Майстер Йода

36

Найпростіший спосіб - скористатися командою "сантехніка" update-refдля видалення поточної гілки.

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

Це повертає вас назад у стан "початкової фіксації", де ви можете почати з нового початкового зобов'язання.

git update-ref -d refs/heads/master
git commit -m "New initial commit"


15

В одному рядку з 6 слів

git checkout --orphan new_root_branch  &&  git commit

@AlexanderMills, ви повинні прочитати git help checkoutпро--orphan
kyb

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

1
легко. ось воноgit help checkout --orphan
киб

8

створити резервну копію

git branch backup

скинути на вказаний коміт

git reset --soft <#root>

потім додайте всі файли до інсценізації

git add .

здійснити без оновлення повідомлення

git commit --amend --no-edit

підштовхнути нову гілку з розчавленими комісіями на репо

git push -f

Чи збереже це попередні повідомлення про прихильність?
not2qubit

1
@ not2qubit ні, це не збереже попередні повідомлення про фіксацію, замість фіксації №1, фіксації №2, фіксації №3, ви отримаєте всі зміни цих комісій, упаковані в одну комісію №1. Коміт №1 буде <root>зобов’язанням, яке ви повернете назад. git commit --amend --no-editбуде вносити всі зміни до поточної комісії, яка <root>не потребує редагування повідомлення про фіксацію.
Девід Мортон

5

Для кабачків використовуйте щеплення

Додайте файл .git/info/grafts, покладіть туди хеш комітів, який ви хочете стати вашим коренем

git log тепер почнеться з цього зобов’язання

Щоб зробити його «справжнім» бігом git filter-branch


1

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

  • Автор (ім’я та електронна адреса)
  • Авторська дата
  • Комітет (ім'я та електронна адреса)
  • Виконана дата
  • Надіслати повідомлення журналу

Звісно, ​​виконувати-SHA нової / єдиної комісії зміниться, тому що вона представляє нову (не) історію, перетворюючись на неповноцінне / кореневе зобов'язання.

Це можна зробити, прочитавши git logта встановивши деякі змінні для git commit-tree. Припускаючи, що ви хочете створити один комітет з masterнової гілки one-commit, зберігаючи дані комісій вище:

git checkout -b one-commit master ## create new branch to reset
git reset --hard \
$(eval "$(git log master -n1 --format='\
COMMIT_MESSAGE="%B" \
GIT_AUTHOR_NAME="%an" \
GIT_AUTHOR_EMAIL="%ae" \
GIT_AUTHOR_DATE="%ad" \
GIT_COMMITTER_NAME="%cn" \
GIT_COMMITTER_EMAIL="%ce" \
GIT_COMMITTER_DATE="%cd"')" 'git commit-tree master^{tree} <<COMMITMESSAGE
$COMMIT_MESSAGE
COMMITMESSAGE
')

1

Для цього ви можете скинути локальний репозиторій git до першого хеш-тегу фіксації, тому всі ваші зміни після цього фіксації будуть нестандартними, тоді ви можете скористатися параметром --amend.

git reset your-first-commit-hashtag
git add .
git commit --amend

А потім відредагуйте першу назву фіксації, якщо потрібно, та збережіть файл.


1

Для мене це спрацювало так: у мене було 4 коми, і я використовував інтерактивну базу даних:

git rebase -i HEAD~3

Найперша фіксація залишається, і я взяв 3 останніх комісії.

Якщо ви застрягли в редакторі, який з’явиться далі, ви побачите щось таке:

pick fda59df commit 1
pick x536897 commit 2
pick c01a668 commit 3

Ви повинні взяти на себе перші зобов’язання та розігнати інших. Що ви повинні мати:

pick fda59df commit 1
squash x536897 commit 2
squash c01a668 commit 3

Для цього використовуйте клавішу INSERT, щоб змінити режим "вставити" та "редагувати".

Для збереження та виходу з редактора використовуйте :wq. Якщо ваш курсор знаходиться між цими лініями фіксації або десь ще натисніть ESC та спробуйте ще раз.

В результаті у мене було два коміти: перший, який залишився, і другий з повідомленням "Це комбінація з 3 комітів".

Докладніше перегляньте тут: https://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit


0

Я зазвичай роблю так:

  • Переконайтеся, що все зроблено, і запишіть останній ідентифікатор фіксації, якщо щось піде не так, або створіть окрему гілку як резервну копію

  • Біжи git reset --soft `git rev-list --max-parents=0 --abbrev-commit HEAD` щоб скинути голову до першої фіксації, але залиште свій індекс без змін. Усі зміни з моменту першої фіксації тепер будуть готові до здійснення.

  • Біжи git commit --amend -m "initial commit" щоб внести зміни до своїх зобов’язань до першого зобов’язання та змінити повідомлення про фіксацію, або якщо ви хочете зберегти існуюче повідомлення про фіксацію, можете запуститиgit commit --amend --no-edit

  • Бігайте, git push -fщоб змусити натиснути зміни

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