Как я могу унифицировать различия разрешений файлов между ветвями? - PullRequest
0 голосов
/ 25 октября 2018

Я что-то напутал и не совсем уверен, как или как это исправить.У меня есть ветвь A и ветвь B, где у A есть то, над чем я работаю.По какой-то причине разрешения для файлов с исчисляемым количеством файлов, которые я не собирался изменять, различаются в моей ветви A. В ветви B предусмотрены соответствующие разрешения для всех этих файлов.Есть ли способ получить git, чтобы установить его так, чтобы файлы, в которых единственная разница между A и B заключалась в разрешениях, были настроены на разрешения, найденные в B?

Короче, я как бы хочуиметь B заменить A, оставив только мои предполагаемые добавления, удаления и модификации файлов.

1 Ответ

0 голосов
/ 26 октября 2018

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

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

  1. Git сохраняет коммиты.Вы, наверное, уже знаете это, но давайте просто запишем это.:-) Каждый коммит уникально идентифицируется большим уродливым хеш-идентификатором, deadcab1feeddad2... или чем-то еще.Эти коммиты, однажды сделанные, никогда не могут быть изменены.Каждый коммит имеет некоторые метаданные - например, ваше имя и адрес электронной почты, отметку времени и ваше сообщение журнала, а также хранит необработанный хэш-идентификатор своего родительского коммита (или для коммитов слияния, оба / все родители).

  2. Передает файлы хранилища как снимок каждого файла, который был в Git в index в то время, когда вы запускали git commit.Индекс - причина, по которой вы должны продолжать работать git add.Файлы, хранящиеся в коммите, находятся в специальной сжатой форме, предназначенной только для Git, где они замораживаются до тех пор, пока существует коммит.

    Когда вы извлекаете коммит, Git извлекает весь снимок вindex, так что теперь индекс совпадает с фиксацией. 1 Это «размораживает» файлы: как только они попадают в индекс, вы можете изменить их.Но они все еще в специальном формате Git-only.Теперь Git извлекает индекс в рабочее дерево, превращая файлы в их полезную форму.

    Файлы в рабочем дереве 1027 * имеют разрешения.Файлы в index имеют только один бит разрешения, а именно: Должен ли этот файл быть исполняемым? По историческим (и Linux) причинам Git представляет это как mode 100644, "не исполняемый ", или mode 100755," исполняемый ", но в действительности он допускает только один бит флага. rest разрешений рабочего дерева происходит от того, что вы установили для своей системы / себя при извлечении файлов.

    Когда вы запускаете git add для файла, вы действительноГоворя Git: Скопируйте файл рабочего дерева в индексную копию. Этот Git-файл отправляет данные, готовя их к замораживанию в коммит.Он также устанавливает режим в зависимости от того, является ли файл исполняемым в данный момент.

    Обычно git diff сообщит вам, если есть изменение режима с 100644 на 100755 или наоборот, поэтому вы можете использовать git diff длясравнить.Поскольку существует три копии каждого файла - зафиксированного, проиндексированного и рабочего дерева - вам нужно два git diff с, чтобы найти все изменения:

    • Чем отличается фиксация от индекса?
    • Чем отличается индекс от рабочего дерева?


    Вы можете запустить git diff --cached или git diff --stagedчтобы получить первый набор, и git diff без параметров, чтобы получить второй набор.(Обратите внимание, что если режим выполнения изменился, Git будет считать, что файл будет изменен.) Команда git status запускает обоих diff, но суммирует их так, что режим не отображается явно.


1 Это не совсем точно (см. Извлечение другой ветки, когда есть незафиксированные изменения в текущей ветке для деталей),но помогает в качестве стартовой ментальной модели.


Ветвь B имеет предполагаемые права доступа ко всем этим файлам.

Здесь нам также нужно немного обойтитот факт, что ветви не так много значат в Git.(См. Также Что именно мы подразумеваем под "ветвью"? .)

В Git ветвь name - я буду использовать branch-A и branch-B здесь для двух имен - просто идентифицирует один конкретный коммит .

I meВыше отмечалось, что каждый коммит имеет уникальный хэш-идентификатор.Однако несколько имен могут иметь одинаковый хэш-идентификатор.Так начинаются новые филиалы.Между тем каждый коммит «указывает назад» на своего родителя, как начинается новая ветвь:

A  <-B  <-C   <--master

Здесь есть только одна ветвь, master.У него есть хеш-идентификатор commit C.Это последний коммит, содержащийся в ветви (а также последний коммит в репозитории, в данном случае).Тем временем commit C запоминает хэш-идентификатор B, который запоминает идентификатор A.A является самым первым коммитом, поэтому у него нет родителя, и действие здесь останавливается.

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

A  <-B  <-C  <-D   <--master

