Показаны сообщения с ярлыком git branch. Показать все сообщения
Показаны сообщения с ярлыком git branch. Показать все сообщения

четверг, 19 марта 2015 г.

Про отрубленную голову Git (detached HEAD state)

1400761301_tudaТы туда не ходи - ты сюда ходи. А то снег башка попадёт - совсем мёртвый будешь...

В этой статье пойдет речь про отрубленное отсоединенное состояние указателя HEAD. Надо понимать что названия веток в Git это указатели на коммиты. Имя ветки, допустим master, указывает на последний коммит в ветке master.

Существует так же множество других указателей и один из них HEAD, но это очень важный указатель. И вот о нем мы и поговорим.

 

И так у нас есть история коммитов

H0001

И сейчас указатель ветки master и указатель HEAD указывают на последний коммит С4 в ветке master.

То же самое можно увидеть в Git

H0002

Оба указателя master и HEAD указывают на коммит С4 (efaaf18).

Теперь передвинем указатель HEAD на коммит C2 командой

$ git checkout 7bbbd68

H0003

Вот мы и получили отрубленную отсоединеннуюый голову указатель HEAD. И Git нам любезно сообщил что снег башка попал…

На диаграмме это можно изобразить так

H0004

То есть команда git checkout 7bbbd68 просто перенесла указатель HEAD на коммит C2 (7bbbbd68). И вместе с этим вернула в рабочий каталог состояние файлов этого коммита. То есть мы откатились назад по истории коммитов.

И Git порекомендовал нам создать новую ветку, а так же сообщил хэш коммита на который сейчас указывает HEAD.

Все это можно увидеть в Git

H0005

Состояние HEAD detached означает что HEAD указывает не на вершину какой либо ветки, а просто на какой-то коммит.

Посмотреть историю перемещения головы можно командой git reflog

H0006

На скрине мы так же посмотрели состояние Git и он нам (аж красным) сообщил, что башка отсоединена на коммит 7bbbd68.

HEAD всегда должен указывать на вершину какой-либо ветки. Это очень важно. И вот почему.

Указатель HEAD по существу указывает на тот коммит, после которого будет сделан следующий коммит. И если в состоянии отрубленной головы мы сейчас сделаем еще один коммит, то у нас будет шанс потерять его, не в смысле что он будет не доступен, а что если мы не будем помнить хэш этого коммита, то мы ни когда не сможем на него переключится.

Давайте сделаем коммит и посмотрим что будет. Изменим файлик и посмотрим статус

H0007

Ну и делаем коммит

H0008

Из лога коммитов видно что сейчас HEAD указывает на коммит С5 (84b361c), но на этот коммит не указывает ни какая ветка, верней сказать ни какой указатель ветки.

Графически это можно изобразить так

H0009

Мы по прежнему находимся в состоянии detached HEAD, о чем нам все время напоминает Git. Еще раз напомню что это означает что HEAD указывает не на вершину какой-либо ветки, а просто на коммит. В нашем случае на коммит С5 (84b361c).

Выйти из состояния detached HEAD очень легко, для этого надо переключится на какую-либо ветку или создать новую ветку.

Давайте переключимся на ветку master командой git checkout master

H0010

При переключении на ветку master, Git заботливо нас предупредил, что мы оставляем 1 commit, который не присоединен ни к какой ветке. Сообщил нам имя этого коммита – С5 и его хэш – 84b361c. И посоветовал, что возможно уже самое время создать новую ветку командой

git branch new_branch_name 84b361c

Поскольку если мы сейчас не создадим ветку (указатель) на этом коммите, то мы можем его потерять.

Если сейчас дать команду просмотра лога коммитов, то мы коммит С5 в логе не увидим

H0011

Таким образом мы можем “потерять” коммит С5, если забудем его хэш. Конечно, как уже говорилось, что в Git какой-либо сделанный коммит сложно потерять (но можно). И в данном случае мы можем посмотреть историю перемещения HEAD

