Фатальный: путь '.eslintrc.json' существует на диске, но отсутствует в 'master~10'

При переходе от разработки к мастеру возникает ошибка на этапе распределенных задач в тестовом задании (в конвейере Gitlab). Локально или в разработке проблем с этим не было.

Я хотел бы понять, что на самом деле означает это сообщение об ошибке, что такое «диск» здесь и что именно означает этот master~10 (погуглив, я понял, что это означает «сравнить текущую нажатую ветку со всеми родительскими коммитами в master до 10", правильно ли это и зачем вообще это делать?)

как решить эту проблему и как отладить локально?


24
1

Ответ:

Решено

I'd like to understand what

fatal: Path '.eslintrc.json' exists on disk, but not in 'master~10'

actually mean[s], what is "disk" here and what does this master~10 mean exactly

Быть точным означает, что нам нужно начать с некоторого ключевого фона.

Фон

При использовании Git мы запускаем:

git checkout somebranch

или:

git switch somebranch

или иногда — системы CI/CD особенно любят это делать — мы могли бы использовать:

git checkout <hash-ID>

или:

git switch --detach <hash-ID>

Эти команды предписывают Git извлечь исходное дерево из определенного коммита и сделать этот коммит текущий коммит. Хэш-идентификатор этого коммита — это либо тот, который мы передаем Git напрямую — хэш-идентификатор сырой, как в случае git switch --detach hash-ID, — либо тот, который подразумевает какое-то имя ветки, когда мы используем имя ветки.

(Когда мы делать используем имя ветки, результатом команды checkout или switch будет сделать это текущая ветвь, а его самый последний или совет зафиксировать текущий коммит. Когда мы используем режим detached HEAD, мы получим ветку нет, но у нас все еще есть данный коммит как текущий коммит.Таким образом, у нас всегда есть текущий коммит, у нас также есть текущий название филиала, если и только если мы используем имя ветки в команде checkout/switch.)

Причина, по которой Git работает таким образом заключается в том, что Git точно не хранит файлы и не работает с ветви. Что Git хранит: совершает. Все коммиты имеют уникальные хеш-идентификаторы: это их «настоящие имена», так сказать. Каждый совершить хранит файлы: на самом деле, каждый коммит хранит полный набор файлов каждый в том виде, в каком он был в то время, когда вы (или кто-то другой) сделал этот коммит. Итак, вот как мы получаем файлы из из Git: они есть в каждом коммите, и мы выбираем нужный коммит и получаем эти файлы.

Проблема с использованием необработанных хеш-идентификаторов заключается в том, что это большие, уродливые, случайные вещи, которые люди обычно не в состоянии понять правильно. Люди не любят и не используют их. компьютер может их использовать — компьютеры хороши в таких вещах — но люди не будет. Людям нравятся названия веток имена:, такие как master или main, develop, feature/tall и так далее. Они хорошо работают для людей. Таким образом, помимо снэпшотов в коммитах, которые хранятся в виде большой базы данных внутренних объектов Git, индексированных по хэш-идентификатору, Git предоставляет базу данных, в которой Git хранит эти имена и связывает каждое имя с одним хэш-идентификатором.

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

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

  • У коммита есть исходный снимок, скорее, как у tar-архива, WinZip-архива или чего-то еще. (Однако, в отличие от некоторых форматов архивов, файлы в моментального снимка хранятся в специальном формате только для Git, где они не только сжаты, но и дедуплицированы для коммитов все, включая сам этот коммит.)

  • Коммит имеет метаданные или информацию о самом коммите. Метаданные включают, например, имя человека, совершившего фиксацию. Они включают сообщение журнала, которое написал коммиттер. Они включают в себя несколько отметок даты и времени, чтобы показать когда, что коммиттер сделал фиксацию.

Чтобы ветки Git работали, Git включает в метаданные каждого коммита список хэш-идентификаторов коммитов предыдущий. Этот список обычно состоит только из одного элемента: он дает хэш-идентификатор фиксации родитель этой фиксации.

Результатом этих родительских хэш-идентификаторов «обычной фиксации» является простая, линейная, но обратная цепочка. Давайте нарисуем его, заменив настоящие хеш-идентификаторы простыми одиночными заглавными буквами. Мы поместим самый последний коммит, чей хэш-идентификатор мы будем называть H, справа, здесь:

... <-F <-G <-H

Коммит H хранит снимок и метаданные, а в метаданных для H Git сохранил другой, более ранний хэш-идентификатор коммита. Этот хэш-идентификатор — G на нашей диаграмме, но на самом деле какая-то большая уродливая случайная вещь — является истинным именем коммита G в большой базе данных объектов Git, поэтому Git может использовать этот хэш-идентификатор для извлечения коммита G.

Коммит G, конечно же, является коммитом, поэтому у него есть и снимок, и метаданные, а в метаданных для G есть один хэш-идентификатор для более раннего коммита F. Таким образом, Git может прочитать G, чтобы найти хэш-идентификатор F, и использовать его, чтобы найти самого F.

Коммит F, конечно же, является коммитом, поэтому у него есть и снимок, и метаданные, и… ну, вы уже должны видеть, куда это идет. Повторно читая один коммит, включая его метаданные, Git может продолжать резервное копирование по истории, по одному коммиту за раз: от H мы добираемся до G, оттуда мы добираемся до F, оттуда мы добираемся (предположительно) до E, и так далее. на, назад во времени. В конце концов мы доберемся до самый первый коммит, который здесь будет коммитом A: этот коммит имеет список пустой предыдущих коммитов, потому что предыдущего коммита нет, и поэтому Git, наконец, может перестать двигаться назад.

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

Для фиксации найтиH — последней фиксации — Git сохраняет хэш-идентификатор из последней фиксации в имени. Когда это имя является именем ветка, мы можем дать его git checkout или git switch и получить «на ветке». Поскольку имя содержит необработанный хэш-идентификатор, мы нарисуем его в виде стрелки, указывающей на коммит:

...--G--H   <-- master

Поскольку имя ветки здесь master, имя master содержит необработанный хэш-идентификатор коммита H, по которому Git может найти более ранние коммиты.

Если и когда мы делаем ветвь новый, мы начинаем с нового имени, указывающего на какой-то существующий коммит. Мы можем выбрать любую фиксацию, но обычно мы начинаем с H в таком случае:

...--G--H   <-- develop, master

Когда мы используем git checkout или git switch, чтобы выбрать одно из этих имен, мы получаем прикрепленная ГОЛОВА, так что название является текущим название:.

...--G--H   <-- develop (HEAD), master

Это означает, что наша последняя проверка/переключение было на develop. У нас есть все файлы от commit H для работы.

Файлы в коммите Git доступны только для чтения. Уловка дедупликации/совместного использования работает только потому, что все части каждого коммита полностью доступны только для чтения (и только Git может на самом деле читать файлы, хранящиеся в коммите), поэтому Git должен скопируйте файлы из коммита, прежде чем мы сможем работать с ними или с ними. После их копирования эти файлы теперь доступны для использования. Git называет их «на диске» в вашем сообщении об ошибке.

Учитывая, что мы работаем над/с фиксацией H через develop, если мы сейчас изменим копию «на диске» некоторых файлов и git add и git commit, мы получим новый коммит, который мы можем назвать I, и Git сделает названиеdevelop указывает на новый коммит I:

          I   <-- develop (HEAD)
         /
...--G--H   <-- master

Имя develop теперь означает последнюю фиксацию develop, то есть фиксацию I. Имя master теперь означает последнюю фиксацию master, которая по-прежнему является фиксацией H. Если мы сделаем второй новый коммит на develop, мы получим:

          I--J   <-- develop (HEAD)
         /
...--G--H   <-- master

То есть имя develop теперь указывает на коммит J. Commit J указывает на коммит I, который указывает на коммит H, который указывает на коммит G и так далее.

Обратите внимание, что:

  • Теперь есть последние коммиты два: H — последний (на master) и J — последний (на develop). По определению, любой коммит, на который указывает имя ветки, является последним коммитом "в" этой ветке. Git обычно этого не делает, но мы можем заставить имя master указывать на G вместо H:

           H--I--J   <-- develop (HEAD)
          /
    ...--G   <-- master
    

    Если мы сделаем это (есть причины не делать этого!), то G будет последним на master, а коммиты H-I-J будут только на develop. Обратите внимание, что все коммиты находятся на develop. Если мы снова переместим master вперед на один прыжок к H, мы вернемся к предыдущей настройке, с коммитами до H и I-J только на develop.

  • Важны коммиты; имена веток просто помогают нам (и Git) найти в коммитах. Коммиты никогда не меняются, но названия веток меняются сильно. Набор коммитов, который находится «на» ветке, обычно увеличивается со временем: мы добавляем новые коммиты и/или у нас есть Git, который перемещает имя ветки «вперед».

Если мы вернемся к master, Git удалит из нашей рабочей области «на диске» все файлы из коммита J и вставит вместо них файлы из коммита H. Теперь мы можем создать вторую ветвь функций:

          I--J   <-- develop
         /
...--G--H   <-- feature (HEAD), master

и сделайте два коммита более:

          I--J   <-- develop
         /
...--G--H   <-- master
         \
          K--L   <-- feature (HEAD)

Мы можем только добавлять делать коммиты в репозиторий. Существующие коммиты не могут быть изменены. Новые коммиты указывают на существующие коммиты, так что есть история. История — это не что иное, как коммиты, которые можно найти, начав с некоторого имени ветки и двигаясь в обратном направлении. В коммитах хранятся файлы и метаданные; метаданные позволяют истории работать; и имена веток находят коммиты самый последний, от которых Git работает в обратном направлении.

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

what is "disk" here and what does this master~10 mean exactly

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

Git нашел .eslint.json в вашем рабочем дереве.

master~10 теперь можно объяснить с помощью графиков, которые мы рисовали. Предположим, у нас есть:

          I--J   <-- develop
         /
...--G--H   <-- master (HEAD)
         \
          K--L   <-- feature

Это означает, что мы «включены» master, используя коммит H. Имя master буквально означает «фиксирует H» всякий раз, когда мы используем его в контексте, в котором Git требуется совершить, например, если мы запускаем:

git diff master feature

или:

git show master

В этих случаях мы направляем Git смотреть коммиты H и L для команды git diff или коммит H и его родителя G для команды git show.

Добавление суффикса тильды ~, за которым следует необязательный номер, указывает Git переходить назад заданное количество раз. Число по умолчанию равно 1, но здесь у нас есть 10, так что это будет означать вернуться десять раз назад. Это слишком много для нашего примера, поэтому давайте вместо этого рассмотрим master~3:

  • начать с H
  • вернуться один раз: мы сейчас в G
  • вернитесь снова: мы сейчас в F, предположительно (хотя я этого не нарисовал)
  • вернуться назад в третий раз: мы сейчас в E, предположительно

поэтому master~3 выберет фиксацию E, предполагая что-то о коммитах, которые я не нарисовал.

Точно так же feature~3 будет означать:

  • начать с L
  • вернуться один раз к K
  • вернуться во второй раз к H
  • вернуться в третий раз к G

и, следовательно, это будет означать совершить G. И develop~3 будет начинаться с J, трижды возвращаться назад и снова заканчиваться коммитом G.

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

Таким образом, сообщение об ошибке, в котором говорится, что .eslintrc.json отсутствует в master~10, просто означает, что какой бы коммит вы ни выбрали с помощью этого суффикса ~10, примененного к master, тот фиксирует в нем нет .eslint.json. В файлах в коммите тот отсутствует файл .eslint.json.

Чтобы получить это конкретное сообщение, вы, вероятно, запустили бы:

git diff master~10

что означает сравнить файлы, хранящиеся в коммите, найденном через master~10, с файлами в рабочем дереве (на диске).