Правильное определение проблемы
Git равен всегда , способен хранить - в коммитах и в индексе Git, то есть - два файла под двумя разными именами -случаи (например, README
и readme
) в одном и том же каталоге, потому что Git вообще не хранит файлы в каталогах операционной системы.Файлы либо фиксируются в коммитах, 1 , что означает, что они сохраняют свою форму независимо от того, находятся ли они в Linux, Windows, MacOS или любой другой системе, или они находятся в индексе Git, который на самом деле является просто данными.файл. 2
Проблема возникает из-за того, что вы, человек, работающий с Git, хотите использовать предоставляемую ОС файловую систему, где ваш компьютер хранит файлы в их обычной повседневной форме, чтобы остальныевашего компьютера может работать с ними тоже.Это не является необоснованным требованием - внутренние файлы Git хранятся во внутренней форме только для Git, которую может использовать только Git.Вы должны иметь возможность использовать Git, чтобы что-то делать , а не просто играть с Git весь день.
MacOS имеет возможность предоставлять чувствительные к регистру файловые системы (которые могут содержать какREADME
и readme
в том же каталоге), но не делает это по умолчанию.Итак, либо вообще не используя MacOS, либо используя эту способность, кто-то , а не вы, совершил такую вещь:
Затем я обнаружил, что источник имеетоба SeatSubstate.vue & seatSubstate.vue в пути src/view/callCenter/seatReport
Другими словами, у вас есть оба файла внекоторые существующие commit .Как мы только что сказали, Git вполне способен справиться с этим.Это не ваша ОС.
Так что, если вы запустите git checkout
и выберите для этой фиксации, Git скопирует оба файла в ваш индекс, который теперь имеет оба варианта написания , SeatSubstate.vue
и seatSubstate.vue
.Он также копирует оба файла (с обоими написаниями!) В ваше рабочее дерево, но ваша ОС может содержать только одно правописание, поэтому один файл стирает другой, и у вас остаетсяпросто один файл с написанием one .
Когда Git сравнивает файлы индекса и их содержимое с файлами рабочего дерева и их содержимым, Git будет:
- обратите внимание, что в соответствии с индексом существует два файла;
- попробуйте сравнить каждый файл индекса с файлом рабочего дерева, который получает Git при открытии этого имени;
- жалуйтесь, что один из них изменен.
Вот пример, который я сделал, создав репозиторий в системе Unix-y и предоставив ему два файла, README
и readme
, с разнымисодержимое, затем клонируем это на Mac:
sh-3.2$ git clone ssh://[path]/caseissue
...
Receiving objects: 100% (4/4), done.
sh-3.2$ cd caseissue
sh-3.2$ ls
readme
Давайте посмотрим, что находится в индексе:
sh-3.2$ git ls-files --stage
100644 a931371bf02ce4048b623c56beadb9a926138516 0 README
100644 418440c534135db897251cc3ceca362fe83c2117 0 readme
Конечно, у него есть два файла, отличающиеся только регистром,Давайте посмотрим, что в этих файлах, а что в рабочем дереве:
sh-3.2$ git show :0:README
I AM AN UPPERCASE FILE
sh-3.2$ git show :0:readme
i am a lowercase file
sh-3.2$ cat readme
i am a lowercase file
И наш статус:
sh-3.2$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: README
no changes added to commit (use "git add" and/or "git commit -a")
В зависимости от того, что нам нужно сделатьмы можем сделать это, пока знаем только о индексе, или нам может потребоваться работать напрямую с индексом, что более болезненно.
1 Технически содержимое замороженных файлов хранится в объектах BLOB-объектов , их имена хранятся в объектах дерева , а коммиты - commitобъекты , которые ссылаются на объекты дерева, которые ссылаются на объекты BLOB-объектов.Но с точки зрения пользователя файлы замораживаются в коммите, поэтому мы можем просто использовать эту фразу здесь.
2 Индекс может фактически быть несколькими различными файлами данных, и выможет указать Git на альтернативные индексные файлы и делать с ним все возможные трюки.Вот как, например, git stash
работает.Но «индекс» - это то, где Git создает следующий коммит, который вы сделаете , и для наших целей это просто файл .git/index
.
Что делать с этим, если вы не нужен ни один файл
Let 'Предполагается, что вам не нужно работать с или файлом.Если вам нужно работать с обоими файлами с учетом регистра, чтобы вы могли возиться с содержимым двух отдельных файлов с именами SeatSubstate.vue
и seatSubstate.vue
, вам, очевидно, потребуетсянастроить файловую систему с учетом регистра.Но что бы вы ни делали, мы можем предположить, что вам не нужен файл или для выполнения работы.
Хитрость, которую нужно использовать здесь, состоит в том, чтобы начать с , удалив один оставшийся файл из вашего рабочего дерева, а затем игнорируйте тот факт, что Git сообщает вам, что у вас есть два изменения, которые не подготовлены для фиксации.То есть Git скажет вам, что вы удалили оба файла.
sh-3.2$ rm readme
sh-3.2$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: README
deleted: readme
no changes added to commit (use "git add" and/or "git commit -a")
Теперь просто не используйте git commit -a
вообще , потому что этобудет ставить оба удаления.Вместо этого работайте с оставшимися файлами (в моем случае, вообще ни с одним), делайте все, что вам нужно, и ставьте - git add
- только те файлы, которые вы изменили , не нажимая удалил файл любым способом.
Теперь вы можете git commit
результат, не затрагивая два файла, которые отсутствуют в вашем рабочем дереве, но все еще присутствуют в новом коммите, который вы делаете:
sh-3.2$ echo 'this file is independent of the READMEs' > newfile
sh-3.2$ git add newfile
sh-3.2$ git commit -m 'add new file'
[master 6d5d8fc] add new file
1 file changed, 1 insertion(+)
create mode 100644 newfile
sh-3.2$ git push origin master
Counting objects: 3, done.
...
2dee30f..6d5d8fc master -> master
Перейдите на другой (файловая система с учетом регистра) после обновления до этого коммита:
$ ls
newfile readme README
$ for i in *; do echo -n ${i}: && cat $i; done
newfile:this file is independent of the READMEs
readme:i am a lowercase file
README:I AM AN UPPERCASE FILE
Так что мы вполне способны работать на нашем Mac (или Windows!)), с этими коммитами: мы просто удаляем ненужные файлы и тщательно избегаем постановки удалений.
Что с этим делать, если вам нужен один из файлов , но donне нужно его менять
Теперь проблема немного сложнее, потому что в нашей работе без учета регистра нельзя удерживать оба файла с обоими вариантами написания -дерево на нашем компьютере Mac или Windows.
Но мы можно выбрать и какой файл мы получим!Допустим, нам нужен файл README
.Мы можем видеть, что вместо этого мы получили файл readme
выше.Итак, мы удалим неправильный (ну, мы уже сделали), а затем:
sh-3.2$ git checkout -- README
sh-3.2$ ls
README newfile
sh-3.2$ cat README
I AM AN UPPERCASE FILE
Если нам понадобится вместо этого строчный:
sh-3.2$ rm README
sh-3.2$ git checkout -- readme
sh-3.2$ ls
newfile readme
sh-3.2$ cat readme
i am a lowercase file
То естьмы удаляем неправильный один, а затем используем захват одного файла из операции индекса - git checkout -- <em>path</em>
- чтобы получить один файл с одним случаем, который мы делаем хочу.Теперь мы можем работать с этим файлом.Но мы не можем добавить или изменить его.
Что если вам нужно оба файла или вам нужно поработать над одним из них?
Если вам нужно оба нав то же время с причудливым наименованием у вас проблемы, потому что ваша ОС буквально не может сделать это - по крайней мере, не в этой файловой системе;вам нужно создать файловую систему с учетом регистра, после чего вся эта проблема исчезнет.Но если вам нужно всего лишь по одному , чтобы внести какие-то изменения, это то, чем мы можем управлять, хотя и очень неловко.
Во-первых, давайте отметим, чтоВы можете легко получить один или оба файла содержимое :
sh-3.2$ git show :README
I AM AN UPPERCASE FILE
sh-3.2$ git show :readme
i am a lowercase file
(Примечание: строки :0:README
и :README
означают абсолютно одинаково для git show
:получите файл из нулевого слота индекса под именем README
. Вы можете перенаправить вывод с git show
на любое имя файла, чтобы вы могли получить оба содержимого в два файла с именами, которые ваша ОС считает «разными».можно использовать :README
или :0:README
в качестве аргумента для git show
. Я не всегда согласен с тем, использовать ли здесь индексный номер в форме с префиксом :
. Причина, по которой это форма :0:
состоит в том, что в индексе также есть слоты 1, 2 и 3 ступеней, используемые только во время слияния, то есть, если в индексе есть :1:README
, это базовая копия слияния README
; у вас будет это во время конфликтующего слияния.)
Как мы виделиКроме того, вы также можете удалить файл рабочего дерева и использовать git checkout -- <path>
, чтобы получить один из них, с выбранным вами вариантом, в ваше рабочее дерево с таким же случаем.К сожалению, если вы хотите изменить и повторно добавить файл, это не всегда работает:
sh-3.2$ rm readme
sh-3.2$ git checkout -- README
sh-3.2$ echo UPPERCASE IS LIKE SHOUTING >> README
sh-3.2$ git add README
sh-3.2$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: README
Хлоп!Похоже, что Git решил, что файл README
в рабочем дереве должен обновить нулевой этап readme
файл в индексе!И, конечно же, это именно то, что сделал Git:
sh-3.2$ git show :0:README
I AM AN UPPERCASE FILE
sh-3.2$ git show :0:readme
I AM AN UPPERCASE FILE
UPPERCASE IS LIKE SHOUTING
Так что теперь мы должны прибегнуть к инструменту, который позволяет нам писать непосредственно в индекс.Во-первых, давайте удалим это изменение и вернемся в состояние «чистой очистки», где у нас нет копии рабочего дерева. ПРИМЕЧАНИЕ: если ваша реальная работа более сложная, чем у меня, вы можете сохранить все это где-нибудь еще до того, как git reset
уничтожит ее!
sh-3.2$ git reset --hard
HEAD is now at 6d5d8fc add new file
sh-3.2$ rm readme
sh-3.2$ git status --short
D README
D readme
Вывод --short
здесь, который имеет символ D
во второй позиции, показывает, что оба файла отсутствуют в рабочем дереве, но что индексная копия соответствует копии HEAD
.Итак, теперь мы можем получить нужный файл, какой бы он ни был - я выберу заглавную снова, так как в прошлый раз она пошла не так:
sh-3.2$ git checkout -- README
sh-3.2$ cat README
I AM AN UPPERCASE FILE
Теперь мы используем обычные компьютерные инструменты для работы сfile:
sh-3.2$ echo UPPERCASE IS LIKE SHOUTING >> README
Когда нам нужно добавить его обратно , мы должны использовать git hash-object -w
и git update-index
:
sh-3.2$ blob=$(git hash-object -w README)
sh-3.2$ echo $blob
fd109721431e207046a4daefc9712f1424d7f38f
(echo
здесь просто для иллюстрации, чтобы показать, что мы получили хеш-идентификатор).Теперь нам нужно сделать правильно отформатированную индексную запись, например git ls-files --stage --full-name
.То есть нам нужен полный путь к файлу относительно вершины дерева.Поскольку мои README
и readme
файлы в верхушке дерева, в моем случае здесь это просто означает README
или readme
.Для вашего примера, где ваши два файла были в src/view/callCenter/seatReport
, вам нужно будет включить это в путь.
В любом случае, записав объект blob в базу данных Git, теперь нам нужно обновитьзапись указателя:
sh-3.2$ printf '100644 %s 0\tREADME\n' $blob | git update-index --index-info
sh-3.2$ git status --short
M README
M readme
Это показывает, что у нас есть одно изменение для коммита - до README
- и одно - до readme
.Вот длиннее git status
, если вы предпочитаете это:
sh-3.2$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme
Более непосредственно, мы можем использовать git show
, чтобы посмотреть, что в индексе:
sh-3.2$ git show :README
I AM AN UPPERCASE FILE
UPPERCASE IS LIKE SHOUTING
sh-3.2$ git show :readme
i am a lowercase file
Это то, что мы хотим!Так что теперь мы можем git commit
результат:
sh-3.2$ git commit -m 'annotate README'
[master ff51464] annotate README
1 file changed, 1 insertion(+)
sh-3.2$ git push origin master
Counting objects: 3, done.
...
6d5d8fc..ff51464 master -> master
В Unix-подобной системе:
$ for i in *; do echo -n ${i}: && cat $i; done
newfile:this file is independent of the READMEs
readme:i am a lowercase file
README:I AM AN UPPERCASE FILE
UPPERCASE IS LIKE SHOUTING
Вы всегда можете использовать git hash-object -w
и git update-index --index-info
Если ваша ОС не способна записать имя файла или пути так, как это делает индекс Git, вы все равно можете работать с содержимым файлов , под любыми именами, которые вы можете использовать,Сделав это, вы можете использовать git hash-object -w
, чтобы превратить содержимое в замороженный большой двоичный объект, готовый к принятию, а затем использовать git update-index --index-info
, чтобы записать этот хэш большого двоичного объекта в индекс - в желаемом промежуточном интервале, обычно в ноль - по путиимя, которое нужно Git.
В этом процессе вы отказываетесь от умения разумно использовать git status
, использовать git add
для проблемных имен файлов и вообще использовать git commit -a
.Что нужно Git, чтобы сделать это более удобным - хотя это никогда не будет на 100% удобным;для этого вам нужно, чтобы ваша ОС вела себя лучше - это способность переотображать Git-пути индекса к (различным) локальным путям ОС в обоих направлениях: индексный файл с именем IP для некоторого индексного пути IP не следует предполагать, что оно имеет такое же имя в рабочем дереве, а скорее его сопоставленное имя.Сопоставленное имя должно однозначно отображаться на путь индекса.(То есть сопоставление должно представлять собой биекцию по путям.)
Это необходимо не только для проблем со свертыванием регистра, но и для проблем с Unicode: MacOS сохраняет имена файлов в одной форме,нормализовав их, в то время как Linux позволяет хранить имена файлов в каждой форме.Файл с именем agréable
может иметь два имени в Linux, но только одно в MacOS.