H0012

и в ней мы можем увидеть наш потерянный коммит, хотя мы его и не видели в истории коммитов (логе коммитов).

Теперь дадим команду

$ git branch lost_branch 84b361c

и посмотрим лог

H0013

Теперь, все хорошо. На коммит С5 указывает ветка lost_branch

Графически это выглядит так

H0014

Теперь мы легко можем переключится на ветку lost_branch и состояния HEAD deatached уже не будет, поскольку HEAD уже будет указывать на вершину ветки lost_branch.

H0015

Сейчас переключение произошло безболезненно, поскольку это вполне штатная работа в Git.

Ну и покажу это состояние графически

H0016

На этом, с отсоединенной головой пока все. Хотя есть еще варианты по отделению головы в Git, но об этом как-нибудь в другой раз.

пятница, 20 февраля 2015 г.

Основы Git – практика работы с удалёнными репозиториями (часть 1)

До этого мы просто клонировали удаленный репозиторий Git себе на компьютер и работали после этого с ним. Но может быть и чаще всего бывает другая ситуация, когда для проекта создается локальный репозиторий, а потом возникает необходимость залить его на удаленный. Это мы сейчас и проделаем.

Сперва создадим пустой репозиторий на GitHub. Не будем в него добавлять ни каких файлов, чтобы избежать ошибок синхронизации, когда мы будем заливать в него файлы из локального репозитория.

R0001

Теперь создадим локальный репозиторий, добавим в его каталог файлик и закоммитим его.

R0002

R0003

Все, теперь в нашем локальном репозитории есть первый коммит. Остается только запушить наш локальные репозиторий на GitHub.

Последуем совету GitHub и дадим первую команду что он нам советовал

$ git remote add origin https://github.com/n0tb0dy/UpRemote.git

R0004

Второй командой мы проверили что теперь у нас локальный репозиторий связан с удаленным.

Ну и теперь осталось только запушить наш локальный репозиторий на GitHub

Воспользуемся второй командой что нам посоветовал GitHub

$ git push -u origin master

R0005

И так мы успешно запушили наш локальный репозиторий на GitHub.

Командой git branch –vv мы проверили что наша локальная ветка является отслеживаемой. Кстати в команде git push –u origin master именно ключик –u сделал ветку master отслеживаемой.

Если эту же команду дать без ключа –u, то ветка мастер будет не отслеживаемой:

R0007

Теперь посмотрим что у нас на GitHub

R0006

четверг, 19 февраля 2015 г.

Ветвление в Git - практика перемещения веток (часть 1)

Еще раз попрактикуемся с той же схемой коммитов, что и в прошлом посте.

basic-rebase-1

В коммитах C0, С1 и С2 у нас есть один файл test.txt, в который в каждом коммите мы последовательно добавляли по одной строке.

В коммите С2 этот файл выглядит так:

Rb0001

А история коммитов так:

Rb0002

Теперь в рабочий каталог добавим еще один файл testC3.txt и добавим его в коммит C3.

Rb0003

Сейчас мы уже ближе к блок схеме представленной в самом начале поста

Rb0004

У нас уже есть все коммиты в ветке master с С0 по С3, теперь нам надо как-то сделать ответвление от коммита С2.

Сделаем это так:

Rb0005

Мы переключились на коммит С2, добавили файл testC4.txt в рабочий каталог и сделали коммит С4.

Теперь дерево коммитов выглядит точно так, как на диаграмме в начале этого поста:

Rb0006

Коммиты С3 и С4 различаются только тем что в коммите С3 есть файл test3.txt, а в коммите С4 есть файл testC4.txt. Файл test.txt присутствует в обоих коммитах и абсолютно в них одинаков.

Содержимое рабочего каталога в коммите C4:

Rb0007

Теперь переключимся в ветку master и посмотрим содержимое рабочего каталога в коммите С3:

Rb0008

