Ostatnio w pracy miałem pewien problem dotyczący znaków końca linii w Gicie. Skłoniło mnie to do poszukiwań w internecie, a zdobyte informacje postanowiłem zebrać w tym poście, żeby zostały na przyszłość.

Znaki końca linii na różnych systemach operacyjnych

Istnieją dwie konwencje dodawania znaków końca linii w plikach tekstowych.  Wywodzą się one z dwóch głównych systemów operacyjnych:

  • Konwencja Windowsowa: na końcu linii dodawane są znaki CR (Carriage Return – 0x0A ASCII) LF (Line Feed – 0x0D ASCII).
  • Konwencja Linuksowa: na końcu linii dodawany jest tylko znak LF.

Jeżeli więc nad tym samym projektem pracują osoby używające różnych systemów operacyjnych, pliki tworzone przez różne osoby mogą mieć różne konwencje końca linii. Co więcej, jeśli plik z linuksowymi znakami nowej linii jest edytowany w Windowsie, edytor tekstu może nadpisać istniejące znaki nowej linii w całym pliku lub użyć swojej konwencji dla nowo dodanych linii.

Jak git sobie z tym radzi?

Aby można było sobie poradzić z tym problemem, Git udostępnia mechanizm automatycznej konwersji znaków końca linii podczas commita i checkouta. Odpowiada za to zmienna konfiguracyjna core.autocrlf, którą można ustawić na trzy wartości:

  • true: Podczas commita we wszystkich plikach tekstowych CRLF jest konwertowane na LF. W repozytorium wszystkie znaki końca linii przechowywane są jako LF. Podczas checkouta we wszystkich plikach tekstowych LF jest konwertowane na CRLF. To ustawienie jest rekomendowane dla użytkowników Windowsa.
  • input: Podczas commita we wszystkich plikach tekstowych CRLF jest konwertowane na LF. W repozytorium wszystkie znaki końca linii przechowywane są jako LF. Podczas checkouta nie jest dokonywana żadna konwersja znaków końca linii. To ustawienie jest rekomendowane dla użytkowników Linuxa.
  • false: Podczas commita i checkouta nie jest dokonywana żadna konwersja. W repozytorium znaki końca linii są przechowywane w takiej samej formie w jakiej zostały scommitowane. To ustawienie wymaga samodzielnego radzenia sobie ze znakami końca linii i nie jest zalecane.

Aby podejrzeć swoje aktualne ustawienie, należy użyć komendy:

git config --global core.autocrlf

Aby zmienić ustawienie na Windowsowe:

git config --global core.autocrlf true

Jeżeli chcemy mieć dla jakiegoś projektu albo folderu ustawienia inne niż globalne, możemy skorzystać z pliku .gitattributes.

Rozwiązanie mojego problemu

Jeżeli mamy ustawienie core.autocrlf true, należy pamiętać, że podczas checkouta Git aktualizuje końce linii tylko tam, gdzie zostały dokonane zmiany. Czyli jeżeli lokalnie mamy pliki z innym znakiem końca linii, mogą one nie zostać podmienione. Zwykle to w niczym nie przeszkadza, bo nie jest to traktowane przez gita jako zmiany w pliku. Możemy więc mieć lokalnie takie pliki z innym znakiem końca linii i nigdy tego nie zauważyć.

Ja jednak musiałem wgrać kod ze swojego gitowego repo na SVN. SVN nie ma takiego inteligentnego mechanizmu obsługi znaku końca linii. Cała obsługa spada na użytkownika. Kiedy więc próbowałem zrobić commit na SVN, wyświetliło mi się wiele zmian w plikach, których nie ruszałem.

Aby poradzić sobie z tym problemem musiałem w lokalnym gitowym repo usunąć wszystkie pliki, w których występował ten problem, a następnie wywołać komendę:

git reset --hard HEAD

Dzięki temu pliki zostały odtworzone z repo i git wykonał automatyczną konwersję znaków końca linii.