Відмова від прихованої попи у Git


257

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

Я спробував git merge --abort, але git стверджував, що не відбувається злиття. Чи є простий спосіб скасувати поп, не знищуючи змін, які я спочатку мав у каталозі?


Щодо ваших невмілих змін: чи були ці зміни вже в індексі?
jørgensen

Чи можете ви розмістити версію git, яку ви використовуєте.
Тиньман


2
Прийнята відповідь виглядає складною. Я думаю, що це досить гарна загальна практика ніколи не робити щось на кшталт замаху git stash popна нечистого робочого режиму. У такому випадку ви можете просто, git reset --hardа ваші сховища залишаються цілими. (Це більш-менш те, що підказує пов'язана тема @ BradKoch)
Стівен Лу,

1
@StevenLu, я погоджуюся, але проблема може виникнути в чистому робочому режимі, якщо ви приховуєте зміни, щоб перенести їх на іншу гілку. Прихований конфлікт з комісіями, які існують у новій гілці, яка не існувала на старій.
Джейк Стівенс-Хаас

Відповіді:


55

Гаразд, я думаю, що я розробив "git stash unpply". Це складніше, ніж git apply --reverseтому, що вам потрібно здійснити зворотне злиття, якщо було зроблено об'єднання git stash apply.

Зворотне злиття вимагає, щоб усі поточні зміни були витіснені в індекс:

  • git add -u

Потім інвертуйте те, merge-recursiveщо було зроблено git stash apply:

  • git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1

Тепер у вас залишиться лише неприховані зміни. Вони будуть в індексі. git resetЯкщо ви хочете, ви можете скасувати свої зміни.

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

Ось приклад, який показує, як робоча копія (через git status) знову закінчується чистотою:

 $ git status
# On branch trunk
nothing to commit (working directory clean)
 $ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
 $ git add -u
 $ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
 $ git status
# On branch trunk
nothing to commit (working directory clean)

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

7
git stash applyніколи не скидає приховування, а коли злиття не вдається, то git stash popтакож зберігає приховування
Xerus

Погані речі відбудуться, якщо відбувся конфлікт злиття під час оригінальної
скриптової

286

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

git reset HEAD --hard
git checkout my_correct_branch
git stash pop

Легко.


22
Отже, схована копія залишається в списку сховищ, поки у вас не буде успішного поп-шоу?
Райан Кларк

52
Це не є відповіддю на оригінальне запитання, оскільки воно би стерло місцеві зміни, які не були здійснені.
Дмитро

13
@RyanClark Дивіться відповідь DavidG нижче. В основному, так, він залишається в списку скриньки.
fkorsa

1
Я думаю, що переважна більшість користувачів, які опинилися в цій ситуації, вважають це рішення ідеальним. Я не можу уявити ситуацію, коли я змінив зміни в своєму робочому каталозі, перш ніж намагатися вступити stash popв нього. Це звучить як рецепт катастрофи.
Шадоніня

2
Приємно. Прочитавши це, я подивився на git stash pop docs, і там написано: "Застосування стану може не вдатися до конфліктів; в цьому випадку він не видаляється зі списку скриньки." Отже, ось чому скриньку можна повторно випустити після скидання / оформлення замовлення.
Брейді Холт

48

Редагувати: З git help stashдокументації в поп-розділі:

Застосування держави може провалитися при конфліктах; у цьому випадку він не видаляється зі списку сховищ. Необхідно вирішити конфлікти вручну і вручну зателефонувати за допомогою git stash drop.

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

Спробуйте скопіювати все репо в новий редактор (щоб у вас була його копія) та запустіть:

git stash show і збережіть цей вихід десь, якщо вам це важливо.

тоді: git stash dropщоб викинути конфліктний скрипт:git reset HEAD

Це повинно залишити ваше РЕПО у тому стані, яке було раніше (сподіваюся, я досі не зміг спростувати вашу проблему)

===

Я намагаюся спростувати вашу проблему, але все, що я отримую, коли git stash popвикористовується:

error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting

У чистому режимі:

git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop

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


Спробуйте внести зміни в інший файл.
асмеурер

Спроба зберігати в іншому файлі не вдалося (див. Нові кроки повторного запиту). Я просто не можу спростувати цю проблему ...
DavidG

1
Це рішення не працює, якщо в робочому каталозі є незаперечні зміни.
тут

@here Це не справжнє рішення, це лише для того, щоб показати, що я не можу відтворити проблему, яку має ОП, і що він не зробив жодних кроків, щоб дістатися до місця, де він знаходиться.
DavidG

1
Фактичний сценарій: 1) Змінення файлу А. 2) Сховання змін 3) Зробити конфліктні зміни у файлі A та зробити (наприклад, змінити той самий рядок) 4) Змінити файл B 5) Зробити «git stash pop». Тепер у вас конфлікт та місцеві зміни. Як правило, вони будуть у різних файлах, але ви ніколи не дізнаєтесь, які безконфліктні модифіковані файли зі сховища та які локальні нестандартні зміни.
Дмитро

16

Я завжди користувався

git reset --merge

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


@GauravPaliwal, я перегляну це наступного разу git reset. Чи знаєте ви, чи він функціонально ідентичний git reset --merge?
Кенн Себеста

