Git sta sh apply - git пытается объединиться перед применением? - PullRequest
1 голос
/ 08 февраля 2020

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

Если бы я остался sh изменения, продолжайте работать (после сохранения) в той же ветке и затем git -применить перед добавлением или фиксацией изменений, которые были сделаны после установки sh.

Как git обработает "применять" ? is apply = apply + пытаемся объединиться?

Что делает git внутренне при применении sta sh в репо?

Должны ли мы всегда фиксировать или сохранять sh наши изменения перед применением других сохраненных изменений в той же ветке?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 09 февраля 2020

TL; DR

Это довольно долго, поэтому позвольте мне высказать выводы и узнать, как я их получу.

Должны ли мы всегда совершать или сохранять sh наши изменения перед применением других сохраненных изменений в той же ветке?

Если вы не уверены, что sta sh будет применяться корректно, я рекомендую сделать это. Рассмотрите возможность превращения особенно сложных ситуаций sh в реальные ветви , используя git stash branch.

Обратите внимание, что некоторые версии git stash apply и git stash pop более осторожны, чем другие. (В частности, ограничение «Рабочий каталог должен соответствовать индексу» не выполняется для каждой версии Git!)

Long

Допустим, у меня есть локальное репо и внесли некоторые изменения - некоторые были добавлены в индекс, в то время как некоторые были только отслежены, но оставлены в рабочей области без добавления в индекс.

Слово track или отслежены имеет некоторые специфические c значения в Git, ни один из которых не подходит. В этом случае соответствующее определение слова отслеживается означает, что существует в индексе . Я собираюсь предположить, что все файлы в вашем рабочем дереве были "отслежены" в соответствии с этим определением, и вы имеете в виду, что вы сделали:

git check something
<edit several files>
git add <some of those files, but not all>

таким, что git status будет сказал, в этот момент:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   wt-status.c

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   wt-status.h

(это фактический результат команды git status, минус первые три строки).

Если бы я должен был sh изменения,

На данном этапе важно знать, что git stash делает.

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

  • снимок индекса и
  • снимок рабочего дерева.

(Поскольку они являются коммитами, у них также есть автор и коммиттер - которые, конечно, вы - и метки даты и времени, и сообщение журнала, и и т. д. Все они генерируются автоматически, за исключением того, что вы можете указать сообщение коммита как сообщение sta sh. В основном они не очень интересны.)

Помните, что индекс содержит предлагаемый следующий коммит. 1 То есть индекс никогда не будет 2 пусто . Просто после git checkout master или любой другой команды, которую вы используете для начала, индекс обычно соответствует текущему коммиту . Когда он совпадает, git status говорит, что нет изменений, которые нужно зафиксировать. То есть каждая копия файла в индекса совпадает с копией этого же файла в HEAD коммите.

Даже если каждый файл действительно соответствует так один из двух коммитов, которые делает git stash, будет по-прежнему иметь копию каждого файла. 3 Так что индексный коммит определенно не пустой . Просто его содержимое может соответствовать содержимому HEAD.


1 Во время конфликтующего слияния индекс расширяется и больше не работает как предлагаемый следующий коммит. В течение этого периода, однако, вы также не можете запустить git stash.

2 Ну, вряд ли когда-либо: вы можете иметь пустой индекс, если у вас вообще нет файлов, например .

3 Каждый коммит, который повторно использует файл в каком-то более раннем коммите, на самом деле делит файл с , что более ранний коммит. Таким образом, если вы делаете какой-либо новый коммит, который точно соответствует моментальному снимку какого-либо существующего коммита, новый коммит занимает почти нулевое дисковое пространство - ему нужно только место для хранения метаданных. Опять же, метаданные - это часть, в которой хранятся такие вещи, как ваше имя, дата и время и ваше сообщение журнала. Хранение этого файла часто занимает всего один дисковый «блок», независимо от размера блока для вашей системы. Это может быть всего 512 байт или 4 КБ, в зависимости от вашего минимального размера дискового блока - хотя на это тоже может влиять множество других факторов.

Вернуться к вашему вопросу

Если бы я должен был sh внести изменения, [и затем] продолжить (после того, как копил) одну и ту же ветку ...

Теперь важно знать, что git stash делает немедленно после совершения двух коммитов. Документация sta sh вызывает их I (для индекса) и W (для рабочего дерева); Мне нравится называть их одинаково, но строчными буквами, и рисовать их так:

...--F--G--H   <-- branch (HEAD)
           |\
           i-w   <-- refs/stash

