Практичне використання скидання git --soft?


136

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

Я розумію, що я можу використовувати програмне скидання для редагування комісії без зміни індексу чи робочого каталогу, як я це робив git commit --amend.

Чи справді ці дві команди однакові ( reset --softпроти commit --amend)? Будь-яка причина використовувати те чи інше в практичному плані? І що ще важливіше, чи існують якісь інші користі для того, щоб reset --softвнести зміни до комісії?

Відповіді:


110

git resetвсе стосується переїзду HEAD, і взагалі гілки ref .
Питання: а що з робочим деревом та індексом?
При роботі з --soft, рухається HEAD, найчастіше оновлюючи відгалуження галузі, і тількиHEAD .
Це відрізняється від commit --amend:

  • це не створює нового комітету.
  • він може фактично перемістити HEAD до будь-якого комітету (як commit --amendце стосується лише не переміщення HEAD, дозволяючи повторити поточну комісію)

Щойно знайшов такий приклад поєднання:

  • класичне злиття
  • злиття піддерева

всі в одну (восьминога), оскільки з’єднано більше двох гілок) здійснюють злиття.

Томаш "wasHamster" Карнекі пояснює у своїй статті "Злиття підводного восьминога" :

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

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

У мене є суперпроект, назвемо його projectA, і підпроект projectB, який я об'єднав у підкаталог projectA.

(це частина злиття піддерева)

Я також підтримую кілька місцевих комітетів.
ProjectAрегулярно оновлюється, projectBмає нову версію кожні пару днів або тижнів і зазвичай залежить від конкретної версії projectA.

Коли я вирішую оновити обидва проекти, я не просто переходжу від цього, projectAі projectB це створить два коміти для того, що має бути атомним оновленням всього проекту .
Замість цього я створюю один злиття зробити який поєднує в собі projectA, projectBі мої коммітов .
Тут складна частина полягає в тому, що це злиття восьминога (три голови), але projectBйого потрібно об'єднати зі стратегією підкреслень . Отже, це я роблю:

# Merge projectA with the default strategy:
git merge projectA/master

# Merge projectB with the subtree strategy:
git merge -s subtree projectB/master

Тут автор використав a reset --hard, а потім read-treeдля відновлення того, що перші два злиття було зроблено з робочим деревом та індексом, але саме тут reset --softможе допомогти:
Як переробити ці два злиття , які спрацювали, тобто моє робоче дерево та індекс добре, але без того, щоб записувати ці два коміти?

# Move the HEAD, and just the HEAD, two commits back!
git reset --soft HEAD@{2}

Тепер ми можемо відновити рішення Томаса:

# Pretend that we just did an octopus merge with three heads:
echo $(git rev-parse projectA/master) > .git/MERGE_HEAD
echo $(git rev-parse projectB/master) >> .git/MERGE_HEAD

# And finally do the commit:
git commit

Отже, кожен раз:

  • вас влаштовує те, що ви закінчуєте (що стосується робочого дерева та індексу)
  • вас не влаштовують усі зобов’язання, які взяли вас до туди:

git reset --soft це відповідь.


8
Примітка для себе: простий приклад защемлення з git reset --soft: stackoverflow.com/questions/6869705 / ...
VonC

3
також корисно, якщо ви довели до неправильної гілки. всі зміни повертаються до місця постановки та рухаються разом із вами, коли ви перевіряєте праву гілку.
smoebody

44

Використовуйте регістр - комбінуйте ряд місцевих комітетів

"На жаль. Ці три коміти можуть бути лише одним".

Отже, скасуйте останні 3 (або що завгодно) вчинення (не впливаючи на індекс і робочу директорію). Потім введіть усі зміни як одну.

Напр

> git add -A; git commit -m "Start here."
> git add -A; git commit -m "One"
> git add -A; git commit -m "Two"
> git add -A' git commit -m "Three"
> git log --oneline --graph -4 --decorate

> * da883dc (HEAD, master) Three
> * 92d3eb7 Two
> * c6e82d3 One
> * e1e8042 Start here.

> git reset --soft HEAD~3
> git log --oneline --graph -1 --decorate

> * e1e8042 Start here.

Тепер усі ваші зміни збереглися і готові здійснитись як цілі.

Короткі відповіді на ваші запитання

Чи справді ці дві команди однакові ( reset --softпроти commit --amend)?

  • Немає.

Будь-яка причина використовувати те чи інше в практичному плані?

  • commit --amend додати / rm файли з самого останнього фіксації або змінити його повідомлення.
  • reset --soft <commit> об'єднати кілька послідовних комісій у новий.

І що ще важливіше, чи існують якісь інші користі для того, щоб reset --softвнести зміни до комісії?

  • Дивіться інші відповіді :)