5

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

git reset .
git checkout .
git clean -f

4

Гаразд, я думаю, що мені вдалося знайти робочий потік, який поверне вас туди, де вам потрібно бути (як би ви не зробили попсу).

ВИКОНУЙТЕ РЕКЛАМ ПЕРЕГЛЯД !! Я не знаю, чи спрацює це для вас, тому скопіюйте всю репо-службу на випадок, якщо вона не працює.

1) Виправте проблеми з об'єднанням і виправте весь конфлікт, вибравши всі зміни, що надходять з патчу (у tortoisemerge це відображається як one.REMOETE (їх)).

git mergetool

2) Введіть ці зміни (вони вже будуть додані за допомогою команди mergetool). Дайте йому повідомлення про "злиття" або щось, що вам запам’яталося.

git commit -m "merge"

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

git add .
git add -u .
git commit -m "local changes"

4) Перевернути пластир. Це можна зробити за допомогою наступної команди:

git stash show -p | git apply -R

5) Зробіть ці зміни:

git commit -a -m "reversed patch"

6) Позбавтеся від патчів / unpatch зобов’язань

git rebase -i HEAD^^^

з цього, видаліть два рядки з "злиттям" і "зворотним виправленням" в ньому.

7) Поверніть свої незмінені зміни та скасуйте команду "локальні зміни"

git reset HEAD^

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


Якщо це не зовсім спрацює, сподіваємось, що ви отримаєте більшу частину шляху там! :-)
agentgonzo

git stash show -p | git apply -Rце не спрацьовує, якщо git stash applyсправжнє злиття. Дивіться мою відповідь ...
Бен Джексон

3

Я вирішив це дещо по-іншому. Ось що сталося

По-перше, я вискочив на неправильну гілку і у мене виникли конфлікти. Стек залишався недоторканим, але індекс був у вирішенні конфлікту, блокуючи багато команд.

Простий git reset HEADскасував розв’язання конфлікту і залишив без змінНЕБЕЗВЕЗДЕНО) ) зміни.

Кілька git co <filename>повернули індекс до початкового стану. Нарешті, я переключився на відділення git co <branch-name>і запустив нову git stash pop, яка вирішилася без конфліктів.


2

Деякі ідеї:

  • Використовуйте git mergetoolдля розділення файлів злиття на оригінальну та нову частини. Сподіваємось, одним із них є файл із вашими незмінними змінами.

  • Застосуйте різницю скриньки в зворотному порядку, щоб скасувати лише ці зміни. Напевно, вам доведеться вручну розділити файли з конфліктами злиття (що, сподіваємось, вищенаведений трюк спрацює).

Я не перевіряв жодного з них, тому не знаю точно, чи спрацює.


1

Я міг відтворити чисте git stash pop у "брудному" каталозі, із невідомими змінами, але ще не з'явився, що породжує конфлікт злиття.

Якщо в конфлікті злиття скрипт, який ви намагалися застосувати, не зник, ви можете спробувати перевірити git show stash@{0}(необов'язково з --oursабо --theirs) та порівняти з git statisі git diff HEAD. Ви повинні мати можливість бачити, які зміни відбулися в результаті застосування сховища.


1

Якщо DavidG правильний, що він не випустив скриньку через конфлікт злиття, тоді вам просто потрібно очистити робочий каталог. Швидко git commitвсе, що вам важливо. (Ви можете resetабо squashвзяти на себе зобов’язання пізніше, якщо ви не закінчите.) Потім з усім, про що ви дбаєте про безпеку, з git resetусім іншим, що git stash popпотрапило у ваш робочий каталог.


1

Якщо перед тим git stash pop, як у питанні, не було поетапних змін , то наступні дві команди повинні працювати.

git diff --name-only --cached | xargs git checkout --ours HEAD
git ls-tree stash@{0}^3 --name-only | xargs rm

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

З man git stash: The working directory must match the index. Що вказує @DavidG, stash popпомилка буде в разі конфлікту будь-яких нестандартних модифікованих файлів. Таким чином, нам не потрібно турбуватися про розкручування конфліктів злиття після повернення HEAD. Будь-які інші модифіковані файли потім не пов’язані зі сховищем та були змінені доstash pop

Якщо були поетапні зміни, мені незрозуміло, чи можемо ми покластися на одні і ті ж команди, і ви можете спробувати техніку @Ben Jackson. Пропозиції оцінені ..

Ось тестова установка для всіх різних випадків https://gist.github.com/here/4f3af6dafdb4ca15e804

# Result:
# Merge succeeded in m (theirs)
# Conflict in b
# Unstaged in a
# Untracked in c and d

# Goal:
# Reverse changes to successful merge m
# Keep our version in merge conflict b
# Keep our unstaged a
# Keep our untracked d
# Delete stashed untracked c

Я думаю, що це найправильніша відповідь досі. Дуже добре продуманий.
Агент П’ятниця

0

Використовуйте, git reflogщоб перелічити всі зміни, внесені в вашу історію git. Скопіюйте ідентифікатор дії та введітьgit reset ACTION_ID


2
Git не створює запис перемикань перед тим, як з'явитися (потрібно взяти на себе зобов’язання)
Casebash

-1

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

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