Як трохи додаткового пояснення зверніть увагу, що git stashробиться або два коміти, або три коміти. За замовчуванням два; Ви отримуєте три, якщо використовуєте будь-яке написання параметрів --allабо --include-untracked.
Ці два, або три, коміти є особливими одним важливим чином: вони не знаходяться в жодному відділенні. Git знаходить їх за спеціальною назвою stash. 1 Найголовніше, однак, це те, що Git дозволяє вам - і змушує - робити ці два-три коміти. Щоб зрозуміти це, нам потрібно подивитися, що в цих комітах.
Що всередині схованки
У кожному коміті може бути перелік одного або декількох батьківських комітів. Вони утворюють графік, де пізніше коміти вказують на попередні. Зазвичай сховище містить два коміти, які я люблю називати iдля вмісту індексу / області прогону та wдля вмісту робочого дерева. Пам'ятайте також, що кожен коміт містить знімок. У звичайному коміті цей знімок зроблений із вмісту індексу / області прогону. Тож iкоміт насправді є цілком нормальним комітом! Це просто не на жодній гілці:
...--o--o--o <-- branch (HEAD)
|
i
Якщо ви робите звичайний тайник, git stashкод робить wзараз, копіюючи всі ваші відстежувані файли робочого дерева (у тимчасовий допоміжний індекс). Git встановлює першого з батьків цього wкоміту вказувати на HEADкоміт, а другого з батьків - на фіксацію i. Нарешті, він встановлює stashвказівку на цей wкоміт:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
Якщо ви додаєте --include-untrackedабо --all, Git робить додатковий коміт, uміж перервами iта w. Вміст знімка для - uце ті файли, які не відстежуються, але не ігноруються ( --include-untracked), або файли, які не відслідковуються, навіть якщо вони ігноруються ( --all). Це додаткове uзобов'язання не мають ні батьків, а потім , коли git stashмарка w, вона встановлює w«s третього батька це uзробити, так що ви отримаєте:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
На цьому етапі Git також видаляє будь-які файли робочого дерева, які заводились у uкоміті (використовуючи git cleanдля цього).
Відновлення схованки
Коли ви хочете відновити схованку, у вас є можливість використовувати її --indexабо не використовувати. Це говорить git stash apply(або який - або з команд , які внутрішньо використовувати apply, наприклад pop) , що він повинен використовуватиi зробити , щоб спробувати змінити свій поточний індекс. Ця модифікація виконується за допомогою:
git diff <hash-of-i> <hash-of-i's-parent> | git apply --index
(більш-менш; тут є купа дрібних деталей, які заважають цій основній ідеї).
Якщо ви пропустите --index, git stash applyповністю ігнорує iкоміт.
Якщо схованка має лише два коміти, git stash applyтепер можна застосувати wкоміт. Він робить це, викликаючи git merge2 (не дозволяючи йому фіксувати або розглядати результат як звичайне злиття), використовуючи вихідний коміт, для якого було зроблено сховище ( iбатьківський та wперший батьківський) як основу злиття, wяк --theirscommit, а ваш поточний (HEAD) коміт як ціль злиття. Якщо злиття вдається, все добре - ну, принаймні Git так думає - і git stash applyсамо успіх. Якщо раніше ви git stash popзастосовували схованку, код тепер скидає схованку. 3 Якщо злиття не вдається, Git оголошує заявку невдалою. Якщо ви використовувалиgit stash pop, код зберігає схованку і надає той самий статус відмови, що і для git stash apply.
Але якщо у вас є третій коміт - якщо uу сховищі, яке ви застосовуєте, є коміт - тоді все змінюється! Немає можливості робити вигляд, що uкоміт не існує. 4 Git наполягає на вилученні всіх файлів із цього uкоміту в поточне дерево роботи. Це означає, що файли або не повинні взагалі існувати, або мати такий самий вміст, як у uкоміті.
Щоб це сталося, ви можете скористатися git cleanсобою, але пам’ятайте, що файли, що не відстежуються (ігноруються чи ні), не існують у сховищі Git, тому будьте впевнені, що ці файли можуть бути знищені! Або ви можете створити тимчасовий каталог і перенести туди файли на зберігання - або навіть зробити інший git stash save -uабо git stash save -a, оскільки вони працюватимуть git cleanза вас. Але це просто залишає у вас інший uзапас у стилі, з яким слід мати справу пізніше.
1 Це насправді refs/stash. Це має значення, якщо ви робите філію з іменем stash: повне ім’я філії refs/heads/stash, тому вони не суперечать. Але не робіть цього: Git не заперечить, але ви заплутаєте себе. :-)
2git stash код на насправді використовує git merge-recursiveпрямо тут. Це необхідно з багатьох причин, а також має побічним ефектом переконання, що Git не розглядає це як злиття, коли ви вирішуєте конфлікти та фіксуєте.
3 Ось чому я рекомендую уникати git stash popна користь git stash apply. Ви отримуєте шанс розглянути то , що отримав застосовані, і вирішити , чи був він на самому справі застосовується правильно. Якщо ні, у вас все ще є схованка, це означає, що ви можете використати, git stash branchщоб відновити все ідеально. Ну, якщо припустити відсутність цього докучливого uкоміту.
4 Справді повинно бути: git stash apply --skip-untrackedабо щось інше. Також повинен бути варіант, який означає викинути всі ці uфайли комітів до нового каталогу , наприклад git stash apply --untracked-into <dir>, можливо.
git stash show -p | git apply --3