Надеюсь теперь все более менее понятно. Так что будем делать rebase. А до этого запомним что коммит С4 у нас имеет хэш 4f3c493. А так же что у нас имеется ветвление в истории коммитов.

Переходим в ветку experiment и выполняем там перемещение (rebase):

Rb0009

В этот раз у нас нет ни каких конфликтов и перемещение происходит гладко. Как видим изменения применились в коммите С4 и теперь это АБСОЛЮТНО НОВЫЙ коммит.

Теперь посмотрим рабочий каталог после операции перемещения:

Rb0010

Напомню что перед операцией перемещения мы перешли в ветку С4, и до операции перемещения там были только два файла test.txt и testC4.txt. После операции перемещения появился файл testC3, который приплыл сюда из коммита С3.

Стоит еще раз напомнить что, при помощи команды rebase вы можете взять все изменения, которые попали в коммиты на одной из веток, и повторить их на другой. Но при этом указатель ветки на которую вы переносите изменения, как бы перемещается на ветку с которой вы переносите изменения и устанавливается на последний коммит своей ветки. А последний коммит ветки, с которой вы переносите изменения, становится предком для цепочки изменений ветки куда вы переносите, то есть таким образом вы избавляетесь от ветвления.

Все это кажется не особо понятным, но может эта диаграмма внесет ясность:

basic-rebase-3

Коммит С4, как бы перестал сущестовать, а вернее видоизменился и сейчас это абсолютно НОВЫЙ ДРУГОЙ коммит  (у него даже другой хэш) но с именем С4.

Это можно так же посмотреть и в логах коммитов Git:

Rb0011

Из истории коммитов так же видно, что ветвление исчезло. Что у коммита С4 другой хэш (был 4f3c493, а стал d78f51b).

Кроме того, стоит обратить внимание что ветка master по прежнему указывает на коммит С3 (который остался без изменений).

На этом этапе можно переключиться на ветку master и выполнить слияние-перемотку (fast-forward merge), то есть переместить указатель ветки master на коммит С4:

Rb0012

То есть мы пришли к вот такому виду истории коммитов:

basic-rebase-4

То же самое видим в логе коммитов:

Rb0013

Теперь ветку experiment можно вообще удалить, чтобы глаза не мозолила

Rb0014

Теперь у нас снова чистенькая веточка master.

По существу это то же самое, что в предыдущем посте, просто я исключил возникновение конфликтов, чтобы материал был более понятен и ясен.

среда, 18 февраля 2015 г.

Ветвление в Git - Удалённые ветки

Удалённые ветки — это ссылки на состояние веток в ваших удалённых репозиториях. Это локальные ветки, которые нельзя перемещать; они двигаются автоматически всякий раз, когда вы осуществляете связь по сети. Удалённые ветки действуют как закладки для напоминания о том, где ветки в удалённых репозиториях находились во время последнего подключения к ним.

Они выглядят как (имя удал. репоз.)/(ветка). Например, если вы хотите посмотреть, как выглядела ветка master на сервере origin во время последнего соединения с ним, проверьте ветку origin/master. Если вы с партнёром работали над одной проблемой, и он выложил ветку iss53, у вас может быть своя локальная ветка iss53; но та ветка на сервере будет указывать на коммит в origin/iss53.

Всё это, возможно, сбивает с толку, поэтому давайте рассмотрим пример. Я создал удаленный репозиторий на GitHub https://github.com/n0tb0dy/RemoreBranches

Там я сделал три коммита

RB0001

Чтобы склонировать его воспользуемся ссылкой для клонирования

https://github.com/n0tb0dy/RemoreBranches.git

RB0002

При клонировании удаленного репозитория Git автоматически назовёт его origin, заберёт оттуда все данные, создаст указатель на то, на что там указывает ветка master, и назовёт его локально origin/master (но вы не можете его двигать). Git также сделает вам вашу собственную ветку master, которая будет начинаться там же, где и ветка master в origin, так что вам будет с чем работать.