Commits F, G и H - последние три на ветке, с H будучи верхушкой ветви. name branch идентифицирует коммит H.

Коммит i содержит все, что было в вашем index , что означает, что он содержит все файлы из HEAD коммит, перезаписывается любыми файлами, которые вы git add редактировали. В моем примере вывода git status, приведенном выше, у меня есть все файлы, составляющие источник Git, за исключением того, что я переписал wt-status.c новой версией. Коммит w содержит то, что вы получили бы , запустив git add -u для этого индекса. В этом случае это сохранит обновленный wt-status.c, который я уже скопировал в индекс (и находится в моем рабочем дереве на данный момент), и перезапишет H -коммитную копию wt-status.h той копией, которая у меня есть. в моем рабочем дереве.

Следовательно, после git stash, диффузия коммита i против H покажет, что wt-status.c отличается, а все остальное - то же самое. Отклонение коммита w от H показало бы, что и wt-status.c, и wt-status.h различны.

Последний акт git stash на этом этапе - запуск git reset --hard. Это заставляет индекс совпадать с фиксацией H снова, и мое соответствие по рабочему дереву фиксирует H. Все эти файлы отслеживаются (они все в индексе), но все три копии совпадают: те, что в H совпадают с теми, что в индексе, и те, что в Индекс совпадает с указанным в моем рабочем дереве.

... [и затем] продолжайте работать (после сброса) в той же ветке ...

Так что теперь в вашем рабочем дереве у вас есть несколько измененных файлов. Все эти файлы все еще отслеживаются - их копии есть в (единственном) индексе, но версии work-tree не копируются в индекс. Индекс соответствует фиксации H, но не соответствует рабочему дереву.

... и затем git -применить перед добавлением или фиксацией изменений, которые были сделаны после sta sh.

git apply - команда, но она не использует sta sh. git stash apply - это другая команда, хотя и несколько связанная. Я думаю, что вы имеете в виду: Что если бы я должен был запустить git stash apply сейчас, без добавления или фиксации?

Предполагая, что вы делаете значит git stash apply, у нас теперь есть чтобы получить больше осложнений. Применяется sta sh, на который указывает refs/stash. Вот эта пара i-w коммитов. 4 Вы можете указать git stash apply использовать оба коммита, но по умолчанию он использует только w коммит . 5

В любом случае, при условии отсутствия третьего коммита, git stash apply теперь продолжается w коммит. Он запускает git merge-recursive напрямую, минуя все проверки работоспособности, которые использует команда верхнего уровня git merge (по уважительной причине: например, мы не хотим фиксировать результат). Это выполняет то, что я люблю называть действием merge-as-a-verb . Следовательно, ответ на:

[пытается git stash apply] попытаться объединиться?

равен да, это .

Для каждого слияния в Git требуется три входа. Обычно все три входа совершают , 6 , но в этом случае один из них - ваше активное рабочее дерево. Это несколько опасно, потому что процесс слияния как глагол записывает в ваше рабочее дерево . Команда переднего конца git merge всегда настаивает на том, чтобы у вас было «чистое» рабочее дерево, так что написание на нем не уничтожает выполняемую работу. Запуск git merge-recursive напрямую, как git stash apply, обходит эту проверку безопасности. (К счастью, есть резервная проверка безопасности, но ее сложно описать.)

В любом случае, именно это сейчас и происходит. В git merge-recursive, который запускается git stash apply, указывается использовать родителя w коммита в качестве базы слияния . В приведенном мной примере родительским коммитом является commit H. Файлы этого коммита go в промежуточный слот индекса 1. Он использует текущий индексный контент в качестве "нашего" коммита, то есть эти файлы (из нулевого временного интервала текущего индекса) go в промежуточный индекс слот 2. Наконец, он использует коммит w в качестве «их», т. е. эти файлы попадают в промежуточный слот 3.

В этот момент вступают в силу действительно сложные правила слияния, потому что Git теперь имеет до трех записей индекса для каждого файла. Если все три совпадают, или даже если два совпадают, результат объединения будет простым, и Git не будет набрасываться на копии рабочего дерева. Только если все три различны, Git перезапишет файл рабочего дерева. В этом последнем случае начинается проверка безопасности резервного копирования: если Git должен перезаписать файл рабочего дерева, он должен соответствовать копии индекса. Если нет, то git merge-recursive сам прерывается и ничего не делает. 7 Итак, тот факт, что git stash apply исполняет этот сложный танец с вашим измененным, но никогда не работающим с git-add рабочим деревом, вероятно safe.

