Co zrobić gdy pracowaliśmy na złej gałęzi

TL;DR - kliknij aby rozwinąć

 

Cześć,

bardzo się cieszę, że odwiedziłeś mój blog. Chiałbym Ci opowiedzieć o problemie jaki miałem w pracy, oraz o tym jak go rozwiązałem.

Otóż,po paru godzinach zorientowałem się, że zmiany wprowadzałem na złej gałęzi. Niestety, kiedy próbowałem zmienić ją na właściwą otrzymałem znane wielu osobom ostrzeżenie:

~/Documents/GitTest (master)
$ git checkout dev
error: Your local changes to the following files would be overwritten by checkout:
 index.html
Please commit your changes or stash them before you switch branches.
Aborting

Jego powodem jest to, że gałęzie aktualna i ta na którą próbujemy się przełączyć są niezgodne, a zmiana jednej na drugą mogła by spowodować problemy.  Żeby pokazać Ci dlaczego tak się dzieje i jak to naprawić stworzyłem syntetyczny przypadek:

Na początek tworzymy prosty plik html:

<html>
<head></head>
<body>
Wersja pierwsza
</body>
</html>

oraz nowe repozytorium GIT z jednym commitem:

 ~/Documents/GitStash (master)
$ git init
Initialized empty Git repository in C:/Users/user/Documents/GitStash/.git/

 ~/Documents/GitStash (master)
$ git add *

 ~/Documents/GitStash (master)
$ git commit -m firstCommit
[master (root-commit) 8b7de5c] firstCommit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 index.html

Teraz zrobimy sobie drugi branch, na którym będziemy rozwijali naszą stronę:

 ~/Documents/GitStash (master)
$ git branch develop
 ~/Documents/GitStash (master)
$ git checkout develop
 ~/Documents/GitStash (develop)

Kolejnym krokiem jest zmiana pliku:

<html>
<head></head>
<body>
Wersja z gałęzi test
</body>
</html>

Teraz robimy commit i wracamy na gałąź master:

 ~/Documents/GitStash (develop)
$ git commit -a -m testVersion
[test 47e6549] secondVersionOfPage
 1 file changed, 7 insertions(+)
 ~/Documents/GitStash (develop)
$ git checkout master
 ~/Documents/GitStash (develop)

Znowu zmieniamy index.html:


<html>
<head></head>
<body>
Wersja z którą chcemy mieć w develop
</body>
</html>

Teraz jeśli spróbujemy zmienić aktualna gałąź dostaniemy błąd podobny do tego z początku postu:

$ git checkout develop
error: Your local changes to the following files would be overwritten by checkout:
        index.html
Please commit your changes or stash them before you switch branches.
Aborting

Na szczęście GIT daje nam dwa wyjścia. Pierwsze to git commit, a drugie to git stash.

Komenda git commit odpada, niestety potrzebujemy tych zmian w innej gałezi. Natomiast git stash może nam pomóc.

Polecenie git stash umieszcza aktualne zmiany w schowku. Jak działa pokażę Ci poniżej. Najpierw jednak zajrzyjmy jakie mamy zamiany w kodzie:

$ git status
On branch 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:   index.html

no changes added to commit (use "git add" and/or "git commit -a")

Jak widzimy jest jeden zmieniony plik. Skoro wiemy co się dzieje przejdziemy do rozwiązania problemu. W tym celu użyjemy polecenia git stash:

$ git stash
Saved working directory and index state WIP on master: 0863480 InitialCommit
HEAD is now at 0863480 InitialCommit
$ git status
On branch master
nothing to commit, working tree clean

Jak widzimy z komendy status, nie ma żadnych zmian. To samo potwierdza szybki rzut oka na kod:

<html>
<head></head>
<body>
Wersja pierwsza
</body>
</html>

Gdzie w takim razie podziały się nasze zmiany? Są w schowku! Żeby pokazać co w nim jest użyjemy komendy git stash list:

$ git stash list
stash@{0}: WIP on master: 0863480 InitialCommit

Jak widzimy jest tam jedna zmiana, którą możemy odzyskać w każdej chwili. Na razie jednak jest nam to niepotrzebne, ponieważ chcemy przejść do gałęzi develop:

$ git checkout develop
Switched to branch 'develop'

Udaje się to bez problemów. Pytaniem jest, jak możemy teraz odzyskać dane? Używamy komendy git stash apply, która użyje ostatnich zmian odłożonych do schowka:

$ git stash apply
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html

Teraz wystarczy spojrzeć do kodu,

<html>
<head></head>
<body>
<<<<<<< Updated upstream Wersja z gałęzi develop ======= Wersja z która powinna być w gałęzi develop >>>>>>> Stashed changes
</body>
</html>

scalić wersje,

<html>
<head></head>
<body>
Wersja z która powinna być  w gałęzi  develop
</body>
</html>

i zrobić commit.

$ git commit -a -m secondDevelopCommit
[develop 74a8680] secondDevelopCommit
 1 file changed, 4 insertions(+)

Na koniec sprzątamy. W tym celu usuniemy zestaw zmian ostatnio dodany do schowka:

$ git stash list
stash@{0}: WIP on master: 9b7d3dc initialCommit

$ git stash drop
Dropped refs/stash@{0} (f7783d7bea969d85e294823d593535b79f89c207)

$ git stash list

Jak widzisz po poleceniu git stash drop usunięty został ostatnio dodany zestaw zmian.

W ten sposób szybko i bezboleśnie możesz przerzucić niezacommitowane zmiany ze złego brancha do dobrego.

Mam nadzieję, że moja notka będzie dla Ciebie przydatna. W normalnych warunkach nie powinieneś mieć potrzeby jej użyć, ale nie ma błędu którego nie da się popełnić.

Pozdrawiam
Rafał Karwat