“origin” это не специальное название

Это подобно названию ветки master, которое дается по умолчанию при создании локального репозитория. Точно так же как ветка master создается по умолчанию при команде git init, точно также по умолчанию используется название origin при команде git clone. Если вы дадите команду git clone –o booyah, то вы получите booyah/master как вашу удаленную ветку по умолчанию.

И так возвращаемся к нашим… коммитам. На удаленном репозитории они выглядят так

RB0003

После команды git clpne https://github.com/n0tb0dy/RemoreBranches.git локальный репозиторий будет выглядеть так

RB0004

Клонирование Git-проекта даёт вам собственную ветку master и origin/master, указывающий на ветку master в origin.

После клонирования команда git log –oneline --decorate покажет нам тоже самое, что мы видим на диаграмме:

RB0005

Еще раз напомню что HEAD указывает на ветку где вы сейчас находитесь.

Если вы сделаете что-то в своей локальной ветке master, а тем временем кто-то ещё отправит (push) изменения на github.com/n0tb0dy/RemoreBranches  и обновит там ветку master, то ваши истории продолжатся по-разному. И ещё на заметку, до тех пор, пока вы не свяжетесь с сервером origin, ваш указатель origin/master не будет сдвигаться.

Продемонстрируем это, сделав непосредственно на сервере GitHub в нашем проекте пару коммитов. И так же сделаем пару коммитов локально.

RB0006

RB0007

А на локальном компьютере это будет выглядеть так:

RB0008

Команда git log –oneline --decorate выполненная локально покажет нам тоже самое:

RB0009

При выполнении локальной работы и отправке кем-то изменений на удалённый сервер каждая история продолжается по-разному.

Для синхронизации вашей работы выполняется команда git fetch origin. Эта команда ищет, какому серверу соответствует origin (в нашем случае это github.com/n0tb0dy/RemoreBranches ); извлекает оттуда все данные, которых у вас ещё нет, и обновляет ваше локальное хранилище данных; сдвигает указатель origin/master на новую позицию.

RB0010

Как видим притянулись два наших изменения сделанных на GitHub в ветку origin/master и туда же сдвинулся указатель HEAD это ветки. А вот наша локальная ветка осталась без изменений и HEAD ветки master указывает на тот же коммит, что и до команды git fetch.

Команда git log --oneline --decorate --graph --all покажет нам всю картину изменений более ясно:

RB0011

Визуально дерево коммитов после команды git fetch на локальном компьютере будет выглядеть так:

RB0012

Команда git fetch обновляет ваши удалённые ссылки. Локальные же, после ее применения остаются без изменений.

Мы можем переключится на ветку origin/master

RB0013

Но Git ругнется на это сказав что мы сейчас задеатачены и т.д. и т.п. даст кучу советов, но переключится. Соответственно в рабочем каталоге поменяется файл test.txt, в котором мы увидим изменения сделанные в двух последних коммитах, которые мы сделали непосредственно на сервере GitHub.

Если мы посмотрим сейчас статус, то увидим это:

RB0014

Нам опять скажут что мы задеатачены.

Переключимся обратно на ветку master

RB0015

Git нам любезно сообщает что наша ветка (master) и ветка origin/master разошлись и каждая имеет по два разных коммита, соответственно. И предлагает дать команду git pull чтобы слить изменения удаленной ветки с локальной.

Но мы можем поступить и по другому, просто дать команду git merge origin/master находясь в ветке master, чтобы слить изменения в одну ветку. Естественно у нас будет конфликт слияния.

RB0016

Чтобы долго не мучится просто оставим без изменений слитый файл, который создал Git. Просто закоммитим их.

В результате получаем такую картину

RB0017

А теперь все это добро запушим на сервер. Для этого используется команда git push (удал. сервер) (ветка). В нашем случае команда будет выглядеть просто git push origin master.

RB0018

После того как мы запушили изменения на сервер дерево коммитов будет выглядеть так:

RB0019