Этот процесс слияния как глагол имеет обычный результат: если Git может объединить изменения из базовой версии (подтвержденная копия, которая является родителем w), в w совершая изменения из базы в текущее дерево работы, вы получаете хорошее чистое слияние. Если нет, вы получите конфликты слияния, записанные в вашем текущем рабочем дереве.


4 Технически, имя stash указывает на коммит w. Код git stash находит другой коммит или два других коммита в зависимости от типа sta sh из коммита w.

5 Если у вас есть три sta sh commit, git stash apply настаивает на применении третьего коммита, также независимо от того, что вы делаете. Вы можете git stash apply игнорировать i коммит или использовать его, но если у вас третий u коммит, git stash apply попытается применить его. Это может быть довольно раздражающим. Здесь мы не будем go.

6 Это важно, потому что все коммиты всегда полностью доступны только для чтения . Ничто из того, что делает Git, не может перезаписать эти зафиксированные моментальные снимки: они безопасно восстанавливаются навсегда или, по крайней мере, до тех пор, пока существует сам коммит. Обратите внимание, однако, что git stash drop бросает i и w sta sh фиксирует в tra sh, после чего git gc может удалить их.

7 Здесь есть много угловых и краевых корпусов с обнаружением переименования. Возможно, что есть несколько пропущенных случаев. После более чем десяти лет использования Git это не все, что вероятно , но если вам все-таки удастся столкнуться с одним из них, тот факт, что это вряд ли, не поможет. : -)


Должны ли мы всегда фиксировать или сохранять sh наши изменения перед применением других сохраненных изменений в той же ветке?

Теоретически этого не должно быть. Но из-за всех описанных выше осложнений я рекомендую это сделать.

В общем, я рекомендую избегать git stash. Это полезно для быстрой работы иногда, но если выясняется, что то, что вам нужно сделать, в конце концов сложно, быстрые вещи, которые git stash сделал, могут быть неуместными.

Если вы делаете имейте стойкость sh для чего-то, что вы намеревались быть быстрым и легким, и это оказывается сложным, есть хорошее решение для этого. Во-первых, совершите или оставьте sh все, что вы находитесь в середине. (Если вы используете git stash, помните, что это может усложниться! ?) Затем вместо git stash apply используйте git stash branch.

Команда git stash branch принимает имя ветви. Это создаст новую ветку с этим именем. Вспомните, как мы нарисовали выше sh, затем рассмотрим эту картинку:

         L--M   <-- branch2
        /
       /           N--O   <-- branch3 (HEAD)
      /           /
...--F--G--H--J--K--P   <-- branch1
           |\
           i-w   <-- refs/stash

Здесь кто-то (вероятно, мы) сделали ставку sh, но затем забыли об этом и сделали больше коммитов. Нет никакой гарантии, что коммит w (по сравнению с его родительским H) может применяться только к любому коммитов M, O или P. Но вместо того, чтобы пытаться иметь дело с w как sta sh, мы можем превратить его в свою собственную ветку, используя git stash branch:

git stash branch xyzzy

Это находит i-w sta sh (через refs/stash), находит фиксацию H - что легко, поскольку w указывает непосредственно на H - и делает там новое имя ветви go. Так что теперь имя ветви xyzzy идентифицирует коммит H.

Мы (или git stash) теперь проверяем коммит H. Затем мы применяем индексный коммит i и коммит рабочего дерева w, используя git stash apply --index. Поскольку i и w были сделаны из H, все это go гладко. git stash branch теперь сбрасывает тайник; все готово для того, чтобы мы запустили git commit, чтобы сделать коммит из индекса, если это необходимо, затем запустить git add -u и зафиксировать снова (или впервые в новой ветке). В результате получается два коммита, если мы сначала зафиксируем индекс, или один коммит, который мы можем нарисовать так:

         L--M   <-- branch2
        /
       /           N--O   <-- branch3
      /           /
...--F--G--H--J--K--P   <-- branch1
            \
             I   <-- xyzzy (HEAD)

Вместо sta sh, с которым сложно работать, мы имеем обычный коммит (или, может быть, два, но я нарисовал только один). Все наши обычные инструменты работают с этим коммитом (или коммитами).

0 голосов
/ 08 февраля 2020

некоторые из них только отслеживались, но оставлялись в рабочей области без добавления в индекс

В этом случае нельзя использовать git stash apply, поскольку

Рабочий каталог должен соответствовать индексу.

https://git-scm.com/docs/git-stash

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...