«Изменения не подготовлены для коммита» даже после того, как git commit -am b / c origin имеет файл с заглавными буквами - PullRequest
0 голосов
/ 02 февраля 2019

Проблема: два файла под двумя разными именами в одном каталоге, который я не знал в первый раз.Поэтому я был очень удивлен, увидев это,

git commit -am "why"
On branch tmp
Changes not staged for commit:
    modified:   src/view/callCenter/seatReport/SeatSubstate.vue

Тогда я обнаружил, что у источника есть и SeatSubstate.vue & seatSubstate.vue в пути src/view/callCenter/seatReport

Но на моем Mac

ls src/view/callCenter/seatReport/
...     seatSubstate.vue /* did NOT show SeatSubstate.vue only seatSubstate.vue */

Я знаю, что есть дискуссия о Как я могу зафиксировать только изменения имени файла в Git с учетом регистра?

НоЯ до сих пор не понимаю, почему git не может зафиксировать этот файл.

Во-вторых, как мне решить эту проблему?Например, в этом обсуждении SO многие ответили упомянутое git mv, но я не уверен, git mv может решить мою проблему или нет.

----- update -----

Я внезапно понял, что мой Mac (мой HD, чтобы быть точно) не чувствителен к регистру (APFS), см. https://apple.stackexchange.com/questions/71357/how-to-check-if-my-hd-is-case-sensitive-or-not.

enter image description here

Обычно это должно означать, что SeatSubstate.vue и seatSubstate.vue - это один и тот же файл, но каким-то образом git делает их 2 разными файлами и вызывает проблемы.git mv, кажется, решает проблему, но я не уверен на 100%.

См. Изменение заглавных букв имен файлов в Git

1 Ответ

0 голосов
/ 02 февраля 2019

Правильное определение проблемы

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.

...