Потерянные отслеживаемые файлы при выполнении git stash --include-unraracked - PullRequest
1 голос
/ 25 октября 2019

когда я сделал git status, есть как отслеженные, так и неотслеживаемые файлы. Рано днем ​​я только что узнал, что git stash --include-untracked будет хранить неотслеживаемые файлы. Это работало для меня в то время. Поэтому я подумал, что git stash --include-untracked сохранит изменения как отслеженных, так и неотслеживаемых файлов. Но когда я git stash apply, остается только изменение неотслеживаемых файлов. Изменения отслеживаемых файлов потеряны.

1 Ответ

1 голос
/ 26 октября 2019

Здесь есть что-то подозрительное, но это, вероятно, не сам тайник

git stash --include-untracked, который можно кратко записать git stash -u, делает три коммитов для тайника.

Первые два - это те же два, что и обычно: один для хранения всего, что было в индексе в то время, когда вы запустили git stash, а другой для хранения того, что было в рабочем дереве - но только для отслеживаемых файлов - вто время. Другими словами, фиксация i, содержащая индекс, содержит результат git write-tree, а фиксация w содержит результат (эквивалент) git add -u && git write-tree (хотя код stash делает это сложным образом, илиделал в старые времена в хранилище сценариев оболочки).

Это все, что было бы в хранилище, если бы вы запустили git stash без --all или --include-untracked: у него было бы двакоммиты для i (состояние индекса) и w (состояние рабочего дерева), оба из которых имеют текущий коммит C в качестве первого родителя. Commit w имеет i в качестве второго родителя:

...--o--o--C   <-- HEAD
           |\
           i-w   <-- stash

Если вы do добавите -u или -a, однако вы получите three -commit stash: commit w получает третьего родителя, фиксацию, которую мы можем назвать u, которая содержит неотслеживаемые файлы. У этого третьего родителя нет своего родителя (это сирота / корневой коммит), поэтому чертеж теперь выглядит так:

...--o--o--C   <-- HEAD
           |\
           i-w   <-- stash
            /
           u

Интересная вещь об этом новом коммите и его эффекте в рабочем дереветакже: * Коммит u содержит только неотслеживаемых файлов. **

Помните, что фиксация - это полный и полный снимок всех (отслеживаемых) файлов. Фиксация u производится во временном индексе , отбрасывая все отслеживаемых файлов и, вместо этого, отслеживая некоторые или все неотслеживаемых файлов. Этот шаг добавляет только файлы без отслеживания, но не игнорирования (git stash -u) или все файлы (git stash -a). Затем Git записывает коммит u, используя git write-tree, чтобы превратить временный индекс в дерево для фиксации u, чтобы коммит u содержал только выбранных файлов.

Теперь, когда эти выбранные файлы находятся в коммите u, git stash удаляет их из рабочего дерева . На практике раньше просто запускали git clean с соответствующими параметрами. Новый поклонник C-кодированный git stash по-прежнему делает аналог (но, можно надеяться, с меньшим количеством ошибок; см. Ниже).

Это похоже на то, что он делает для файлов в i и /или w: он эффективно выполняет git reset --hard, так что отслеживаемые файлы рабочего дерева соответствуют фиксации HEAD. (То есть он делает это, если вы не используете --keep-index, в этом случае он сбрасывает файлы в соответствии с фиксацией i.) git reset на данный момент не влияет на неотслеживаемых файлов,которые выходят за рамки git reset и не влияют на текущую ветвь, поскольку сброс преднамеренно сохраняет это в HEAD.

При этом некоторые неотслеживаемые файлы были сохранены в коммите u, однако, git stash затем удаляет эти файлы из рабочего дерева. Это очень важно позже (и, возможно, также сразу).

Примечание: произошла ошибка при объединении git stash push со спецификациями пути, которая потенциально затрагивает все, но особенно затрагивает варианты хранения, созданные с помощью-u или -a, где некоторые версии Git удаляют слишком много файлов. То есть, вы можете git stash просто подмножество ваших файлов, но тогда Git будет git reset --hard или git clean всех файлов или слишком много файлов. (Я полагаю, что все они исправлены сегодня, но в целом я вообще не рекомендую использовать git stash, и особенно не причудливые варианты pathspec. Удаление неперехваченных файлов, которые на самом деле не были сохранены, является особенно вопиющим поведением, и некоторые версии Git делают это!)