2
Дивіться інші відповіді на reset --soft
тему

У відповідь на останній коментар - було погоджено частково, але я б сказав, що внесення змін до одного зобов'язання є надто конкретним. Наприклад, ви можете захотіти, як тільки буде написана функція, розбити все і вирізати нові комітети, більш корисні для рецензентів. Я б сказав, що м'які перезавантаження (включаючи прості git reset) хороші, коли ви (а) хочете переписати історію, (б) не переймаєтесь старими документами (тому ви зможете уникнути суєти інтерактивного ребайна) та (c) мають більше одного зобов’язання змінити (інакше commit --amendпростіше).
johncip

18

Я використовую це, щоб змінити більше, ніж лише останнє зобов'язання.

Скажімо, я допустив помилку в скоєнні A, а потім зробив скоєння B. Тепер я можу лише внести зміни git reset --soft HEAD^^B.

Звичайно, для великих комісій це не дуже зручно ... але все одно не варто робити великих зобов'язань ;-)


3
git commit --fixup HEAD^^ git rebase --autosquash HEAD~Xдобре працює теж.
Ніклас

3
git rebase --interactive HEAD^^де ви вирішите редагувати обидва комірки A і B. Цей спосіб зберігає повідомлення про фіксацію A і B, якщо потрібно, ви все ще можете змінювати їх.
Пол Пладійс

2
Як повторно здійснити B після повернення до A?
northben

1
Інший спосіб , який, ймовірно , менше роботи: перевірити журнал GIT, отримати Комміт хеша B, а потім git reset A, зробити і додати зміни, git commit --amend, git cherry-pick <B-commit-hash>.
naught101

15

Ще одне потенційне використання - це альтернатива захованню (що деякі люди не люблять, див., Наприклад, https://codingkilledthecat.wordpress.com/2012/04/27/git-stash-pop-considered-harmful/ ).

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

git commit -am "In progress."

потім майстер оформлення замовлення і виконайте виправлення. Коли я закінчую, я повертаюся до своєї філії та займаюся

git reset --soft HEAD~1

продовжувати працювати там, де я зупинився.


3
Оновлення (тепер, коли я краще розумію git): --softнасправді тут непотрібно, якщо ви дійсно не піклуєтесь про те, щоб зміни були негайно виконані. Я зараз просто користуюся, git reset HEAD~роблячи це. Якщо у мене є зміни, які потрібно здійснити, коли мені потрібно переключити гілки, і я хочу їх зберегти таким чином, тоді я це роблю git commit -m "staged changes", потім git commit -am "unstaged changes", потім пізніше git reset HEAD~, git reset --soft HEAD~щоб повністю відновити робочий стан. Хоча, чесно кажучи, я обидва ці речі роблю набагато менше тепер, коли я знаю про git-worktree:)
deltacrux

7

Ви можете використовувати git reset --softдля зміни версії, яку ви хочете мати як батьків, для змін, що відбулися у вашому індексі та робочому дереві. Випадки, коли це корисно, рідкісні. Іноді ви можете вирішити, що зміни, що відбулися у вашому робочому дереві, повинні належати до іншої гілки. Або ви можете використовувати це як простий спосіб збити кілька комітетів в один (подібний до сквош / складання).

Дивіться цю відповідь VonC на практичному прикладі: Скасуйте перші два коміти в Git?


Це хороше питання, якого я раніше не знаходив. Але я не впевнений, що бачу, як змінити зміни на іншій гілці за допомогою програмного забезпечення скидання. Отже, у мене є відділення для оформлення замовлення, потім я використовую, git reset --soft anotherBranchа потім здійснюю там? Але ви насправді не змінюєте гілку ckeckout, тож ви зобов’яжетеся взяти участь у відділенні або до іншогоBranch ?
AJJ

Робити це важливо, ви не використовуєте git checkout, оскільки це змінить ваше дерево. Подумайте про скидання git --soft як про спосіб просто маніпулювати версією, на яку вказує HEAD.
Йоганнес Рудольф

7

Одне можливе використання - це коли ви хочете продовжити свою роботу на іншій машині. Це діятиме так:

  1. Оформити нову філію зі схожим назвою,

    git checkout -b <branchname>_stash
    
  2. Просуньте свою приховану гілку,

    git push -u origin <branchname>_stash
    
  3. Перейдіть на іншу машину.

  4. Спустіть як ваші сховища, так і наявні гілки,

    git checkout <branchname>_stash; git checkout <branchname>
    
  5. Ви повинні бути у вашому існуючому відділенні зараз. Об’єднайте зміни з гілки приховування,

    git merge <branchname>_stash
    
  6. Програмне відновлення існуючої гілки до 1 до об'єднання,

    git reset --soft HEAD^
    
  7. Видаліть свою скриньку,

    git branch -d <branchname>_stash
    
  8. Також видаліть свою сховище від походження,

    git push origin :<branchname>_stash
    
  9. Продовжуйте працювати зі своїми змінами так, ніби ви їх нормально приховали.

