Як я можу скріплювати свої останні X зобов’язання разом в одну команду за допомогою Git?
Як я можу скріплювати свої останні X зобов’язання разом в одну команду за допомогою Git?
Відповіді:
Використовуйте git rebase -i <after-this-commit>
та замініть "підбір" на другому та наступних фіксаціях на "сквош" або "фіксація", як описано в посібнику .
У цьому прикладі <after-this-commit>
є або хеш SHA1, або відносне місце розташування від HEAD поточної гілки, з якого коміти аналізуються для команди rebase. Наприклад, якщо користувач бажає переглянути 5 команд з поточної HEAD в минулому, це команда git rebase -i HEAD~5
.
<after-this-commit>
?
<after-this-commit>
є фіксація X + 1, тобто батько найдавнішої комісії, яку ви хочете зробити сквош.
rebase -i
підходом і reset --soft
полягає в тому, rebase -i
що я дозволяє утримувати автора прихильності, в той час як reset --soft
дозволяє мені повторно. Іноді мені потрібно розряджати запити на тягнення, зберігаючи інформацію про автора. Іноді мені потрібно скинути програмне забезпечення у власних програмах. В будь-якому разі підносить обидва чудові відповіді
Ви можете зробити це досить легко без 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 до останнього комітету, який ви не хочете робити сквош. Ні індекс, ні робоче дерево не торкаються м'якого скидання, залишаючи індекс у бажаному стані для вашої нової фіксації (тобто він уже має всі зміни від комісій, які ви збираєтеся "викинути").
git rebase --squash-recent
, чи навіть git commit --amend-many
.
branch@{upstream}
(або просто @{upstream}
для поточної гілки; в обох випадках останню частину можна скоротити @{u}
; див. Gitrevision ). Це може відрізнятися від вашої "останньої поштовху" (наприклад, якщо хтось інший штовхнув щось, що склалося на вашому останньому натиску, а потім ви це зробили), але здається, що це може бути близьким до того, що ви хочете.
push -f
але в іншому випадку це було чудово, дякую.
git push --force
згодом, щоб взяти на себе зобов’язання
Можна використовувати 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
запропонованим Крісом Джонсоном у своїй відповіді, - це те, що ви отримуєте повідомлення про фіксацію, яке популяризується кожним повідомленням про фіксацію, яке ви стискаєте.
git rebase -i
ви, але не даєте причини. Орієнтовно - це це, тому що мені здається, що насправді все навпаки, і це хакер; ви не виконуєте більше команд, ніж потрібно, тільки щоб змусити git merge
виконувати одну з речей, git rebase
спеціально розроблених для цього?
git merge --squash
також простіше використовувати в сценарії. По суті, міркування полягали в тому, що для цього вам взагалі не потрібна "інтерактивність" git rebase -i
.
git merge --squash
менша ймовірність створювати конфлікти злиття перед переміщенням / видаленням / перейменовуванням у порівнянні з повторним використанням, особливо якщо ви з’єднуєтесь з місцевою гілкою. (відмова від відповідальності: ґрунтуючись лише на одному досвіді, виправте мене, якщо це не так у загальному випадку!)
HEAD@{1}
щоб просто бути на безпечній стороні, наприклад, коли ваш робочий процес на годину переривається відключенням електроенергії тощо.
Я рекомендую уникати, git reset
коли це можливо - особливо для Git-новачків. Якщо вам справді не потрібно автоматизувати процес на основі ряду комітетів, існує менш екзотичний спосіб ...
git merge --squash (working branch name)
git commit
Повідомлення про фіксацію буде популярним на основі сквош.
gitk
щоб позначити рядок коду, який ви сквош, а також позначити основу, на яку потрібно сквош. У звичайному випадку обидві ці мітки вже будуть існувати, тому крок (1) можна пропустити.
git branch your-feature && git reset --hard HEAD~N
найзручніший спосіб. Однак він знову включає скидання git, чого ця відповідь намагалася уникнути.
На основі відповіді Кріса Джонсена ,
Додати глобальний псевдонім "сквош" від 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
їх змінити вручну. (Або відредагуйте псевдонім відповідно до ваших смаків.)
git squash -m "New summary."
і N
визначив автоматично як кількість непущених комітетів.
git commit --amend
для подальшої зміни повідомлення, але цей псевдонім дозволяє вам добре почати те, що повинно бути в повідомленні про фіксацію.
Завдяки цій зручній публікації в блозі я виявив, що ви можете скористатися цією командою, щоб скинути останні 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
.
Закриваючи редактор, виправлення потрапить у фіксацію функції, і моя історія фіксування буде виглядати приємно і чисто!
Це добре працює, коли всі комісії локальні, але якщо ви спробуєте змінити будь-які комісії, які вже натиснуті на пульт, ви можете справді створити проблеми іншим розробникам, які перевірили ту саму гілку!
pick
в рядку 1. Якщо ви виберете squash
або виконуватимете fixup
команду в рядку 1, git покаже повідомлення, що говорить "помилка: не можна" виправити "без попереднього зобов'язання". Тоді ви дасте можливість виправити це: "Ви можете виправити це за допомогою" git rebase --edit-todo ", а потім запустіть" git rebase --continue "." або ви можете просто перервати і почати спочатку: "Або ви можете перервати ребазу за допомогою" git rebase --abort "."
Якщо ви використовуєте TortoiseGit, ви можете виконувати функцію Combine to one commit
:
Show Log
Combine to one commit
у контекстному менюЦя функція автоматично виконує всі необхідні одиничні дії git. На жаль, доступно лише для Windows.
Для цього можна скористатися наступною командою 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....
Для детальної інформації натисніть посилання
Виходячи з цієї статті, я знайшов цей спосіб простішим для моєї справи.
Моя гілка «dev» випередила «походження / dev» на 96 комітетів (тому ці комісії ще не були висунуті у віддалений).
Мені хотілося розкласти ці зобов’язання до одного, перш ніж натиснути на зміни. Я вважаю за краще повернути гілку до стану "origin / dev" (це дозволить залишити всі зміни з 96 комісій без змін), а потім здійснити зміни відразу:
git reset origin/dev
git add --all
git commit -m 'my commit message'
У галузі, яку ви хочете об'єднати, виконайте наступні дії:
git rebase -i HEAD~(n number of commits back to review)
приклад:
git rebase -i HEAD~1
Це відкриє текстовий редактор, і ви повинні переключити "вибирати" перед кожною коміткою на "сквош", якщо ви хочете, щоб ці зобов'язання були об'єднані разом. З документації:
Наприклад, якщо ви хочете об'єднати всі комітети в одну, 'pick' - це перше зобов’язання, яке ви зробили, а всі майбутні (розміщені нижче першого) слід встановити на 'squash'. Якщо ви використовуєте vim, використовуйте : x у режимі вставки, щоб зберегти та вийти з редактора.
Потім продовжити ребауз:
git rebase --continue
Більш детально про цей та інші способи переписати історію фільмів дивіться у цій корисній публікації
--continue
та vim :x
.
git add
встановите правильну конфігурацію у своїх файлах, яку ви використовуєте git rebase --continue
для переходу до наступного комітету та початку злиття. :x
це одна команда, яка збереже зміни файлу при використанні vim дивіться це
Відповідь аномій - це добре, але я почувався невпевнено щодо цього, тому вирішив додати пару скріншотів.
Подивіться, де ви знаходитесь git log
. Найголовніше - знайти хеш-код першої комісії, який ви не хочете робити сквош. Тож тільки:
Виконай git rebase -i [your hash]
, у моєму випадку:
$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d
У моєму випадку я хочу розігнати все, що було вперше вчасно. Впорядкування відбувається від першого до останнього, точніше так, як і в git log
. У моєму випадку я хочу:
Якщо ви вибрали лише одну фіксацію, а решту скосили, ви можете скорегувати одне повідомлення про фіксацію:
Це воно. Коли ви збережете це ( :wq
), ви закінчите. Погляньте на це git log
.
git log
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 ubuntu
shell. Якщо ви використовуєте різні os ( Windows
або Mac
), то вищезгадані команди однакові, крім редактора. Ви можете отримати іншого редактора.
git add <files>
--fixup
варіант, і OLDCOMMIT
слід, на якому нам потрібно об'єднати (скріш) цей коміт.git commit --fixup=OLDCOMMIT
Тепер це створює новий комітет поверх HEAD за допомогою fixup1 <OLDCOMMIT_MSG>
.
OLDCOMMIT
.git rebase --interactive --autosquash OLDCOMMIT^
Тут ^
мається на увазі попереднє зобов’язання OLDCOMMIT
. Ця rebase
команда відкриває інтерактивне вікно в редакторі (vim чи nano), на якому нам не потрібно нічого робити, просто збереження та вихід достатньо. Оскільки варіант, переданий цьому, автоматично перемістить останню фіксацію на наступну стару фіксацію та змінить операцію на fixup
(еквівалентну сквош). Потім база даних продовжується і закінчується.
--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
Щоб розбити останні 10 комірок на 1 одиночний комітет:
git reset --soft HEAD~10 && git commit -m "squashed commit"
Якщо ви також хочете оновити віддалену гілку зі скошеним комітом:
git push -f
Якщо ви знаходитесь на віддаленій гілці (називається feature-branch
), клонованій із золотого сховища ( golden_repo_name
), то ось методика розбивати ваші комісії на одне:
Оформити золоту репо
git checkout golden_repo_name
Створіть з нього нову гілку (золоте репо) наступним чином
git checkout -b dev-branch
Кабачкові злиття з місцевим відділенням, яке ви вже маєте
git merge --squash feature-branch
Введіть свої зміни (це буде єдине зобов’язання, яке входить у відділ розробки)
git commit -m "My feature complete"
Перемістіть гілку у ваше місцеве сховище
git push origin dev-branch
Що може бути справді зручним:
скажіть, скажіть, хеш, який ви хочете зробити скріш d43e15
.
Тепер використовуйте
git reset d43e15
git commit -am 'new commit name'
Це супер-пупер безглуздо, але якось круто, тому я просто кину його на ринг:
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, але ви можете змінити це, як би вам не подобалось.)
Я, мабуть, зробив те, що запропонував Марк Лонгейр .
Якщо ви хочете скосити кожну комісію в одну команду (наприклад, при першому випуску проекту публічно), спробуйте:
git checkout --orphan <new-branch>
git commit
Я думаю, що найпростіший спосіб зробити це - створити нову гілку від ведучого і зробити злиття - розкручування гілки функцій.
git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch
Тоді ви готові здійснити всі зміни.
Простий однолінійний працівник, який завжди працює, враховуючи, що ви зараз перебуваєте на гілці, яку ви хочете скосати, майстер - це гілка, з якої вона виникла, а остання фіксація містить повідомлення про фіксацію та автора, який ви хочете використовувати:
git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}
якщо ви, наприклад, хочете скасувати останні три комісії на одну команду у відділенні (віддаленому сховищі), наприклад: https://bitbucket.org
Що я і зробив
(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 ви бачите це:
Чотири коміти:
Думаючи, що ніхто ніколи не потурбується прочитати повну історію сховища. (Насправді є сховище, натисніть на посилання вище!) Ви вирішили зменшити ці комісії. Тож ти йдеш і біжиш git reset --soft HEAD~4 && git commit
. Потім ви перейдете git push --force
на GitHub, щоб прибрати свій PR.
А що відбувається? Ви щойно взяли на себе зобов’язання, які дісталися від Фріца до Білла Клінтона. Тому що ви забули, що вчора працювали над версією проекту «Букінгемський Нікс». І git log
не відповідає тому, що ви бачите на GitHub.
git checkout
їхgit reset --soft
цеgit commit
що перекоси безпосередньо від від до доgit rebase -i HEAD^^
де число ^ 's дорівнює X
(у такому випадку розбийте два останні зобов’язання)
На додаток до інших відмінних відповідей, я хотів би додати, як git rebase -i
завжди плутає мене з порядком виконання зобов’язань - старішим до більш новим чи навпаки? Отже, це мій робочий процес:
git rebase -i HEAD~[N]
, де N - кількість комітетів, які я хочу приєднати, починаючи з останнього . Отже, git rebase -i HEAD~5
це означало б "скинути останні 5 приєднань до нового";А як відповідь на запитання, пов'язане з таким робочим процесом?
merge --squash
після PR, але команда думала, що це сповільнить процес.)Я не бачив подібного робочого процесу на цій сторінці. (Це можуть бути мої очі.) Якщо я rebase
правильно розумію , багаторазове злиття потребує декількох конфліктів . Я НЕ хочу навіть думати про це!
Отже, це, здається, працює для нас.
git pull master
git checkout -b new-branch
git checkout -b new-branch-temp
git checkout new-branch
git merge --squash new-branch-temp
// ставить усі зміни на стадіїgit commit 'one message to rule them all'
git push
Я вважаю, що більш загальним рішенням є не вказівка комітетів "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 мають бути добре з цим.)
git squash master
коли нас перевіряють на раба. що це станеться? чи приховатимемо конфлікт?
Під питанням може бути неоднозначно, що означає "останній".
наприклад, 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 (втрачаючи цілком зобов'язані зміни після розгалуження і перед об'єднанням.