Konflikty scalania w Git
W systemach kontroli wersji chodzi o zarządzanie wynikami prac wielu rozproszonych autorów (zazwyczaj programistów). Czasami wielu programistów może podjąć próbę edytowania tej samej zawartości. Jeśli programista A próbuje edytować kod edytowany przez programistę B, może wystąpić konflikt. Aby ograniczyć występowanie konfliktów, programiści pracują w odrębnych, odizolowanych gałęziach. Podstawową funkcją polecenia git merge
jest łączenie odrębnych gałęzi i rozwiązywanie wszelkich sprzecznych zmian.
Informacje na temat konfliktów scalania
Konflikty zazwyczaj pojawiają się, gdy dwie osoby zmienią te same wiersze w pliku lub gdy jeden programista usunie plik modyfikowany przez innego programistę. W takich przypadkach Git nie jest w stanie automatycznie wskazać poprawnej wersji. Konflikty dotyczą wyłącznie programisty, który przeprowadza operację scalania, a reszta zespołu nie jest ich świadoma. Git oznaczy plik jako źródło konfliktu i wstrzyma proces scalania. Wówczas to programista będzie musiał rozwiązać konflikt.
Rodzaje konfliktów scalania
Konflikt w trakcie scalania może wystąpić na dwóch różnych etapach: podczas uruchamiania procesu scalania i w trakcie jego realizacji. Poniżej omówiono sposoby postępowania w przypadku każdego z tych scenariuszy.
Niepowodzenie podczas uruchamiania scalania w Git
Rozpoczęcie scalania w Git będzie niemożliwe, jeśli w katalogu roboczym lub przechowalni bieżącego projektu znajdują się zmiany. Git nie uruchomi scalania, ponieważ te oczekujące zmiany mogłyby zostać nadpisane scalanymi commitami. W takim przypadku przyczyną nie jest konflikt z pracą innych programistów, tylko z oczekującymi zmianami lokalnymi. Wówczas należy ustabilizować stan lokalny za pomocą polecenia git stash
, git checkout
, git commit
lub git reset
. Niepowodzenie przy próbie rozpoczęcia scalania spowoduje wyświetlenie następującego komunikatu o błędzie:
error: Entry '<fileName>' not uptodate. Cannot merge. (Changes in working directory)
Niepowodzenie w trakcie scalania w Git
Niepowodzenie W TRAKCIE scalania wskazuje na konflikt między bieżącą gałęzią lokalną a gałęzią scalaną. Oznacza to konflikt z kodem innych programistów. Git postara się jak najlepiej scalić pliki, pozostawiając jednak zmiany w plikach powodujących konflikt do ręcznego rozwiązania. Niepowodzenie w trakcie scalania spowoduje wyświetlenie następującego komunikatu o błędzie:
materiały pokrewne
Zaawansowany dziennik Git
POZNAJ ROZWIĄZANIE
Poznaj środowisko Git z rozwiązaniem Bitbucket Cloud
error: Entry '<fileName>' would be overwritten by merge. Cannot merge. (Changes in staging area)
Wywoływanie konfliktu scalania
Aby zapoznać się z konfliktami scalania w rzeczywistej sytuacji, w kolejnej sekcji zostanie utworzona symulacja konfliktu do późniejszego przeanalizowania i rozwiązania. W przykładzie do wykonania przykładowej symulacji zostanie użyty interfejs wiersza poleceń Git, który jest podobny do tego w systemie Unix.
$ mkdir git-merge-test
$ cd git-merge-test
$ git init .
$ echo "this is some content to mess with" > merge.txt
$ git add merge.txt
$ git commit -am"we are commiting the inital content"
[main (root-commit) d48e74c] we are commiting the inital content
1 file changed, 1 insertion(+)
create mode 100644 merge.txt
Ten przykładowy kod wykonuje sekwencję poleceń pozwalających przeprowadzić następujące operacje:
- Utworzenie nowego katalogu o nazwie
git-merge-test
, przejście do tego katalogu i zainicjowanie go jako nowego repozytorium Git. - Utworzenie nowego pliku tekstowego
merge.txt
z pewną zawartością. - Dodanie pliku
merge.txt
do repozytorium i zatwierdzenie go.
W ten sposób otrzymaliśmy nowe repozytorium zawierające jedną gałąź main
oraz plik merge.txt
z pewną zawartością. Następnie utworzymy nową gałąź, którą wykorzystamy do wywołania konfliktu scalania.
$ git checkout -b new_branch_to_merge_later
$ echo "totally different content to merge later" > merge.txt
$ git commit -am"edited the content of merge.txt to cause a conflict"
[new_branch_to_merge_later 6282319] edited the content of merge.txt to cause a conflict
1 file changed, 1 insertion(+), 1 deletion(-)
Sekwencja powyższego polecenia spowodowała przeprowadzenie następujących operacji:
- Utworzenie i wyewidencjonowanie nowej gałęzi o nazwie
new_branch_to_merge_later
. - Zastąpienie zawartości w pliku
merge.txt
. - Zatwierdzenie nowej zawartości.
W tej nowej gałęzi new_branch_to_merge_later
utworzyliśmy commit, który zastępuje zawartość pliku merge.txt
.
git checkout main
Switched to branch 'main'
echo "content to append" >> merge.txt
git commit -am"appended content to merge.txt"
[main 24fbe3c] appended content to merge.tx
1 file changed, 1 insertion(+)
Ten łańcuch poleceń powoduje wyewidencjonowanie gałęzi main
, dołączenie zawartości do pliku merge.txt
i zatwierdzenie go. W ten sposób w naszym przykładowym repozytorium znajdują się 2 nowe commity. Jeden w gałęzi main
i jeden w gałęzi new_branch_to_merge_later
. Wykonajmy zatem polecenie git merge new_branch_to_merge_later
i zobaczmy, co się stanie.
$ git merge new_branch_to_merge_later
Auto-merging merge.txt
CONFLICT (content): Merge conflict in merge.txt
Automatic merge failed; fix conflicts and then commit the result.
BUM 💥. Wystąpił konflikt. Dzięki, Git, że dałeś nam o nim znać!
Identyfikowanie przyczyn konfliktów scalania
Jak można było zobaczyć w poprzednim przykładzie, Git wygeneruje opisowy komunikat informujący o wystąpieniu konfliktu. Aby uzyskać więcej szczegółów, możemy wykonać polecenie git status.
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: merge.txt
Dane wyjściowe uzyskane po wykonaniu polecenia git status
wskazują, że w wyniku konfliktu powstały niescalone ścieżki. Plik merge.text
jest teraz wyświetlany w stanie zmodyfikowanym. Przyjrzyjmy się zatem plikowi i zobaczmy, co zostało zmodyfikowane.
$ cat merge.txt
<<<<<<< HEAD
this is some content to mess with
content to append
=======
totally different content to merge later
>>>>>>> new_branch_to_merge_later
Użyliśmy tutaj polecenia cat
, aby wyświetlić zawartość pliku merge.txt
. Pojawiło się kilka nowych, dziwnych dodatków.
<<<<<<< HEAD
=======
>>>>>>> new_branch_to_merge_later
Potraktujmy te nowe wiersze jako „linie podziału konfliktu”. Wiersz =======
stanowi „centrum” konfliktu. Cała zawartość umieszczona między centrum a wierszem <<<<<<< HEAD
to zawartość, która występuje w bieżącej gałęzi głównej, na którą wskazuje wskaźnik HEAD
. Z kolei cała zawartość między centrum a wierszem >>>>>>> new_branch_to_merge_later
to zawartość, która występuje w naszej scalanej gałęzi.
Rozwiązywanie konfliktów scalania za pomocą wiersza polecenia
Najbardziej bezpośrednim sposobem rozwiązania konfliktu scalania jest wprowadzenie zmian w pliku powodującym konflikt. Otwórz plik merge.txt
w ulubionym edytorze. Na potrzeby naszego przykładu usuńmy po prostu wszystkie linie podziału konfliktu. Zmodyfikowana zawartość pliku merge.txt
powinna wyglądać następująco:
this is some content to mess with
content to append
totally different content to merge later
Po wprowadzeniu zmian w pliku wykonaj polecenie git add merge.txt
, aby umieścić nowo scaloną zawartość w przechowalni. Aby sfinalizować scalanie, utwórz nowy commit, wykonując polecenie:
git commit -m "merged and resolved the conflict in merge.txt"
Git zobaczy, że konflikt został rozwiązany i utworzy nowy commit scalenia, aby sfinalizować scalanie.
Polecenia Git ułatwiające rozwiązywanie konfliktów scalania
Narzędzia ogólne
git status
Pracując w Git, często używa się polecenia „status”, które w przypadku scalania ułatwia identyfikację plików powodujących konflikt.
git log --merge
Przekazanie argumentu --merge
do polecenia git log
spowoduje utworzenie dziennika zawierającego listę commitów, które powodują konflikt między scalanymi gałęziami.
git diff
Polecenie diff
pomaga odszukać różnice między stanami repozytorium/plików. Jest to przydatne przy przewidywaniu konfliktów scalania i zapobieganiu ich występowania.
Narzędzia przydatne, gdy Git nie może rozpocząć scalania
git checkout
Za pomocą polecenia checkout
można cofnąć zmiany w plikach lub zmienić gałęzie.
git reset --mixed
Polecenie reset
pozwala cofnąć zmiany w katalogu roboczym i przechowalni.
Narzędzia przydatne przy rozwiązywaniu konfliktów Git w trakcie scalania
git merge --abort
Wykonanie polecenia git merge
z opcją --abort
zakończy proces scalania i przywróci gałąź do stanu przed rozpoczęciem procesu scalania.
git reset
Polecenia git reset
można użyć w przypadku konfliktu scalania do zresetowania powodujących konflikt plików do znanego, prawidłowego stanu.
Podsumowanie
Konflikty scalania bywają trudnym doświadczeniem. Na szczęście Git oferuje zaawansowane narzędzia ułatwiające identyfikowanie i rozwiązywanie konfliktów. Git jest w stanie poradzić sobie samodzielnie z większością przypadków scalania, wykorzystując funkcje automatycznego scalania. Konflikt powstaje, gdy jeden wiersz w pliku został zmodyfikowany z poziomu dwóch różnych gałęzi lub gdy plik został usunięty z jednej gałęzi, ale edytowano go w drugiej. Konflikty występują najczęściej podczas pracy w środowisku zespołowym.
Istnieje wiele narzędzi ułatwiających rozwiązywanie konfliktów scalania. Git zapewnia wiele narzędzi wiersza polecenia, które tutaj omówiliśmy. Szczegółowe informacje na temat tych narzędzi można znaleźć na osobnych stronach dotyczących poleceń git log, git reset, git status, git checkout i git reset. Oprócz narzędzi dostępnych w Git istnieje także wiele rozwiązań innych firm usprawniających obsługę konfliktów scalania.
Udostępnij ten artykuł
Następny temat
Zalecane lektury
Dodaj te zasoby do zakładek, aby dowiedzieć się więcej na temat rodzajów zespołów DevOps lub otrzymywać aktualności na temat metodyki DevOps w Atlassian.