Я думаю, в майбутньому, GitHub і co. має запропонувати цю функцію "віддаленого сховання" за кілька кроків.


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

7

Одне практичне використання - якщо ви вже взяли на себе місцеве репо (наприклад, git -m -m), ви можете скасувати цю останню передачу, виконавши скидання git --soft HEAD ~ 1

Також для вашого відома, якщо ви вже здійснили зміни (наприклад, з додаванням git), тоді ви можете змінити інсценізацію, виконавши скидання git - змішану HEAD або я зазвичай також використовуюgit reset

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


1
git reset --soft HEAD ~1дає мені, fatal: Cannot do soft reset with paths.я думаю, нам потрібно прибрати простір після HEAD, щоб це булоgit reset --soft HEAD~1
Ендрю Лор

6

Відмінним приводом для використання " git reset --soft <sha1>" є переміщенняHEAD в голому репо.

Якщо ви намагаєтесь використовувати --mixedабо--hard , ви отримаєте помилку, оскільки ви намагаєтеся змінити та працювати дерево та / або індекс, який не існує.

Примітка. Це потрібно буде робити прямо з голого репо.

Знову зверніть увагу: Вам потрібно буде переконатися, що гілка, яку ви хочете скинути в голому репо, є активною гілкою. Якщо немає, слідувати VonC в відповіді про те , як оновити активну гілка в голом репо , коли у вас є прямий доступ до репо.


1
Як уже згадувалося у вашій попередній відповіді ( stackoverflow.com/questions/4624881/… ), це вірно, коли у вас є прямий доступ до голої репо (про яку ви згадуєте тут). Хоча +1.
VonC

@VonC Так, абсолютно правильно і дякую за додавання замітки! Я постійно забуваю додати, що оскільки я припускаю, що скидання зроблено безпосередньо з репо. Крім того, я припускаю, що гілка, яку людина хоче скинути в голому репо, є їх активною гілкою. Якщо гілка не є їхньою активною гілкою, то потрібно оновити відповідно до вашої відповіді ( stackoverflow.com/questions/3301956/… ) про те, як оновити активну гілку для голих репостів. Я оновлю відповідь також інформацією про активну галузь. Знову дякую!!!
Газок

2

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

Так що git reset --soft HEAD~1набагато корисніше, ніжcommit --amend у цьому сценарії. Я можу скасувати фіксацію, повернути всі зміни в область постановки та відновити налаштування поетапних бітів за допомогою SourceTree.

Дійсно, мені здається, що commit --amendце надмірна команда двох, але git - це git і не ухиляється від подібних команд, які роблять трохи інші речі.


1

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

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

Але наприкінці мого завдання, коли я хочу побачити всі мої зміни разом (до 1-ї комісії), зробити самоперевірку коду перед тим, як зробити запит на витяг, я побачив би лише зміни, що відбулися з моєї попередньої комісії (після фіксації 4) і не змінюється з усіх комітетів мого поточного завдання.

Тому я використовую git reset --soft HEAD~4для повернення 4 коміти. Це дозволяє мені бачити всі зміни разом. Коли я впевнений у своїх змінах, я можу це зробити git reset HEAD@{1}і впевнено натиснути на віддалений.


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

Гей, я не зовсім зрозумів, що ти пропонуєш.
Swanky Coder

1
Просто, замість того, git reset @{1}щоб відновити свою першу чернетку, ви можете замість цього створити серію для публікації з git add -pта git commit.
jthill

Так, правильно. Ще один спосіб - це (той, за яким я зазвичай дотримуюся), щоб перезавантажити версію за течією / майстер і розбити комміси в один.
Swanky Coder

1

Інший випадок використання, коли ви хочете замінити іншу гілку на вашу у запиті на виклик, наприклад, скажемо, що у вас є програмне забезпечення з функціями A, B, C у розробці.

Ви розробляєте наступну версію, і ви:

  • Видалена функція B

  • Додана функція D

У процесі розробки щойно доданих виправлень для функції B.

Ви можете об'єднати розробку в наступну, але це часом може бути безладно, але ви також можете використовувати git reset --soft origin/developта створювати комісію зі своїми змінами, і гілка може бути об'єднаною без конфліктів і зберігати зміни.

Виявляється, git reset --softце зручна команда. Я особисто використовую це для того, щоб скасувати комісії, які не мають "завершеної роботи", наприклад "WIP", тому коли я відкриваю запит на витяг, всі мої зобов'язання зрозумілі.

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