Как увидеть неотслеживаемые файлы в Git тайниках - PullRequest
4 голосов
/ 14 февраля 2020

Я регулярно сохраняю sh работу, которую выполнил в новых (неотслеживаемых) файлах, и надеюсь, что смогу найти эту работу позже. Очевидный способ найти это, кажется, git show.

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

Чтобы сделать это более конкретным, предположим, что у меня есть

On branch feature/request-reappointment
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:   app/models/job.rb
    modified:   config/initializers/environmental_email_interceptor.rb

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    app/models/receipt.rb
    db/migrate/20200130091050_create_receipts.rb

no changes added to commit (use "git add" and/or "git commit -a")

Если я выполню

git stash save -u 'miscellaneous improvements'
git show stash@{0}

Git, то пропущу неотслеживаемые файлы. Это не только поведение Git из командной строки, но и GUI инструменты, такие как Fork и GitKraken, имеют ту же проблему, предположительно унаследованную от Git.

Как я могу показать фактическое содержимое sta sh, , включая неотслеживаемых файлов?

ОБНОВЛЕНИЕ:

, хотя GitKraken и Fork не удалось отобразить неотслеживаемые файлы в любых тайниках Башня 2 (старая версия, поскольку я отказался переходить на модель оценки на основе подписки) успешно . Я должен отдать им должное, и если бы не цена, я бы купил новую копию сегодня.

1 Ответ

2 голосов
/ 14 февраля 2020

TL; DR

Чтобы увидеть имена файлов, используйте git ls-tree -r stash^3; чтобы увидеть их содержимое, используйте git show stash^3.

Long

Как мне показать фактическое содержимое sta sh, , включая неотслеживаемые файлы?

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

Во-первых, важно понять, как Git работает внутри. Хранилище Git в основном представляет собой пару баз данных. Один - самый большой, почти всегда - содержит коммитов и другие Git объектов. Меньшая база данных содержит имена: имена веток, такие как master, имена тегов, такие как v2.1, имена для удаленного отслеживания, такие как origin/master, и так далее. Имена ветвей и имена тегов и тому подобное - это конкретные c формы общего назначения ref или ссылка . Ссылка содержит один Git га sh идентификатор, чаще всего это коммит га sh идентификатор. 1

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

Итак, имя ветки просто содержит ha sh ID коммита, который, как мы хотели бы сказать, является последним коммитом в / on / of эта ветка. Несколько имен могут выбрать один и тот же идентификатор ha sh, и / или идентификатор ha sh может быть достижимым из какой-либо ветви, начиная с коммита наконечника и работая в обратном направлении. Таким образом, коммиты в Git часто находятся в нескольких ветвях.

В самих коммитах содержатся снимки всех ваших (отслеживаемых) файлов. Git делает их, записывая отслеживаемые файлы в Git index , а затем используя git write-tree, который записывает файлы во внутренние объекты дерева, затем git commit-tree, который записывает объект фиксации используя дерево (и), написанное git write-tree. Таким образом, все коммиты имеют свое происхождение в индексе. И любой файл в индексе отслеживается по определению. Таким образом, это приводит нас к некоторой загадке.


1 Имена филиалов и имена удаленного отслеживания необходимы для хранения только совершить га sh идентификаторов; имена тегов более гибки.


sta sh

Специальный ref refs/stash, если он существует, указывает на один коммит. Это stash@{0} коммит. (Его записи reflog в stash@{1} on up также указывают на один коммит каждый.) Таким образом, sta sh, когда он существует, состоит из коммитов. Эти коммиты находятся на ветке no : 2 вместо них они найдены через refs/stash.

Обычный sta sh имеет форму коммит слияния, но не то же самое вещество. git stash делает два коммита, используя метод очень низкого уровня git write-tree и git commit-tree, описанный выше. Первый такой коммит, основанный на том, что находится в вашем индексе, когда вы запускаете git stash save или git stash push, прост: git write-tree уже просто записывает то, что находится в индексе, поэтому две команды, вместе взятые, делают этот индекс фиксации , который я называю i (а документация git stash вызывает I).

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

Поскольку git stash использует низкоуровневые команды, оно может заставить коммит рабочего дерева - я называю это w - с любым набором родителей, которых он выберет. Он делает этот коммит с обоими i и HEAD в качестве двух его родителей:

...--o--o   <-- branch (HEAD)
        |\
        i-w

Коммит i выглядит как любой обычный коммит, а коммит w напоминает слияние. На самом деле это не слияние - это просто снимок вашего рабочего дерева, добавленный в индекс, но он имеет в качестве первого родителя коммит-наконечник вашей текущей ветви, а в качестве второго родителя - коммит i.

После этого sta sh, git stash save делает git reset --hard, так что ваш индекс и рабочее дерево совпадают HEAD. Сам HEAD никогда не перемещается, а неотслеживаемые файлы не сохраняются и не затрагиваются.

Однако, когда вы делаете git stash save -u или git stash save -a, Git делает третью commit, который я называю u. Этот третий коммит использует индекс, который очищается от всех отслеживаемых файлов, а затем загружается с некоторыми или всеми вашими неотслеживаемыми файлами, как если бы это было git add --force по указанным c именам файлов. 3 Набор файлов, которые go в этом третьем коммите, зависит от того, использовали ли вы -u или -a: -u перечисляет неотслеживаемые файлы, которые not игнорируется, а -a перечисляет все неотслеживаемые файлы, даже если они игнорируются . Git копирует эти файлы в (ну, в) индекс и делает этот u коммит без родителя вообще, за до он делает окончательный w коммит. Затем он делает w коммит с u в качестве третьего родителя:

...--o--o   <-- branch (HEAD)
        |\
        i-w
         /
        u

Таким образом, w^1 - это коммит в конце ветви, w^2 - это индексный коммит i и w^3 - это коммит u. w продолжает напоминать коммит слияния - на этот раз слияние осьминога - но его снимок тот же, что и для любого двухкоммитового sta sh.

Сделав u коммит, git stash save или git stash push сейчас удаляет из вашего рабочего дерева все файлы, которые находятся в коммите u. Если некоторые файлы в u также игнорируются, они все равно удаляются.

Применение трехкомпонентного sta sh завершается неудачно, если Git не может извлечь коммит u в текущее рабочее дерево. , Так что определенно полезно знать, что находится в этом третьем коммите u. Но нет git stash ____ (заполнить пробел глаголом), чтобы показать, существует ли коммит u, тем более, что в нем. Поэтому мы должны прибегнуть к командам Git более низкого уровня.

В частности, поскольку u является root commit , git show будет отличаться от пустое дерево . Вы можете использовать git show --name-only или git ls-tree -r, чтобы получить список файлов, если вам не нужен полный diff. name commit u, мы можем назвать любой sta sh commit - любой из w commit объектов, на которые указывает refs/stash или одну из записей reflog для него - и добавить ^3 суффикс для обозначения третьего родителя . Если у sta sh есть только w и i, ^3 потерпит неудачу: третьего родителя нет и, следовательно, нечего показать.


2 Вы можете, если хотите, на самом деле получить их на ветке, но результат ... в лучшем случае уродлив.

3 Внутренне git stash использует временный индекс вместо реальный / основной индекс, чтобы сделать это проще. Он делает это и для коммита w. Хотя существует один специальный индекс, индекс , который отслеживает ваше рабочее дерево, вы можете в любое время создать временный индекс, поместить его путь в GIT_INDEX_FILE и использовать с ним команды Git. временный индекс вместо использования выделенного индекса. Это удобно для любой команды, которая должна создать коммит, для которого требуется использование индекса, который не хочет нарушать индекс в процессе.

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