Вы описываете apply временную проблему, но, возможно, не обычную

Вот что вы сказали:

Я думал, что git stash --include-untracked сохранит изменения как отслеженных, так и неотслеживаемых файлов.

Как всегда, Git не сохраняет изменений , он сохраняет снимков.

Но когда я делаю git stash apply, меняются только неотслеживаемые файлы. Изменения в отслеживаемых файлах будут потеряны.

Применение стандартного тайника (без отслеживания файлов) выполняется одним из двух способов, в зависимости от того, используется ли флаг --index. Вариант без --index легче объяснить, поскольку буквально игнорирует коммит i. (Вариант с флагом --index сначала использует git apply --index в diff, и, если это не удается, предлагает попробовать без --index. Если вы хотите с эффектом --index, этоужасный совет, и вы должны его игнорировать. Однако для этого ответа давайте полностью проигнорируем параметр --index. *

Примечание: это не флаг --keep-index, а скорее--index флаг. Флаг --keep-index применяется только тогда, когда создает тайник. Флаг --index применяется, когда применяет тайник.

Чтобы применить коммит w, Git напрямую запускает git merge-recursive. Это не то, что вы должны когда-либо делать как пользователь, и когда git stash делает это, это не совсем так мудро, но это то, что он делает. Эффект во многом похож на выполнение git merge, за исключением того, что если у вас есть незафиксированные изменения в вашем индексе и / или рабочем дереве, может оказаться невозможным вернуться в это состояние каким-либо автоматическим способом.

Однако, если вы начнете с «чистого» индекса и рабочего дерева, то есть если git status говорит nothing to commit, working tree clean, то эта операция слияния почти точно такая же, как и обычная git merge или git cherry-pick, во многих отношениях. ,(Обратите внимание, что и git merge, и git cherry-pick требуют , чтобы все было чисто, по крайней мере по умолчанию.) Операция слияния выполняется с базой слияния , установленной в родительский элемент commit w, текущий или --ours коммит является текущим коммитом как обычно, а другой или --theirs коммит является коммитом w.

То есть предположим, что ваш коммитГрафик теперь выглядит так:

       o--o--A--B   <-- branch (HEAD)
      /
...--o--o--C
           |\
           i-w   <-- stash
            /
           u

, так что вы находитесь на коммите B. Операция слияния для применения stash выполняет трехстороннее слияние с C в качестве базы слияния и w в качестве --theirs коммита, и с текущим коммитом / рабочим деревом в качестве --ours коммита. Git diffs C против B, чтобы увидеть, что мы изменили, и C против w, чтобы увидеть, что они изменились, и объединяет два набора различий.

Так будет выполняться слияние с B, при условии , что Git может сначала разблокировать коммит u. Обычная проблема на этом этапе заключается в том, что Git не может un-stash u.

Помните, что commit u содержит точно (и только) unracked файлы, которые присутствовали при создании тайника и которые Git затем удалял с помощью git clean (и соответствующих опций). Эти файлы все еще должны отсутствовать в рабочем дереве. Если они не отсутствуют, git stash apply не сможет извлечь файлы из u и не будет продолжать работу.

Поскольку неотслеживаемые файлы не отслеживаются, трудно понять, изменились ли они

Но когда я 'git stash apply`, остается только изменение неотслеживаемых файлов. Изменения в отслеживаемых файлах потеряны.

Вы говорите о изменениях в неотслеживаемых файлах.

Git, конечно, не сохраняет изменения, поэтому вы можете 'найти их таким образом. И если файлы не отслеживаются, они тоже не включены в индекс. Итак: откуда вы знаете, что они изменены? Вам нужен какой-то другой набор файлов, с которым их можно сравнить.

Шаг, который извлекает коммит u, должен быть полностьюили-ничего: он должен либо извлечь все файлы u, либо нет. Если он извлекает все u файлы, git stash apply должен продолжить попытки слияния, как бы с помощью git cherry-pick -n (за исключением того, что cherry-pick также пишет в индекс), зафиксировать w в тайнике. Это должно оставить вас с извлеченными u файлами и объединенными w -vs- C изменениями в вашем рабочем дереве.

Если существуют конфликты между C -vs-work-tree и C -vs- w, у вас должны быть маркеры конфликта, присутствующие в рабочем дереве, и ваш индекс должен быть расширен как обычно дляконфликтное слияние

...