Прекращение загрузки тайника в Git

257

Я открыл тайник, и произошел конфликт слияния. В отличие от вопроса, который указан как дубликат, у меня уже были некоторые незафиксированные изменения в каталоге, которые я хотел сохранить. Я не просто хочу, чтобы конфликт слияния исчез, но и чтобы вернуть мой каталог в состояние, в котором он был до появления.

Я пытался git merge --abort, но Git утверждал, что слияние не происходит. Есть ли простой способ прервать всплывающее окно, не разрушая изменения, которые у меня были изначально в каталоге?

Casebash
источник
Что касается ваших незафиксированных изменений: были ли эти изменения уже в индексе?
Йоргенсен
Можете ли вы опубликовать версию Git, которую вы используете.
Tinman
2
Принятый ответ выглядит сложным. Я думаю, что это хорошая практика - никогда не пытаться делать что-то наподобие git stash popнечистого рабочего режиссера. В этом случае вы можете просто git reset --hardи ваш тайник все еще не поврежден. (Это более или менее то, что предлагает связанная тема @ BradKoch)
Стивен Лу
1
@ StevenLu, я согласен, но проблема может возникнуть в чистом рабочем каталоге, если вы храните изменения, чтобы переместить их в другую ветку. Тайник конфликтует с коммитами, существующими в новой ветке, которых не было в старой.
Джейк Стивенс-Хаас

Ответы:

55

Хорошо, я думаю, что я разработал "git stash unapply". Это более сложно, чем 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

Легко.

jmoz
источник
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

Это должно оставить ваше репо в том состоянии, в котором оно было раньше (надеюсь, я все еще не смог воспроизвести вашу проблему)

===

Я пытаюсь повторить вашу проблему, но все, что я получаю, когда usin 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

Я не вижу мерзавца, пытающегося объединить мои изменения, он просто терпит неудачу. Есть ли у вас какие-либо шаги по репро, которые мы можем выполнить, чтобы помочь вам?

DavidG
источник
Попробуйте сохранить изменения в другом файле.
asmeurer
Попытка сохранить в другой файл не сработала (см. Новые шаги по воспроизведению). Я просто не могу воспроизвести проблему ...
DavidG
1
Это решение не работает, если в рабочем каталоге есть незафиксированные изменения.
здесь
@ здесь Это не реальное решение, это просто чтобы показать, что я не могу воспроизвести проблему, с которой сталкивается ОП, и что он не предоставил никаких шагов, чтобы добраться туда, где он находится.
DavidG
1
Фактический сценарий: 1) Изменение файла A. 2) Сохранение изменений 3) Создание конфликтующих изменений в файле A и фиксация (например, изменение той же строки) 4) Изменение файла B 5) Выполнение 'git stash pop'. Теперь у вас есть конфликт и локальные изменения. Обычно они находятся в разных файлах, но вы никогда не узнаете, какие неконфликтующие измененные файлы из stash, а какие являются локальными неустановленными изменениями.
Дмитрий
17

Я всегда использовал

git reset --merge

Я не могу вспомнить, чтобы это никогда не подводило.

Кенн Себеста
источник
@GauravPaliwal, я посмотрю в следующий раз git reset. Знаете ли вы, если он функционально идентичен git reset --merge?
Кенн Себеста
5

Если вам не нужно беспокоиться о любых других внесенных вами изменениях и вы просто хотите вернуться к последнему коммиту, тогда вы можете сделать:

git reset .
git checkout .
git clean -f
Hugo der Hungrige
источник
4

Хорошо, я думаю, что мне удалось найти рабочий процесс, который вернет вас туда, где вы должны быть (как будто вы не сделали поп-музыку).

СДЕЛАТЬ РЕЗЕРВНОЕ КОПИРОВАНИЕ ДО Я не знаю, сработает ли это для вас, поэтому скопируйте весь репозиторий на тот случай, если он не сработает.

1) Исправьте проблемы слияния и исправьте все конфликты, выбрав все изменения, которые появятся в патче (в tortoisemerge это отображается как единое целое. 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) Избавиться от патча / распаковать коммиты

git rebase -i HEAD^^^

из этого удалите две строки с «слияния» и «обратный патч» в нем.

7) Верните свои неизмененные изменения и отмените фиксацию «локальных изменений».

git reset HEAD^

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

agentgonzo
источник
Если это не сработает, надеюсь, это поможет вам в этом! :-)
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для разделения файлов слияния на оригинальные и новые части. Надеюсь, одним из них является файл с вашими изменениями, не являющимися тайниками.

  • Примените diff тайника в обратном порядке, чтобы отменить только эти изменения. Возможно, вам придется вручную разбить файлы с конфликтами слияния (что, надеюсь, сработает вышеупомянутый прием).

Я не проверял ни один из них, поэтому я не знаю точно, будут ли они работать.

asmeurer
источник
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сбрасывали в рабочем каталоге.

robrich
источник
1

Если до git stash pop, как в вопросе, поэтапных изменений не было , то должны работать следующие две команды.

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

Первый отменяет любые слияния из тайника, удачные или нет. Второй удаляет все неотслеживаемые файлы, введенные тайником.

From 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 не создает запись reflog до того, как вы выскочите (вам нужно сделать коммит)
Casebash
-1

Я пишу здесь в надежде, что другие найдут мой ответ полезным. У меня была похожая проблема, когда я пытался сделать треск в другой ветке, чем тот, из которого я спрятал. В моем случае у меня не было файлов, которые были незафиксированы или находятся в индексе, но все же попали в случай конфликтов слияния (тот же случай, что и @pid). Как уже отмечалось ранее, неудачный всплеск git stash действительно сохранил мой stash, затем быстрый сброс git git plus, возвращаясь к моей первоначальной ветке и выполняя оттуда сундук, решил мою проблему.

Виктор Камачо
источник