Если вы были внимательны, то должны были заметить разницу между этим скриншотом и скриншотом сразу после того как мы закоммитили смердженные ветки. Теперь оба HEAD указателя origin/master и master находятся на одном коммите 9f3e200, это тот коммит в котором мы слили изменения из двух веток. А до этого указатель HEAD origin/master оставался на коммите 546a797 ветки origin/master.

Это означает что метки удаленных репозиториев обновляются только после подключения к удаленому репозиторию.

На GitHub эти изменения выглядят так:

RB0020

Еще раз напомню про разницу между командами fetch и pull. Fetch не трогает ваши локальные изменения, а просто притягивает удаленные в другую ветку и позволяет их вам посмотреть и если надо слить со своей веткой. Pull же старается сразу объединить изменения сделанные на удаленном сервере со связанной локальной веткой.

Еще про отправку изменений

Когда вы хотите поделиться веткой с окружающими, вам необходимо отправить (push) её на удалённый сервер, на котором у вас есть права на запись. Ваши локальные ветки автоматически не синхронизируются с удалёнными серверами — вам нужно явно отправить те ветки, которыми вы хотите поделиться. Таким образом, вы можете использовать свои личные ветки для работы, которую вы не хотите показывать, и отправлять только те тематические ветки, над которыми вы хотите работать с кем-то совместно.

Если у вас есть ветка serverfix, над которой вы хотите работать с кем-то ещё, вы можете отправить её точно так же, как вы отправляли вашу первую ветку. Выполните git push (удал. сервер) (ветка). Давайте для примера создадим ее, сделаем изменения в файлике test.txt и отправим это все на сревер GitHub.

RB0021

Вы также можете выполнить git push origin serverfix:serverfix — произойдёт то же самое — здесь говорится “возьми мой serverfix и сделай его удалённым serverfix”. Можно использовать этот формат для отправки локальной ветки в удалённую ветку с другим именем. Если вы не хотите, чтобы ветка называлась serverpigs на удалённом сервере, то вместо предыдущей команды выполните git push origin serverfix:serverpigs. Так ваша локальная ветка serverfix отправится в ветку serverpigs удалённого проекта.

На сервере GitHub появится наш последний коммит в своей ветке

RB0022

В следующий раз, когда один из ваших соавторов будет получать обновления с сервера, он получит ссылку на то, на что указывает serverfix на сервере, как удалённую ветку origin/serverfix:

$ git fetch origin
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 15 (delta 5), reused 0 (delta 0)
Unpacking objects: 100% (15/15), done.
From git@github.com:schacon/simplegit
* [new branch]      serverfix    -> origin/serverfix

Важно отметить, что когда при получении данных у вас появляются новые удалённые ветки, вы не получаете автоматически для них локальных редактируемых копий. Другими словами, в нашем случае вы не получите новую ветку serverfix — только указатель origin/serverfix, который вы не можете менять.

Чтобы слить эти наработки в свою текущую рабочую ветку, выполните git merge origin/serverfix. Если вам нужна своя собственная ветка serverfix, над которой вы сможете работать, то вы можете создать её на основе удалённой ветки:

$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"

Это даст вам локальную ветку serverfix, на которой можно работать. Она будет начинаться там, где и origin/serverfix.

Если вы хотите чтобы локальное имя ветки отличалось от удаленной то можете эту же команду дать например так:

$ git checkout -b MYserverfix origin/serverfix

Для примера создадим новую ветку CreatedOnGitHub в нашем проекте на сервере GitHub и сделаем изменения в нашем многострадальном файле test.txt и закоммитим их. А затем притянем себе в локальный Git.

RB0023

Вот наша, созданная удаленно веточка и коммит в ней притянулись. Как видим в имени ветки стоит origin/CreatedOnGitHub. То есть как и говорилось выше мы не получили локальную ветку CreatedOnGitHub после выполнения команды git fetch origin.

