GIT удаляет изменения конкретной фиксации из текущего HEAD

Предположим

У меня есть последние изменения, добавленные или не добавленные в индекс. Теперь я выбираю конкретную фиксацию, не создавая новую фиксацию в своей ГОЛОВЕ...

git cherry-pick -n <commit>

Как удалить из индекса ненужные изменения? я мог бы сделать

git reset HEAD

но мне придется повторить все изменения, которые я добавил ранее.


Цель

Если кто-то делает тайник, тайник не может быть перемещен на удаленный компьютер. Текущий WIP нельзя получить с пульта в другой системе для работы. Поэтому я написал функции оболочки для имитации git-stash, за исключением того, что я использую ветки для каждого тайника.

apply или pop обычно применяют спрятанные изменения к WIP, но не к текущему индексу. Пока я использую вишневый выбор для применения изменений из ветки stash, все эти изменения будут добавлены в индекс, и впоследствии мне нужно будет удалить их из индекса.


Изменить (2018-01-29)

Я прочитал ответ @torek и понял его. Тем не менее, мне нравится делиться своими функциями bash, которые я уже использовал раньше.

function git-stash {
    local gitbranch="$( git branch | grep \* )"
    local currentbranch="$( [ "${gitbranch}" == "* (HEAD"* ] && echo "${gitbranch}" | cut -d ' ' -f5 | cut -d ')' -f1 || echo "${gitbranch}" | cut -d ' ' -f2- )"
    local stashname="stash/$( date +%s )"
    git stash save -u ${stashname}
    git checkout -b ${stashname}
    git stash pop
    git add .
    [ ${1} ] && git commit -m "WIP: "$1 || git commit -m "WIP"
    git checkout ${currentbranch}
}

function git-stash-apply {
    local stashbranches="$( git branch | grep stash/ | cut -d ' ' -f3- | sort -r )"
    local stashbranches=(${stashbranches[@]})
    local lateststashbranch="${stashbranches[0]}"
    git cherry-pick -n "${lateststashbranch}"
}

function git-stash-pop {
    local stashbranches="$( git branch | grep stash/ | cut -d ' ' -f3- | sort -r )"
    local stashbranches=(${stashbranches[@]})
    local lateststashbranch="${stashbranches[0]}"
    git cherry-pick -n "${lateststashbranch}"
    git branch -D "${lateststashbranch}"
    git push origin :"${lateststashbranch}"
}

Это еще не правильное решение, не говоря уже об отсутствующей обработке ошибок в файле stash pop.


person codekandis    schedule 25.01.2018    source источник


Ответы (1)


Непосредственная проблема

В этом конкретном случае вы можете попробовать git revert -n <commit>, что в основном то же самое, что и применение этих изменений в обратном порядке. Однако, как правило, это необратимая операция, и в первую очередь неразумно выполнять git cherry-pick -n.

Рассмотрим, например, что произойдет, если дельта вычисляется следующим образом:

git diff $commit^ $commit

говорит, что git cherry-pick $commit должен добавить строку в README, удалить строку из f1.txt и изменить строку в f2.txt ("изменение" подразумевается удалением-старым-добавление-новым, на самом деле).

Но если вы уже добавили эту строку в README и внесли это изменение в f2.txt, на самом деле запуск вишневого выбора изменит только f1.txt. (Это происходит потому, что Git использует свой механизм слияния, который обнаружит, что ваши изменения и их изменения перекрываются, и, следовательно, уменьшит перекрытия.) Если вы сейчас решите отменить выбор вишни и запустить git revert -n $commit, Git отменит это, удаление строки из README, добавление строки обратно в f1.txt и восстановление исходной строки в f2.txt. Git не узнает, что операция слияния отбросила два из этих трех изменений как «уже на месте», и отменит все три.

Более общая проблема

Если кто-то делает тайник, тайник не может быть перемещен на удаленный компьютер.

Это не совсем так (но и не совсем неверно). Что делает git stash, так это делает два, а иногда и три коммита, ни один из которых не находится в ветке: один сохраняет текущий индекс, один сохраняет текущее рабочее дерево (но только для файлов, которые находятся в текущем индексе). Если существует третья фиксация, в ней хранятся неотслеживаемые файлы за вычетом игнорируемых файлов или неотслеживаемые файлы, включая игнорируемые файлы.

Коммиты устроены так, что фиксация рабочего дерева является последней сделанной, а две другие (плюс текущая фиксация) являются ее родителем. Затем хэш-идентификатор окончательного коммита «помещается» в стек тайника с помощью git update-ref.

Поскольку это коммиты, они могут быть git push-ed. Однако вы должны придумать для них имена на пульте дистанционного управления, которые пульт позволит вам установить. Имя refs/stash обычно недоступно для записи. Так, например:

git push fred stash:refs/heads/sneaky

создаст имя ветки sneaky на удаленном fred, используя refs/stash.

Можно отправить коммиты под другим именем, как указано выше, а затем, войдя в другую систему, переправить их под именем refs/stash, если вы хотите это сделать. Вам даже не нужно делать это, так как git stash apply и тому подобное будут принимать любой идентификатор, который разрешается в фиксацию, подобную тайнику (в частности, коммит рабочего дерева, который имеет либо двух родителей, если тайник является двумя -commit entity или три родителя, если тайник является сущностью с тремя фиксациями):

fred$ git stash apply sneaky

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

fred$ git branch -D sneaky

Еще одно техническое примечание

apply или pop обычно применяют спрятанные изменения к WIP, но не к текущему индексу.

Это также верно, если вы не используете опцию --index. В этом случае код тайника пытается восстановить индекс, используя git show <index-commit-hash> | git apply --index.

Применение такого тайника вызывает внутренний механизм слияния Git, хотя в целом вы можете получить очень похожий эффект, используя git apply -3. (Обратите внимание, что есть тонкие различия между этим и реальным вызовом выбора вишни, слияния или тайника.)

person torek    schedule 25.01.2018
comment
Очень хорошее техническое понимание того, как git обрабатывает пересекающиеся изменения. Я не думал об этом. Я постараюсь углубиться в оба последних технических примечания, чтобы понять их полностью. - person codekandis; 29.01.2018