Поскольку внутренние стрелки не могут измениться (они являются частью коммитов!), нам просто нужно помнить, что они всегда указывают назад (влево на этих рисунках).Итак, в какой-то момент у нас есть некоторый хеш коммита, для краткости назовем его H, в котором мы создаем имя branch-A:

...--H   <-- branch-A

Если мы также создадим имя branch-B сейчас, мыиметь оба имени, указывающих на H:

...--H   <-- branch-A, branch-B

Это означает, что обе ветви содержат одинаковых коммитов (начиная с H и работая в обратном направлении).Снимок в H будет точно совпадать со снимком в H, разумеется, как в файловых режимах, так и в содержимом.Таким образом, между этими двумя коммитами не может быть различий.

Следовательно, если вы видите разные разрешения для файлов при проверке branch-A против при проверке branch-B, они должны указывать на разные коммиты , иначе флаги исполняемых / неисполняемых файлов будут совпадать.

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

...--H--I--J   <-- branch-A
      \
       K--L   <-- branch-B

В коммите J теперь права доступа неправильные,и исправить в коммите L: но верны они или нет в коммите I, который также только на branch-A?Вы хотите сделать новый коммит, похожий на I, за исключением прав доступа?Если вы это сделаете, вы также должны сделать замену для J, это очень похоже на J, но использует замену I в качестве родителя.

Если достаточно просто сделать * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *. *В конечном итоге вы получите:

...--H--I--J--M   <-- branch-A
      \
       K--L   <-- branch-B

, где commit M соответствует фиксации J во всем, кроме сохраненного исполняемого бита для каждого такого файла.

Просмотр режимов в любом коммите

Чтобы увидеть имена файлов и их режимы для любого данного коммита, используйте git ls-tree с опцией -r (рекурсивный).Просто определите фиксацию, которую вы хотите:

git ls-tree -r <hash>

Вывод - одна строка на файл фиксации.Поскольку Git хранит только файлов, рекурсивный листинг по умолчанию не содержит промежуточных поддеревьев, необходимых для внутреннего представления Git, и вы получите список всех blob объектов - это внутреннее представление Git дляфайлы - за исключением двух особых случаев: символические ссылки и подмодули (технически, gitlinks на этом уровне).Если у вас нет ни одного из них, вы можете игнорировать особые случаи.

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

100644 blob 12a89f95f993546888410613458c9385b16f0108    .clang-format
100644 blob 49b30516419c8dfe8c039ef368a3af984439ebcc    .gitattributes
100644 blob 64e605a02b71c51e9f59c429b28961c3152039b9    .github/CONTRIBUTING.md
100644 blob adba13e5baf4603de72341068532e2c7d7d05f75    .github/PULL_REQUEST_TEMPLATE.md

Первое поле - строка mode.Вторым всегда является blob (за исключением тех особых случаев, которых у вас, вероятно, нет, но если вы это сделаете, отфильтруйте все не-blob-строки)Третий - внутренний хэш-идентификатор для снимка файла, а в последнем поле после буквенного символа вкладки ASCII указывается путь к файлу, как показано на этом снимке.

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

git ls-tree -r HEAD

Я мог бы также использовать git ls-tree -r master или git ls-tree -r f84b9b09d40408cf91bbc500d9f190a7866c3e0f: все они идентифицируют commit f84b9b09d40408cf91bbc500d9f190a7866c3e0f, который является вершиной ветви master.В вашем случае вы можете использовать git ls-tree -r branch-B, чтобы получить все элементы режима и имени из коммита подсказки branch-B.

Изменение режимов рабочего дерева и фиксация

На этом этапетогда вам просто нужно проверить текущий совет branch-A.Затем из режимов и имен вы можете использовать любую команду «изменить режим файла» на вашем компьютере, чтобы установить желаемый режим рабочего дерева.git add все обновленные файлы рабочего дерева, чтобы скопировать их новые режимы в индекс и запустить git commit.Например, если вы работаете в системе Linux, вы можете использовать chmod напрямую (примечание: это имеет небольшой недостаток, если у вас есть файлы, имена которых содержат пробел, так как read не будет уважать его должным образом, если вы не поиграетес IFS):

git checkout branch-A
git ls-tree -r branch-B | while read mode ftype hash path; do
    [ $ftype = blob ] && chmod "$path" $mode
done
git add -u
git commit

Это приведет к жалобам на любые файлы в коммите, имена которых branch-B не существуют в коммите, извлеченном git checkout branch-A.Если у вас есть файлы с пробелами, вы также можете получить здесь ошибки, поэтому обязательно проверьте.

Вы можете придумать это по мере необходимости, но это суть.Обратите внимание, что если вы хотите перестроить существующие коммиты, вы можете использовать интерактивный git rebase, чтобы делать это по одному коммиту за раз.

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