Теперь мы можем переключится в ветку origin/CreatedOnGitHub и получить измененный файл test.txt (с изменениями которые мы сделали на сервере) в рабочем каталоге и получить редактируемую копию ветки origin/CreatedOnGitHub в локальной ветке CreatedOnGitHub простой командой git checkout CreatedOnGitHub:

RB0024

То есть мы проделали ту же подобную команду (git checkout -b serverfix origin/serverfix), только гораздо проще. Хотя при этом мы создали отслеживаемую ветку (об этом читаем ниже).

Мы можем посмотреть ветки которые у нас есть

RB0025

Теперь сделаем изменения в нашем файлике test.txt в ветке CreatedOnGitHub и отправим эти изменения обратно на сервер GitHub

RB0026

Теперь посмотрим это на GitHub

RB0027

Видим что наш Local Commit 04 бла бла бла… успешно запушен на сервер GitHub.

Отслеживание веток

Получение локальной ветки с помощью git checkout из удалённой ветки автоматически создаёт то, что называется отслеживаемой веткой. Отслеживаемые ветки — это локальные ветки, которые напрямую связаны с удалённой веткой. Если, находясь на отслеживаемой ветке, вы наберёте git push, Git уже будет знать, на какой сервер и в какую ветку отправлять изменения. Аналогично выполнение git pull на одной из таких веток сначала получает все удалённые ссылки, а затем автоматически делает слияние с соответствующей удалённой веткой.

При клонировании репозитория, как правило, автоматически создаётся ветка master, которая отслеживает origin/master, поэтому git push и git pull работают для этой ветки "из коробки" и не требуют дополнительных аргументов. Однако, вы можете настроить отслеживание и других веток удалённого репозитория.

Если вы хотите посмотреть какие отслеживаемые ветки у вас есть то можете дать команду

$ git branch –vv

Примечание: номера коммитов отображаемые командой git branch –vv,  могут не соответствовать тем, что у вас есть в реальности, так как она показывает номера коммитов которые были после последней команды fetch. Это просто надо иметь в виду. Если же вы хотите чтобы все отображаемые номера коммитов соответствовали тому что есть на сервере, то сперва надо дать команду git fetch --all.

RB0028

Из этого скрина мы видим что у нас отслеживаются ветки master и CreatedOnGitHub, а ветка serverfix не отслеживается.

Это можно поправить следующей командой

$ git branch -u origin/serverfix

Поскольку локальная ветка serverfix у нас уже существует то надо использовать ключ –u, чтобы включить ее отслеживание, но для этого сперва надо перейти в эту ветку, чтобы дать эту команду. Смотрим скрин

RB0029

Теперь все три наших ветки отслеживаются.

Примечание: при команде git push нам приходилось каждый раз вводить логин и пароль для публикации изменений на сервере GitHub. Этого можно избежать настроив кэш учетных записей.

Pulling

Команда git fetch просто получает обновления с сервера которых у вас еще нет и ни каким образом не изменяет вашу рабочую директорию. Эта команда просто получает данные и позволяет вам самим решать что с ними делать (объединять с вашими данными, редактировать и т.п.)

Команда git pull, в большинстве случаев, сразу же производит слияние полученных данных с вашими.

Обычно, лучше просто использовать команду git fetch и команду git merge, чтобы иметь самим возможность проконтролировать процесс слияния.

Удаление удаленных веток Улыбка

Имеется конечно в виду удаление веток на удаленном сервере Улыбка

Скажем, вы и ваши соавторы закончили с нововведением и слили его в ветку master на удалённом сервере (или в какую-то другую ветку, где хранится стабильный код). Вы можете удалить ветку на удалённом сервере используя команду:

$ git push origin --delete serverfix

RB0030

Хлоп! И ветка на удаленном сервере исчезла. Но в принципе эта команда просто удаляет указатель ветки на удаленном сервере. Git сервер продолжит хранить всю информацию о коммитах до тех пор пока вы не запустите команду уборки мусора.