Code Monkey home page Code Monkey logo

progit-mynotes's Introduction

Pro Git

Scott Chacon, Ben Straub.

https://git-scm.com/book/ru/v2

Licence. Это произведение распространяется по свободной лицензии Creative Commons Attribution- NonCommercial-ShareAlike 3.0. Ознакомиться с текстом лицензии вы можете на сайте https://creativecommons.org/licenses/by-nc-sa/3.0/deed.ru

image


К оглавлению

Можно использовать аббревиатуры для опций. Например, можно использовать команду git commit --a вместо git commit --amend. Сокращение применимо только для тех опций, первая буква в имени которых не является начальной для других. При написании скриптов рекомендуется использовать полное название.

Настройка и конфигурация

Две довольно распространённые команды, используемые как сразу после установки Git, так и в повседневной практике для настройки и получения помощи — это config и help.

git config

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


Команды git config core.editor

Согласно инструкциям, большинство редакторов может быть установлено следующим образом:

Emacs -> git config --global core.editor emacs

Vim -> git config --global core.editor "vim --nofork"


git help

Команда git help служит для отображения встроенной документации Git о других командах. И хотя мы приводим описания самых популярных команд в этой главе, полный список параметров и флагов каждой команды доступен через git help <command>.


Клонирование и создание репозиториев

Существует два способа создать Git репозиторий. Первый — клонировать его из существующего репозитория (например, по сети); второй — создать репозиторий в существующем каталоге.


git init

Чтобы превратить обычный каталог в Git репозиторий и начать версионировать файлы в нём, просто запустите git init.


git clone

На самом деле git clone работает как обёртка над некоторыми другими командами. Она создаёт новый каталог, переходит внутрь и выполняет git init для создания пустого репозитория, затем она добавляет новый удалённый репозиторий (git remote add) для указанного URL (по умолчанию он получит имя origin), выполняет git fetch для этого репозитория и, наконец, извлекает последний коммит в ваш рабочий каталог, используя git checkout.

--bare, чтобы создать копию Git репозитория без рабочей копии.

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


Основные команды

Всего несколько команд нужно для базового варианта использования Git для ведения истории изменений.

git add

Команда git add добавляет содержимое рабочего каталога в индекс (staging area) для последующего коммита. По умолчанию git commit использует лишь этот индекс, так что вы можете использовать git add для сборки слепка вашего следующего коммита.


git status

Команда git status показывает состояния файлов в рабочем каталоге и индексе: какие файлы изменены, но не добавлены в индекс; какие ожидают коммита в индексе. Вдобавок к этому выводятся подсказки о том, как изменить состояние файлов.


git diff

Команда git diff используется для вычисления разницы между любыми двумя Git деревьями. Это может быть разница между вашей рабочей копией и индексом (собственно git diff), разница между индексом и последним коммитом (git diff --staged), или между любыми двумя коммитами (git diff master branchB).

--check для проверки на проблемы с пробелами с помощью аргумента.

git diff A...B как эффективно сравнивать ветки используя синтаксис.

-w для скрытия различий в пробельных символах, как сравнивать конфликтующие изменения с опциями --theirs, --ours и --base.

--submodule для сравнения изменений в подмодулях.


git difftool

Команда git difftool просто запускает внешнюю утилиту сравнения для показа различий в двух деревьях, на случай если вы хотите использовать что-либо отличное от встроенного просмотрщика git diff.


git commit

Команда git commit берёт все данные, добавленные в индекс с помощью git add, и сохраняет их слепок во внутренней базе данных (точка сохранения данных проекта), а затем сдвигает указатель текущей ветки на этот слепок. Есть полезные опции: -a для добавления всех изменений в индекс без использования git add, что может быть удобным в повседневном использовании

-m для передачи сообщения коммита без запуска полноценного редактора.

--amend для изменения последнего совершённого коммита.

-S как подписывать ваши коммиты


git reset

Команда git reset, используется в основном для отмены изменений. Она изменяет указатель HEAD и, опционально, состояние индекса. Также эта команда может изменить файлы в рабочем каталоге при использовании параметра --hard, что может привести к потере наработок при неправильном использовании, так что убедитесь в серьёзности своих намерений прежде чем использовать его.

git reset --hard чтобы отменить слияние в Прерывании слияния, использование команды git merge --abort для этих целей, которая работает как обёртка над git reset.


git rm

Команда git rm используется в Git для удаления файлов из индекса и рабочей копии. Она похожа на git add с тем лишь исключением, что она удаляет, а не добавляет файлы для следующего коммита.

--cached удалить файлы из индекса

--ignore-unmatch при выполнении git filter-branch, которая подавляет ошибки удаления несуществующих файлов (может быть полезно для автоматически выполняемых скриптов).


git mv

Команда git mv — это удобный способ переместить файл, а затем выполнить git add для нового файла и git rm для старого.


git clean

Команда git clean используется для удаления мусора из рабочего каталога. Это могут быть результаты сборки проекта или файлы конфликтов слияний.


Ветвление и слияния

За создание новых веток и слияние их воедино отвечает несколько Git команд.

git branch

Команда git branch — это своего рода "менеджер веток". Она умеет перечислять ваши ветки, создавать новые, удалять и переименовывать их.

git branch -u для отслеживания веток.

git checkout

Команда git checkout используется для переключения веток и выгрузки их содержимого в рабочий каталог.

--track для отслеживания веток.

--conflict=diff3 для разрешения конфликтов заново, в случае если предыдущее решение не подходило по некоторым причинам.


git merge

Команда git merge используется для слияния одной или нескольких веток в текущую. Затем она устанавливает указатель текущей ветки на результирующий коммит.

Имеет вид git merge <branch> с указанием единственной ветки для слияния.

-Xignore-all-whitespace и --abort, используемый для отмены слияния в случае возникновения проблем.


git mergetool

Команда git mergetool просто вызывает внешнюю программу слияний, в случае если у вас возникли проблемы слияния.


git log

Команда git log используется для просмотра истории коммитов, начиная с самого свежего и уходя к истокам проекта. По умолчанию, она показывает лишь историю текущей ветки, но может быть настроена на вывод истории других, даже нескольких сразу, веток. Также её можно использовать для просмотра различий между ветками на уровне коммитов.

Практически во всех главах книги эта команда используется для демонстрации истории проекта.

-p и --stat для получения представления об изменениях в каждом коммите.

--pretty и --oneline для настройки формата вывода этой команды — более полным и подробным или кратким.

--decorate чтобы отобразить указатели веток на истории коммитов.

--graph чтобы просматривать историю в виде дерева.

Синтаксис branchA..branchB, позволяющем команде git log показывать только коммиты, присутствующие в одной ветке, но отсутствующие в другой.

Синтаксис branchA... branchB и опцию --left-right позволяющие увидеть, что находится в одной или в другой ветке, но не в них обеих сразу.

--merge, которая может быть полезной при разрешении конфликтов.

--cc для просмотра конфликтов слияния в истории проекта.

-S и -L для поиска событий в истории проекта, например, истории развития какой-либо фичи.

--show-signature для отображения строки валидации подписи для каждого коммита в git log.


git stash

Команда git stash используется для временного сохранения всех незафиксированных изменений с целью очистки рабочего каталога без необходимости фиксировать незавершённую работу в текущей ветке.


git tag

Команда git tag используется для задания постоянной метки на какой-либо момент в истории проекта. Обычно она используется для релизов.


Совместная работа и обновление проектов

Не так уж много команд в Git требуют сетевого подключения для своей работы, практически все команды оперируют с локальной копией проекта. Когда вы готовы поделиться своими наработками, всего несколько команд помогут вам работать с удалёнными репозиториями.

git fetch

Команда git fetch связывается с удалённым репозиторием и забирает из него все изменения, которых у вас пока нет и сохраняет их локально.


git pull

Команда git pull работает как комбинация команд git fetch и git merge, т. е. Git вначале забирает изменения из указанного удалённого репозитория, а затем пытается слить их с текущей веткой.


git push

Команда git push используется для установления связи с удалённым репозиторием, вычисления локальных изменений отсутствующих в нём, и собственно их передачи в вышеупомянутый репозиторий. Этой команде нужно право на запись в репозиторий, поэтому она использует аутентификацию.

--delete для удаления веток на сервере, используя git push.

--recurse -submodules чтобы удостовериться, что все подмодули будут опубликованы перед отправкой на проекта на сервер, что может быть реально полезным при работе с репозиториями, содержащими подмодули.


git remote

Команда git remote служит для управления списком удалённых репозиториев. Она позволяет сохранять длинные URL репозиториев в виде понятных коротких строк, например origin, так что вам не придётся набирать её каждый раз для связи с сервером. Вы можете использовать несколько удалённых репозиториев для работы и git remote поможет добавлять, изменять и удалять их.

Вид: git remote add <имя> <URL>


git archive

Команда git archive используется для упаковки в архив указанных коммитов или всего репозитория. Мы использовали git archive для создания тарбола (tar.gz файла) всего проекта для передачи по сети.


git submodule

Команда git submodule используется для управления вложенными репозиториями. Например, это могут быть библиотеки или другие, используемые не только в этом проекте ресурсы. У команды submodule есть несколько под-команд — add, update, sync и др. — для управления такими репозиториями.


Осмотр и сравнение

git show

Команда git show отображает объект в простом и человекопонятном виде. Обычно она используется для просмотра информации о метке или коммите. Ещё одна интересная вещь, с помощью git show в режиме ручного слияние файлов — это извлечение содержимого файлов на различных стадиях во время конфликта слияния.


git shortlog

Команда git shortlog служит для подведения итогов команды git log. Она принимает практически те же параметры, что и git log, но вместо простого листинга всех коммитов, они будут сгруппированы по автору.


git describe

Команда git describe принимает на вход что угодно, что можно трактовать как коммит (ветку, тег) и выводит более-менее человекочитаемую строку, которая не изменится в будущем для данного коммита. Это может быть использовано как более удобная, но по-прежнему уникальная, замена SHA-1. Можно использовать git describe в генерации номера сборки и подготовки релиза, чтобы сгенерировать название для архивного файла с релизом.


Отладка

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

git bisect

Команда git bisect — это полезная утилита для поиска коммита в котором впервые проявился баг или проблема с помощью автоматического бинарного поиска.


git blame

Команда git blame выводит перед каждой строкой файла SHA-1 коммита, последний раз менявшего эту строку и автора этого коммита. Это помогает в поисках человека, которому нужно задавать вопросы о проблемном куске кода.


git grep

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


Внесение исправлений

Некоторые команды в Git основываются на подходе к рассмотрению коммитов в терминах внесённых ими изменений, т. е. рассматривают историю коммитов как цепочку патчей.

git cherry-pick

Команда git cherry-pick берёт изменения, вносимые одним коммитом, и пытается повторно применить их в виде нового коммита в текущей ветке. Эта возможность полезна в ситуации, когда нужно забрать парочку коммитов из другой ветки, а не сливать ветку целиком со всеми внесенными в нее изменениями.


git rebase

Команда git rebase — это «автоматизированный» cherry-pick. Он выполняет ту же работу, но для цепочки коммитов, тем самым как бы перенося ветку на новое место.


git revert

Команда git revert — полная противоположность git cherry-pick. Она создаёт новый коммит, который вносит изменения, противоположные указанному коммиту, по существу отменяя его. Можно использовать её, чтобы отменить коммит слияния (merge commit).


Работа с помощью электронной почты

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

git apply

Команда git apply применяет патч, сформированный с помощью команды git diff или GNU diff. Она делает практически то же самое, что и команда patch.


git am

Команда git am используется для применения патчей из входящих сообщений электронной почты, в частности, тех что используют формат mbox. Это используется для простого получения изменений через email и применения их к проекту.


git format-patch

Команда git format-patch используется для создания набора патчей в формате mbox которые можно использовать для отправки в список рассылки.


git send-email

Команда git send-email используется для отсылки патчей, сформированных с использованием git format-patch, по электронной почте.


git request-pull

Команда git request-pull используется для генерации примерного текста сообщения для отсылки кому-либо. Если у вас есть ветка, хранящаяся на публичном сервере, и вы хотите чтобы кто-либо забрал эти изменения без трудностей с отсылкой патчей по электронной почте, вы можете выполнить эту команду и послать её вывод тому человеку.


Внешние системы

В Git есть несколько стандартных команд для работы с другими системами контроля версий.

git svn

Команда git svn используется для работы с сервером Subversion. Это означает, что вы можете использовать Git в качестве SVN клиента, забирать изменения и отправлять свои собственные на сервер Subversion.


git fast-import

Для других систем контроля версий, либо для импорта произвольно форматированных данных, вы можете использовать git fast-import, которая умеет преобразовывать данные в формат, понятный Git.


Администрирование

Если вы администрируете Git репозиторий или вам нужно исправить что-либо, Git предоставляет несколько административных команд вам в помощь.

git gc

Команда git gc запускает сборщик мусора в вашем репозитории, который удаляет ненужные файлы из хранилища объектов и эффективно упаковывает оставшиеся файлы.

Обычно, эта команда выполняется автоматически без вашего участия, но, если пожелаете, можете вызвать её вручную.


git fsck

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


git reflog

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


git filter-branch

Команда git filter-branch используется для переписывания содержимого коммитов по заданному алгоритму, например, для полного удаления файла из истории или для вычленения истории лишь части файлов в проекте для вынесения в отдельный репозиторий.



К оглавлению

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

Локальные системы контроля версий страдают от проблемы: когда вся история проекта хранится в одном месте, вы рискуете потерять всё.

Распределённые системы контроля версий. Здесь в игру вступают распределённые системы контроля версий (РСКВ). В РСКВ (таких как Git, Mercurial, Bazaar или Darcs) клиенты не просто скачивают снимок всех файлов (состояние файлов на определённый момент времени) — они полностью копируют репозиторий. В этом случае, если один из серверов, через который разработчики обменивались данными, умрёт, любой клиентский репозиторий может быть скопирован на другой сервер для продолжения работы. Каждая копия репозитория является полным бэкапом всех данных.

image

История Git. В 2005 году сообщество разработчиков ядра Linux (а в частности Линус Торвальдс — создатель Linux) разработали свою собственную утилиту.

image

СVCS (Centralized Version Control System) — централизованные системы контроля версий, работа которых основана на том, что на сервере имеется одна центральная копия проекта, а программисты совершают свои изменения в этой центральной копии.

В настоящее время используют децентрализованные (распределённые) системы контроля версий DVCS (Distributed Version Control System). Они имеют иную структуру и ряд отличий. Например, у каждого разработчика есть свой репозиторий, который полностью копирует центральный, причём центральный сервер выступает в большей степени в качестве хаба для обмена. Пример — Git.


Что такое Git?

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

image

image

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

В Git для всего вычисляется хеш-сумма, и только потом происходит сохранение. В дальнейшем обращение к сохранённым объектам происходит по этой хеш-сумме. Это значит, что невозможно изменить содержимое файла или каталога так, чтобы Git не узнал об этом. Git сохраняет все объекты в свою базу данных не по имени, а по хеш-сумме содержимого объекта.

У Git есть три основных состояния, в которых могут находиться ваши файлы:
-Изменён (modified) относятся файлы, которые поменялись, но ещё не были зафиксированы.
-Индексирован (staged) изменённый файл в его текущей версии, отмеченный для включения в следующий коммит.
-Зафиксирован (committed) файл уже сохранён в вашей локальной базе.

Мы подошли к трём основным секциям проекта Git: рабочая копия working tree, область индексирования staging area и каталог Git .git directory.

image

Рабочая копия является снимком одной версии проекта. Эти файлы извлекаются из сжатой базы данных в каталоге Git и помещаются на диск, для того чтобы их можно было использовать или редактировать.
Область индексирования — это файл, обычно находящийся в каталоге Git, в нём содержится информация о том, что попадёт в следующий коммит. Её техническое название на языке Git — «индекс», но фраза «область индексирования» также работает. Каталог Git — это то место, где Git хранит метаданные и базу объектов вашего проекта.


Установка

По умолчанию уже стоит в системе, но есть и установка на Mac OS, Windows, Linux, установка напрямую свежей версии из исходников https://git-scm.com/download/

` git --version ` 

Первоначальная настройка Git

В состав Git входит утилита git config, которая позволяет просматривать и настраивать параметры, контролирующие все аспекты работы Git, а также его внешний вид. Эти параметры могут быть сохранены в трёх местах:

  1. Файл [path]/etc/gitconfig содержит значения, общие для всех пользователей системы и для всех их репозиториев. Если при запуске git config указать параметр --system, то параметры будут читаться и сохраняться именно в этот файл. Так как этот файл является системным, то вам потребуются права суперпользователя для внесения изменений в него.
  2. Файл ~/.gitconfig или ~/.config/git/config хранит настройки конкретного пользователя. Этот файл используется при указании параметра --global и применяется ко всем репозиториям, с которыми вы работаете в текущей системе.
  3. Файл config в каталоге Git (т. е. .git/config) репозитория, который вы используете в данный момент, хранит настройки конкретного репозитория. Вы можете заставить Git читать и писать в этот файл с помощью параметра --local, но на самом деле это значение по умолчанию. Неудивительно, что вам нужно находиться где-то в репозитории Git, чтобы эта опция работала правильно.
    Настройки на каждом следующем уровне подменяют настройки из предыдущих уровней, то есть значения в .git/config перекрывают соответствующие значения в [path]/etc/gitconfig

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

git config --list --show-origin

Можно указать ваше имя и адрес электронной почты:

git config --global user.name "Name"
git config --global user.email [email protected]

Eсли указана опция --global, то эти настройки достаточно сделать только один раз, поскольку в этом случае Git будет использовать эти данные для всего, что вы делаете в этой системе. Если для каких-то отдельных проектов вы хотите указать другое имя или электронную почту, можно выполнить эту же команду без параметра --global в каталоге с нужным проектом.

Можно выбрать текстовый редактор, который будет использоваться, если будет нужно набрать сообщение в Git. По умолчанию Git использует стандартный редактор вашей системы, обычно Vim. Если вы хотите использовать другой текстовый редактор, например, Emacs, можно проделать следующее:

git config --global core.editor emacs

Настройка ветки по умолчанию

Когда вы инициализируете репозиторий командой git init, Git создаёт ветку с именем master по умолчанию. Вы можете задать другое имя для создания ветки по умолчанию. Например, чтобы установить имя main для вашей ветки по умолчанию, выполните следующую команду:

git config --global init.defaultBranch main

Если вы хотите проверить используемую конфигурацию, можете использовать команду git config --list, чтобы показать все настройки, которые Git найдёт:

flyboroda@MacBook-Air-Artem ~ % git config --list
credential.helper=osxkeychain
init.defaultbranch=main
user.name=artemiosdev
[email protected]
user.password=********
core.excludesfile=~/.gitignore_global
credential.helper=osxkeychain
credential.username=artemiosdev
alias.last=log -1 HEAD   

Сброс пароля, при новом выполнении git push система спросит пароль/токен и можно ввести новый

git config --global --unset user.password
  or
git config --unset user.password

Как получить помощь?

Если вам нужна помощь при использовании Git, есть три способа открыть страницу руководства по любой команде Git:

git help <команда>
git <команда> --help 
man git-<команда>

К оглавлению

Создание репозитория в существующем каталоге.

Команда создаёт в текущем каталоге новый подкаталог с именем .git, содержащий все необходимые файлы репозитория — структуру Git репозитория

git init

Если вы хотите добавить под версионный контроль существующие файлы (в отличие от пустого каталога), вам стоит добавить их в индекс и осуществить первый коммит изменений. Добиться этого вы сможете запустив команду git add (имеет различные вариации использования). Указав индексируемые файлы, затем выполним git commit чтобы зафиксировать:

git add *.md
git add .
git add <file name>
git commit -m 'Message about commit'

Git индексирует файл в точности в том состоянии, в котором он находился, когда вы выполнили команду git add. Команда git add принимает параметром путь к файлу или каталогу, если это каталог, команда рекурсивно добавляет все файлы из указанного каталога в индекс, это многофункциональная команда, она используется для добавления под версионный контроль новых файлов, для индексации изменений, для указания файлов с исправленным конфликтом слияния, «добавить этот контент в следующий коммит».


Клонирование существующего репозитория

git clone 'https://github.com/artemiosdev/ProGit-MyNotes'

В Git реализовано несколько транспортных протоколов, которые вы можете использовать. В предыдущем примере использовался протокол https://, вы также можете встретить git:// или user@server:path/to/repo.git, использующий протокол передачи SSH.


Запись изменений в репозиторий

image


Определение состояния файлов

Основной инструмент, используемый для определения, какие файлы в каком состоянии находятся — это команда

git status

Игнорирование файлов

Есть сайт который формирует и генерирует нужный файл .gitignore под нужный инструментарий и язык

https://www.toptal.com/developers/gitignore/

Зачастую, у вас имеется группа файлов, которые вы не только не хотите автоматически добавлять в репозиторий, но и видеть в списках неотслеживаемых. К таким файлам обычно относятся автоматически генерируемые файлы (различные логи, результаты сборки программ и т. п.). В таком случае, вы можете создать файл .gitignore. с перечислением шаблонов соответствующих таким файлам. И при коммите указанные файлы будут проигнорированы.

Хорошая практика заключается в настройке файла .gitignore до того, как начать серьёзно работать, это защитит вас от случайного добавления в репозиторий файлов, которых вы там видеть не хотите.
К шаблонам в файле .gitignore применяются следующие правила:

  • Пустые строки, а также строки, начинающиеся с #, игнорируются.
  • Стандартные шаблоны являются глобальными и применяются рекурсивно для всего дерева каталогов.
  • Чтобы избежать рекурсии используйте символ слеш (/) в начале шаблона.
  • Чтобы исключить каталог добавьте слеш (/) в конец шаблона.
  • Можно инвертировать шаблон, использовав восклицательный знак (!) в качестве первого символа.

Glob-шаблоны представляют собой упрощённые регулярные выражения, используемые командными интерпретаторами. Символ (*) соответствует 0 или более символам; последовательность [abc] — любому символу из указанных в скобках (в данном примере a, b или c); знак вопроса (?) соответствует одному символу; и квадратные скобки, в которые заключены символы, разделённые дефисом ([0-9]), соответствуют любому символу из интервала (в данном случае от 0 до 9). Вы также можете использовать две звёздочки, чтобы указать на вложенные каталоги: a/**/z соответствует a/z, a/b/z, a/b/c/z, и так далее.

Пример файла .gitignore:

# Исключить все файлы с расширение .a
*.a

# Но отслеживать файл lib.a даже если он подпадает под исключение выше 
!lib.a

# Исключить файл TODO в корневом каталоге, но не файл в subdir/TODO 
/TODO

# Игнорировать все файлы в каталоге build/ 
build/

# Игнорировать файл doc/notes.txt, но не файл doc/server/arch.txt 
doc/*.txt

# Игнорировать все .txt файлы в каталоге doc/ 
doc/**/*.txt

GitHub поддерживает довольно полный список примеров .gitignore файлов для множества проектов и языков https://github.com/github/gitignore


Просмотр индексированных и неиндексированных изменений

Чтобы увидеть, что же вы изменили, но пока не проиндексировали, наберите git diff без аргументов

Эта команда сравнивает содержимое вашего рабочего каталога с содержимым индекса. Результат показывает ещё не проиндексированные изменения. Если вы хотите посмотреть, что вы проиндексировали и что войдёт в следующий коммит, вы можете выполнить git diff --staged (или git diff --cached для просмотра проиндексированных изменений (--staged и --cached синонимы)). Эта команда сравнивает ваши проиндексированные изменения с последним коммитом:

git diff --staged
 or
git diff --cached

git diff сама по себе не показывает все изменения сделанные с последнего коммита — только те, что ещё не проиндексированы. Такое поведение может сбивать с толку, так как если вы проиндексируете все свои изменения, то git diff ничего не вернёт.


Коммит изменений

Простейший способ зафиксировать изменения — это набрать git commit Для ещё более подробного напоминания, что же именно вы поменяли, можете передать аргумент -v в команду git commit. Это приведёт к тому, что в комментарий будет также помещена дельта/diff изменений, таким образом вы сможете точно увидеть все изменения которые вы совершили. Вы можете набрать свой комментарий к коммиту в командной строке вместе с командой commit указав его после параметра -m

git commit -m "Message about commit"

image

Запомните, что коммит сохраняет снимок состояния вашего индекса. Всё, что вы не проиндексировали, так и висит в рабочем каталоге как изменённое; вы можете сделать ещё один коммит, чтобы добавить эти изменения в репозиторий. Каждый раз, когда вы делаете коммит, вы сохраняете снимок состояния вашего проекта, который позже вы можете восстановить или с которым можно сравнить текущее состояние.


Пропуск индексации

Если у вас есть желание пропустить этап индексирования (т.е добавления файлов командой git add), Git предоставляет простой способ. Добавление параметра -a в команду git commit заставляет Git автоматически индексировать каждый уже отслеживаемый на момент коммита файл, позволяя вам обойтись без git add:

git commit -a -m 'Message about commit'

В данном случае перед коммитом вам не нужно выполнять git add для файла, потому что флаг -a включает все файлы. Это удобно, но будьте осторожны: флаг -a может включить в коммит нежелательные изменения.


Удаление файлов

Для того чтобы удалить файл из Git, вам необходимо удалить его из отслеживаемых файлов (точнее, удалить его из вашего индекса) а затем выполнить коммит. Это позволяет сделать команда git rm, которая также удаляет файл из вашего рабочего каталога, так что в следующий раз вы не увидите его как «неотслеживаемый».

Если вы изменили файл и уже проиндексировали его, вы должны использовать принудительное удаление с помощью параметра -f. Это сделано для повышения безопасности, чтобы предотвратить ошибочное удаление данных, которые ещё не были записаны в снимок состояния и которые нельзя восстановить из Git. Другая полезная штука, которую вы можете захотеть сделать — это удалить файл из индекса, оставив его при этом в рабочем каталоге. Другими словами, вы можете захотеть оставить файл на жёстком диске, но перестать отслеживать изменения в нём. Это особенно полезно, если вы забыли добавить что-то в файл .gitignore и по ошибке проиндексировали, например, большой файл с логами, или кучу промежуточных файлов компиляции. Чтобы сделать это, используйте опцию --cached:

git rm --cached <file name>

В команду git rm можно передавать файлы, каталоги или шаблоны.

git rm log/\*.log

Обратите внимание на обратный слеш \ перед *. Он необходим из-за того, что Git использует свой собственный обработчик имён файлов вдобавок к обработчику вашего командного интерпретатора. Эта команда удаляет все файлы, имеющие расширение .log и находящиеся в каталоге log/.
Или же вы можете сделать вот так:

git rm \*~

Эта команда удаляет все файлы, имена которых заканчиваются на ~.


Перемещение файлов

Команда mv eсли вам хочется переименовать файл:

git mv file_from file_to

Однако, это эквивалентно выполнению следующих команд:

mv README MyFile
git rm README
git add MyFile

Просмотр истории коммитов

Команда git log имеет очень большое количество опций для поиска коммитов по разным критериям. Одним из самых полезных аргументов является -p или --patch, который показывает разницу (выводит патч), внесенную в каждый коммит. Так же вы можете ограничить количество записей в выводе команды; используйте параметр -2 для вывода только двух записей:

git log -p -2

Если вы хотите увидеть сокращенную статистику для каждого коммита, вы можете использовать опцию --stat:

git log --stat

Опция --stat печатает под каждым из коммитов список и количество измененных файлов, а также сколько строк в каждом из файлов было добавлено и удалено. В конце можно увидеть суммарную таблицу изменений.

Следующей опцией является --pretty. Эта опция меняет формат вывода. Существует несколько встроенных вариантов отображения.

Наиболее интересной опцией является format, которая позволяет указать формат для вывода информации. Особенно это может быть полезным когда вы хотите сгенерировать вывод для автоматического анализа — так как вы указываете формат явно, он не будет изменен даже после обновления Git:

git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Artem Androsenko, 1 years ago : Change version number

Полезные опции для git log --pretty=format

`%H` Хеш коммита.   
`%h` Сокращенный хеш коммита.   
`%T` Хеш дерева.   
`%t` Сокращенный хеш дерева.   
`%P` Хеш родителей.   
`%p` Сокращенный хеш родителей.  
`%an` Имя автора.  
`%ae` Электронная почта автора.  
`%ad` Дата автора (формат даты можно задать опцией ` --date=option `).   
`%ar` Относительная дата автора.   
`%cn` Имя коммитера.   
`%ce` Электронная почта коммитера.    
`%cd` Дата коммитера.   
`%cr` Относительная дата коммитера.   
`%s` Содержание. 

Опция --graph команды log. С этой опцией вы сможете увидеть небольшой граф в формате ASCII, который показывает текущую ветку и историю слияний:

git log --pretty=format:"%h %s" --graph
  * 2d3acf9 Ignore errors from SIGCHLD on trap
  *  5e3ee11 Merge branch 'master' of git://github.com/artemiosdev/grit
  |\
  | * 420eac9 Add method for getting the current branch
  * | 30e367c Timeout code and tests
  * | e1193f8 Support for heads with slashes in them
  |/
  * d6016bc Require time for xmlschema
  *  11d191e Merge branch 'defunkt' into local

Наиболее распространенные опции для команды git log

` -p ` Показывает патч для каждого коммита.     
` --stat ` Показывает статистику измененных файлов для каждого коммита.     
` --shortstat ` Отображает только строку с количеством изменений/вставок/удалений для команды ` --stat `.    
` --name-only ` Показывает список измененных файлов после информации о коммите.     
` --name-status ` Показывает список файлов, которые добавлены/изменены/удалены.     
` --abbrev-commit ` Показывает только несколько символов SHA-1 чек-суммы вместо всех 40.     
` --relative-date ` Отображает дату в относительном формате (например, «2 weeks ago») вместо стандартного формата даты.    
` --graph ` Отображает ASCII граф с ветвлениями и историей слияний.    
` --pretty ` Показывает коммиты в альтернативном формате. Возможные варианты опций: `oneline`, `short`, `full`, `fuller` и `format` (с помощью последней можно указать свой формат).   
` --oneline ` Сокращение для одновременного использования опций ` --pretty=oneline --abbrev-commit `

Ограничение вывода

В дополнение к опциям форматирования вывода, команда git log принимает несколько опций для ограничения вывода — опций, с помощью которых можно увидеть определенное подмножество коммитов. Вы можете использовать -<n> , где n — это любое натуральное число и представляет собой n последних коммитов. Опции для ограничения вывода по времени, такие как --since и --until, являются очень удобными. Например, следующая команда покажет список коммитов, сделанных за последние две недели:

git log --since=2.weeks

Это команда работает с большим количеством форматов — вы можете указать определенную дату вида 2008-01-15 или же относительную дату, например 2 years 1 day 3 minutes ago.

Допускается указывать несколько параметров --author и --grep для поиска, которые позволят найти коммиты, соответствующие любому указанному --author и любому указанному --grep шаблону; однако, применение опции --all-match заставит искать коммиты соответствующие всем указанным --grep шаблонам.

Опция -S, которая принимает аргумент в виде строки и показывает только те коммиты, в которых изменение в коде повлекло за собой добавление или удаление этой строки. Например, если вы хотите найти последний коммит, который добавил или удалил вызов определенной функции, вы можете запустить команду:

git log -S function_name

Опция путь. Если вы укажете каталог или имя файла, вы ограничите вывод только теми коммитами, в которых были изменения этих файлов. Эта опция всегда указывается последней после двойного тире (--), чтобы отделить пути от опций:

git log -- path/to/file

Опции для ограничения вывода команды git log

` -(n) ` Показывает только последние n коммитов.    
` --since `, ` --after ` Показывает только те коммиты, которые были сделаны после указанной даты.     
` --until `, ` --before ` Показывает только те коммиты, которые были сделаны до указанной даты.    
` --author ` Показывает только те коммиты, в которых запись author совпадает с указанной строкой.   
` --committer ` Показывает только те коммиты, в которых запись committer совпадает с указанной строкой.   
` --grep ` Показывает только коммиты, сообщение которых содержит указанную строку.   
` -S ` Показывает только коммиты, в которых изменение в коде повлекло за собой добавление или удаление указанной строки.  

Например, если вы хотите увидеть, в каких коммитах произошли изменения в тестовых файлах в исходном коде Git в октябре 2008 года, автором которых был Junio Hamano и которые не были коммитами слияния, вы можете запустить следующую команду:

git log --pretty="%h - %s" --author='Junio C Hamano' --since="2008-10-01" \ --before="2008-11-01" --no-merges -- t/

Из почти 40 000 коммитов в истории исходного кода Git, эта команда показывает только 6, которые соответствуют этим критериям. В зависимости от используемого порядка работы, история коммитов в вашем репозитории может содержать большое количество коммитов слияния, которые сами по себе не очень информативны. Чтобы исключить их из вывода команды git log используйте опцию --no-merges


Операции отмены

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

Отмена может потребоваться, если вы сделали коммит слишком рано, например, забыв добавить какие-то файлы или комментарий к коммиту. Если вы хотите переделать коммит — внесите необходимые изменения, добавьте их в индекс и сделайте коммит ещё раз, указав параметр --amend:

git commit --amend

Например, если вы сделали коммит и поняли, что забыли проиндексировать изменения в файле, который хотели добавить в коммит, то можно сделать следующее:

git commit -m 'Initial commit'
git add forgotten_file
git commit --amend

Можно изменить/заменить сообщение последнего коммита

git commit --amend -m "New text"

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


Отмена индексации файла

Например, вы изменили два файла и хотите добавить их в разные коммиты, но случайно выполнили команду git add * и добавили в индекс оба. Как исключить из индекса один из них? Команда git status напомнит вам:

git add *
git status
  On branch master
  Changes to be committed:
    (use "git reset HEAD <file>..." to unstage)

Используйте git reset HEAD <file> для исключения из индекса.


Отмена изменений в файле

Команда git status напомнит вам:

   Changes not staged for commit:
   (use "git checkout -- <file>..." to discard changes in working directory)
   
   git checkout -- <file>

Важно понимать, что git checkout -- <file> — опасная команда. Все локальные изменения в файле пропадут — Git просто заменит его версией из последнего коммита. Ни в коем случае не используйте эту команду, если вы не уверены, что изменения в файле вам не нужны.
Помните, все что попало в коммит почти всегда Git может восстановить. Можно восстановить даже коммиты из веток, которые были удалены, или коммиты, перезаписанные параметром --amend (см. Восстановление данных). Но всё, что не было включено в коммит и потеряно — скорее всего, потеряно навсегда.


Отмена действий с помощью git restore

Предположим, что вы изменили два файла и хотите зафиксировать их как два отдельных изменения, но случайно набираете git add * и индексируете их оба. Как вы можете убрать из индекса один из двух? Команда git status напоминает вам:

git add *
git status
 On branch master
 Changes to be committed:
 (use "git restore --staged <file>..." to unstage)

Использовать git restore --staged <file> для отмены индексации файла.


Откат измененного файла с помощью git restore

Что, если вы поймете, что не хотите сохранять изменения в файле ? Как легко его откатить — вернуть обратно к тому, как он выглядел при последнем коммите (или изначально клонирован, или каким-либо образом помещён в рабочий каталог).
К счастью, git status тоже говорит, как это сделать. В выводе последнего примера, неиндексированная область выглядит следующим образом:

	Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git restore <file>..." to discard changes in working directory
    
    git restore <file>

Важно понимать, что git restore <file> — опасная команда. Любые локальные изменения, внесенные в этот файл, исчезнут — Git просто заменит файл последней зафиксированной версией. Никогда не используйте эту команду, если точно не знаете, нужны ли вам эти несохраненные локальные изменения.


Работа с удалёнными репозиториями

Просмотр удалённых репозиториев

Для того, чтобы просмотреть список настроенных удалённых репозиториев, вы можете запустить команду git remote. Она выведет названия доступных удалённых репозиториев. Если вы клонировали репозиторий, то увидите как минимум origin — имя по умолчанию, которое Git даёт серверу, с которого производилось клонирование:
-v, чтобы просмотреть адреса для чтения и записи, привязанные к репозиторию:

git remote -v
origin  https://github.com/schacon/ticgit (fetch)
origin  https://github.com/schacon/ticgit (push)

Добавление удалённых репозиториев

Для того, чтобы добавить удалённый репозиторий и присвоить ему имя (shortname), просто выполните команду

git remote add <shortname> <url>

Например, если вы хотите получить изменения, которые есть у кого-то, но нету у вас, вы можете выполнить команду git fetch <shortname>


Получение изменений из удалённого репозитория — Fetch и Pull

Как вы только что узнали, для получения данных из удалённых проектов, следует выполнить:

git fetch [remote-name]

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

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


Отправка изменений в удаленный репозиторий (Push)

Когда вы хотите поделиться своими наработками, вам необходимо отправить их в удалённый репозиторий. Команда для этого действия простая: git push <remote-name> <branch-name>. Чтобы отправить вашу ветку main на сервер origin (повторимся, что клонирование обычно настраивает оба этих имени автоматически), вы можете выполнить следующую команду для отправки ваших коммитов:

git push origin master

Эта команда срабатывает только в случае, если вы клонировали с сервера, на котором у вас есть права на запись, и если никто другой с тех пор не выполнял команду push. Если вы и кто-то ещё одновременно клонируете, затем он выполняет команду push, а после него выполнить команду push попытаетесь вы, то ваш push точно будет отклонён. Вам придётся сначала получить изменения и объединить их с вашими и только после этого вам будет позволено выполнить push.


Просмотр удаленного репозитория

Если хотите получить побольше информации об одном из удалённых репозиториев, вы можете использовать команду git remote show <remote>. Выполнив эту команду с некоторым именем, например, origin, вы получите следующий результат:

git remote show origin

Она выдаёт URL удалённого репозитория, а также информацию об отслеживаемых ветках. Эта команда любезно сообщает вам, что если вы, находясь на ветке main, выполните git pull, ветка main с удалённого сервера будет автоматически влита в вашу сразу после получения всех необходимых данных. Она также выдаёт список всех полученных ею ссылок.


Удаление и переименование удалённых репозиториев

Для переименования удалённого репозитория можно выполнить git remote rename. Например, если вы хотите переименовать oldname в newname, вы можете это сделать при помощи git remote rename:

git remote rename oldname newname

Если вы хотите удалить удаленный репозиторий — вы сменили сервер или больше не используете определённое зеркало, или кто-то перестал вносить изменения — вы можете использовать git remote rm

git remote remove repositoryname

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


Работа с тегами

Git имеет возможность помечать определённые моменты в истории как важные. Как правило, эта функциональность используется для отметки моментов выпуска версий (v1.0, и т. п.). Такие пометки в Git называются тегами.


Просмотр списка тегов

Просмотреть список имеющихся тегов в Git можно очень просто. Достаточно набрать команду git tag (параметры -l и --list опциональны)

Поиск тега по шаблону. Например, репозиторий Git содержит много тегов. Если вы хотите посмотреть теги выпусков 1.8.5, то выполните следующую команду:

git tag -l "v1.8.5*"
  v1.8.5
  v1.8.5.1
  v1.8.5.2
  v1.8.5.3

Если вы хотите отфильтровать список тегов согласно шаблону, использование параметров -l или --list становится обязательным.


Создание тегов

Git использует два основных типа тегов: легковесные и аннотированные.
Легковесный тег — это что-то очень похожее на ветку, которая не изменяется — просто указатель на определённый коммит.

Аннотированные теги хранятся в базе данных Git как полноценные объекты. Они имеют контрольную сумму, содержат имя автора, его e-mail и дату создания, имеют комментарий и могут быть подписаны и проверены с помощью GNU Privacy Guard (GPG). Обычно рекомендуется создавать аннотированные теги, чтобы иметь всю перечисленную информацию; но если вы хотите сделать временную метку или по какой-то причине не хотите сохранять остальную информацию, то для этого годятся и легковесные.


Аннотированные теги

Создание аннотированного тега — это указать -a при выполнении команды tag:

git tag -a v1.4 -m "my version 1.4"
git tag
v1.4

Опция -m задаёт сообщение, которое будет храниться вместе с тегом. Если не указать сообщение, то Git запустит редактор VIM, чтобы его ввести.
С помощью команды git show вы можете посмотреть данные тега вместе с коммитом:

git show v1.4

Легковесные теги

Легковесный тег — это ещё один способ пометить коммит. По сути, это только контрольная сумма коммита, сохранённая в файл — больше никакой информации не хранится. Для создания легковесного тега не передавайте опций -a, -s и -m, укажите только название:

git tag v1.4-lw

Отложенная расстановка тегов

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

git log --pretty=oneline
  4682c3261057305bdd616e23b64b0857d832627b Add todo file
  166ae0c4d3f420721acbb115cc33848dfcc2121a Create write support
  9fceb02d0ae598e95dc970b74767f19372d61af8 Update rakefile

Теперь предположим, что вы забыли отметить версию проекта v1.2, которая была там, где находится коммит «Update rakefile». Вы можете добавить тег и позже. Для отметки коммита укажите его контрольную сумму (или её часть) как параметр команды:

git tag -a v1.2 9fceb02

Обмен тегами

По умолчанию, команда git push не отправляет теги на удалённые сервера. После создания теги нужно отправлять явно на удалённый сервер. Процесс аналогичен отправке веток — достаточно выполнить команду git push origin <tagname>

git push origin v1.5

Если у вас много тегов, и вам хотелось бы отправить все за один раз, то можно использовать опцию --tags для команды git push

git push origin --tags

Теперь, если кто-то клонирует (clone) или выполнит git pull из вашего репозитория, то он получит вдобавок к остальному и ваши метки.
Команда git push отправляет оба типа тегов.
Отправка тегов командой git push <remote> --tags не различает аннотированные и легковесные теги.


Удаление тегов

Для удаления тега в локальном репозитории достаточно выполнить команду git tag -d <tagname>. Например, удалить созданный ранее легковесный тег можно следующим образом:

git tag -d v1.4-lw
Deleted tag 'v1.4-lw' (was e7d5add)

Обратите внимание, что при удалении тега не происходит его удаления с внешних серверов. Существует два способа изъятия тега из внешнего репозитория. Первый способ — это выполнить команду git push <remote> :refs/tags/<tagname>

git push origin :refs/tags/v1.4-lw
  To /[email protected]:schacon/simplegit.git
   - [deleted]         v1.4-lw

Это следует понимать как обновление внешнего тега пустым значением, что приводит к его удалению.
Второй способ убрать тег из внешнего репозитория более интуитивный:

git push origin --delete <tagname>

Переход на тег

Если вы хотите получить версии файлов, на которые указывает тег, то вы можете сделать git checkout для тега. Однако, это переведёт репозиторий в состояние «detached HEAD», которое имеет ряд неприятных побочных эффектов.

git checkout v2.0.0
Note: switching to 'v2.0.0'.

Если в состоянии «detached HEAD» внести изменения и сделать коммит, то тег не изменится, при этом новый коммит не будет относиться ни к какой из веток, а доступ к нему можно будет получить только по его хешу. Поэтому, если вам нужно внести изменения — исправить ошибку в одной из старых версий — скорее всего вам следует создать ветку:

git checkout -b version2 v2.0.0
Switched to a new branch 'version2'

Если сделать коммит в ветке version2, то она сдвинется вперед и будет отличаться от тега v2.0.0, так что будьте с этим осторожны.


Псевдонимы в Git (aliases)

Git не будет пытаться сделать вывод о том, какую команду вы хотели ввести, если вы ввели её не полностью. Если вы не хотите печатать каждую команду для Git целиком, вы легко можете настроить псевдонимы (alias) для любой команды с помощью git config. Вот несколько примеров псевдонимов:

git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status

Это означает, что, например, вместо ввода git commit, вам достаточно набрать только git ci.

Обычно, добавляют команду last следующим образом:

git config --global alias.last 'log -1 HEAD'

Таким образом, можно легко просмотреть последний коммит:

git last

Git просто заменяет эти команды на созданные вами псевдонимы (alias). Однако, возможно, вы захотите выполнить внешнюю команду, а не подкоманду Git. В этом случае, следует начать команду с символа !. Это полезно, если вы пишете свои утилиты для работы с Git-репозиторием.


К оглавлению

image

Принято использовать названия: Master - Hotfix - Release - Development - Feature

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

git add README test.rb LICENSE
git commit -m 'Initial commit'

Когда вы создаёте коммит командой git commit, Git вычисляет контрольные суммы каждого подкаталога (в нашем случае, только основной каталог проекта) и сохраняет его в репозитории как объект дерева каталогов. Затем Git создаёт объект коммита с метаданными и указателем на основное дерево проекта для возможности воссоздать этот снимок в случае необходимости.

Ваш репозиторий Git теперь хранит пять объектов: три блоб объекта (по одному на каждый файл), объект дерева каталогов, содержащий список файлов и соответствующих им блобов, а так же объект коммита, содержащий метаданные и указатель на объект дерева каталогов.

image

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

image

Ветка в Git — это простой перемещаемый указатель на один из таких коммитов. По умолчанию, имя основной ветки в Git — master or main. Как только вы начнёте создавать коммиты, ветка master будет всегда указывать на последний коммит. Каждый раз при создании коммита указатель ветки master будет передвигаться на следующий коммит автоматически.

Ветка master в Git — это не какая-то особенная ветка. Она точно такая же, как и все остальные ветки. Она существует почти во всех репозиториях только лишь потому, что её создаёт команда git init, а большинство людей не меняют её название.

image


Создание новой ветки

Что же на самом деле происходит при создании ветки? Всего лишь создаётся новый указатель для дальнейшего перемещения. Допустим вы хотите создать новую ветку с именем testing. Вы можете это сделать командой git branch :

git branch testing

image

Как Git определяет, в какой ветке вы находитесь? Он хранит специальный указатель HEAD. В Git — это указатель на текущую локальную ветку. В нашем случае мы все еще находимся в ветке master. Команда git branch только создаёт новую ветку, но не переключает на неё.

image

Вы можете легко это увидеть при помощи простой команды git log, которая покажет вам куда указывают указатели веток. Эта опция называется —decorate.

git log --oneline --decorate
f30ab (HEAD -> master, testing)

Здесь можно увидеть указывающие на коммит f30ab ветки: master и testing.


Переключение веток

Для переключения на существующую ветку выполните команду git checkout. Давайте переключимся на ветку testing:

git checkout testing

В результате указатель HEAD переместится на ветку testing.

image

Какой в этом смысл? Давайте сделаем ещё один коммит:

vim test.rb
git commit -a -m 'made a change'

image

Интересная ситуация: указатель на ветку testing переместился вперёд, а master указывает на тот же коммит, где вы были до переключения веток командой git checkout. Давайте переключимся назад на ветку master:

git checkout master

git log не показывает все ветки по умолчанию Если выполнить команду git log прямо сейчас, то в её выводе только что созданная ветка testing фигурировать не будет.
Ветка никуда не исчезла; просто Git не знает, что именно она вас интересует, и выводит наиболее полезную по его мнению информацию. Другими словами, по умолчанию git log отобразит историю коммитов только для текущей ветки.
Для просмотра истории коммитов другой ветки необходимо явно указать её имя: git log testing Чтобы посмотреть историю по всем веткам — выполните команду с дополнительным флагом: git log --all

image

Эта команда сделала две вещи: переместила указатель HEAD назад на ветку master и вернула файлы в рабочем каталоге в то состояние, на снимок которого указывает master. Это также означает, что все вносимые с этого момента изменения будут относиться к старой версии проекта. Другими словами, вы откатили все изменения ветки testing и можете продолжать в другом направлении.

Переключение веток меняет файлы в рабочем каталоге Важно запомнить, что при переключении веток в Git происходит изменение файлов в рабочем каталоге. Если вы переключаетесь на старую ветку, то рабочий каталог будет выглядеть так же, как выглядел на момент последнего коммита в ту ветку. Если Git по каким-то причинам не может этого сделать — он не позволит вам переключиться вообще.

Давайте сделаем еще несколько изменений и создадим очередной коммит:

vim test.rb
git commit -a -m 'made other changes'

Теперь история вашего проекта разошлась (см Разветвлённая история). Вы создали ветку и переключились на нее, поработали, а затем вернулись в основную ветку и поработали в ней. Эти изменения изолированы друг от друга: вы можете свободно переключаться туда и обратно, а когда понадобится — объединить их. И все это делается простыми командами: branch, checkout и commit.

image

Все описанные действия можно визуализировать с помощью команды git log. Для отображения истории коммитов, текущего положения указателей веток и истории ветвления выполните команду git log --oneline --decorate --graph --all

git log --oneline --decorate --graph --all
  * c2b9e (HEAD, master) Made other changes
  | * 87ab2 (testing) Made a change
  |/
  * f30ab Add feature #32 - ability to add new formats to the central interface
  * 34ac2 Fix bug #1328 - stack overflow under certain conditions
  * 98ca9 initial commit of my project

Ветка в Git — это простой файл, содержащий 40 символов контрольной суммы SHA-1 коммита, на который она указывает; поэтому операции с ветками являются дешёвыми с точки зрения потребления ресурсов или времени.


Одновременное создание новой ветки и переключение на неё

Как правило, при создании новой ветки вы хотите сразу на неё переключиться — это можно сделать используя команду git checkout -b <newbranchname>
Начиная с Git версии 2.23, вы можете использовать git switch вместо git checkout, чтобы:
-Переключиться на существующую ветку: git switch testing-branch.
-Создать новую ветку и переключиться на нее: git switch -c new-branch. Флаг -c означает создание, но также можно использовать полный формат: --create.
-Вернуться к предыдущей извлечённой ветке: git switch -


Основы ветвления и слияния

image

Вы решаете, что теперь вы будете заниматься проблемой #53 из вашей системы отслеживания ошибок. Чтобы создать ветку и сразу переключиться на нее, можно выполнить команду git checkout с параметром -b:

git checkout -b iss53
  Switched to a new branch "iss53"

Это то же самое что и:

git branch iss53
git checkout iss53

image

Вы работаете над своим сайтом и делаете коммиты. Это приводит к тому, что ветка iss53 движется вперед, так как вы переключились на нее ранее (HEAD указывает на нее).

vim index.html
git commit -a -m 'Create new footer [issue 53]'

image

Имейте в виду, что если рабочий каталог либо индекс содержат незафиксированные изменения, конфликтующие с веткой, на которую вы хотите переключиться, то Git не позволит переключить ветки. Лучше всего переключаться из чистого рабочего состояния проекта. Есть способы обойти это (припрятать изменения stash или добавить их в последний коммит amend), но об этом мы поговорим позже в разделе Припрятывание и очистка главы 7

Давайте создадим новую ветку для исправления, в которой будем работать, пока не закончим исправление

git checkout -b hotfix
  Switched to a new branch 'hotfix'
vim index.html
git commit -a -m 'Fix broken email address'
  [hotfix 1fb7853] Fix broken email address

image

Чтобы выполнить слияние ветки hotfix с веткой master для включения изменений в продукт. Это делается командой git merge:

git checkout master
git merge hotfix
Updating f42c576..3a0874c
  Fast-forward
   index.html | 2 ++
   1 file changed, 2 insertions(+)

Заметили фразу fast-forward в этом слиянии? Git просто переместил указатель ветки вперед, потому что коммит C4, на который указывает слитая ветка hotfix, был прямым потомком коммита C2, на котором вы находились до этого. Другими словами, если коммит сливается с тем, до которого можно добраться двигаясь по истории прямо, Git упрощает слияние просто перенося указатель ветки вперед, так как нет расхождений в изменениях. Это называется fast-forward.

Теперь ваши изменения включены в коммит, на который указывает ветка master, и исправление можно внедрять.

image

После внедрения исправления из ранее созданной ветки, нужно удалить ветку hotfix, потому что она больше не нужна — ветка master указывает на то же самое место. Для удаления ветки выполните команду git branch с параметром -d:

git branch -d hotfix
  Deleted branch hotfix (3a0874c)

Теперь вы можете переключиться обратно на ветку iss53 и продолжить работу над проблемой #53:

git checkout iss53
  Switched to branch "iss53"
vim index.html
git commit -a -m 'Finish the new footer [issue 53]'
  [iss53 ad82d7a] Finish the new footer [issue 53]
  1 file changed, 1 insertion(+)

image

Стоит обратить внимание на то, что все изменения из ветки hotfix не включены в вашу ветку iss53. Если их нужно включить, вы можете влить ветку master в вашу ветку iss53 командой git merge master, или же вы можете отложить слияние этих изменений до завершения работы, и затем влить ветку iss53 в master.


Основы слияния

Предположим, вы решили, что работа по проблеме #53 закончена и её можно влить в ветку master. Для этого нужно выполнить слияние ветки iss53 точно так же, как вы делали это с веткой hotfix ранее. Все, что нужно сделать — переключиться на ветку, в которую вы хотите включить изменения, и выполнить команду git merge:

git checkout master
  Switched to branch 'master'
git merge iss53  

Результат этой операции отличается от результата слияния ветки hotfix. В данном случае процесс разработки ответвился в более ранней точке. Так как коммит, на котором мы находимся, не является прямым родителем ветки, с которой мы выполняем слияние, Git придётся немного потрудиться. В этом случае Git выполняет простое трёхстороннее слияние, используя последние коммиты объединяемых веток и общего для них родительского коммита.

image

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

image

Теперь, когда изменения слиты, ветка iss53 больше не нужна. Вы можете закрыть задачу в системе отслеживания ошибок и удалить ветку:

git branch -d iss53

Основные конфликты слияния

Иногда процесс не проходит гладко. Если вы изменили одну и ту же часть одного и того же файла по-разному в двух объединяемых ветках, Git не сможет их чисто объединить.

git merge iss53
  Auto-merging index.html
  CONFLICT (content): Merge conflict in index.html
  Automatic merge failed; fix conflicts and then commit the result.

Git не создал коммит слияния автоматически. Он остановил процесс до тех пор, пока вы не разрешите конфликт. Чтобы в любой момент после появления конфликта увидеть, какие файлы не объединены, вы можете запустить git status:

git status
  On branch master
  You have unmerged paths.
    (fix conflicts and run "git commit")
  Unmerged paths:
    (use "git add <file>..." to mark resolution)
      both modified:      index.html
  no changes added to commit (use "git add" and/or "git commit -a")

Всё, где есть неразрешённые конфликты слияния, перечисляется как неслитое. В конфликтующие файлы Git добавляет специальные маркеры конфликтов, чтобы вы могли исправить их вручную. В вашем файле появился раздел, выглядящий примерно так:

<<<<<<< HEAD:index.html
  <div id="footer">contact : [email protected]</div>
  =======
  <div id="footer">
   please contact us at [email protected]
  </div>
  >>>>>>> iss53:index.html

Это означает, что версия из HEAD (вашей ветки master, поскольку именно её вы извлекли перед запуском команды слияния) — это верхняя часть блока (всё, что над =======), а версия из вашей другой ветки iss53 представлена в нижней части. Чтобы разрешить конфликт, придётся выбрать один из вариантов, либо объединить содержимое по-своему. Например, вы можете разрешить конфликт, заменив весь блок следующим:

<div id="footer">
  please contact us at [email protected]
</div>

В этом разрешении есть немного от каждой части, а строки <<<<<<<, ======= и >>>>>>> полностью удалены. Разрешив каждый конфликт во всех файлах, запустите git add для каждого файла, чтобы отметить конфликт как решённый. Добавление файла в индекс означает для Git, что все конфликты в нём исправлены. Если вы хотите использовать графический инструмент (есть и в Xcode тоже) для разрешения конфликтов, можно запустить git mergetool, который проведет вас по всем конфликтам:

git mergetool
  This message is displayed because 'merge.tool' is not configured.
  See 'git mergetool --tool-help' or 'git help config' for more details.
  'git mergetool' will now attempt to use one of the following tools:
  opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge
  p4merge araxis bc3 codecompare vimdiff emerge
  Merging:
  index.html
  Normal merge conflict for 'index.html':
    {local}: modified file
    {remote}: modified file
  Hit return to start merge resolution tool (opendiff):

Cписок всех поддерживаемых инструментов представлен вверху после фразы one of the following tools. Просто введите название инструмента, который хотите использовать.
Если вы считаете, что коммит слияния требует дополнительных пояснений — опишите как были разрешены конфликты и почему были применены именно такие изменения, если это не очевидно.

image

image


Управление ветками

Команда git branch делает несколько больше, чем просто создаёт и удаляет ветки. При запуске без параметров, вы получите простой список имеющихся у вас веток:

git branch
    iss53
  * master
    testing

Обратите внимание на символ *, стоящий перед веткой master: он указывает на ветку, на которой вы находитесь в настоящий момент (т. е. ветку, на которую указывает HEAD). Это означает, что если вы сейчас сделаете коммит, ветка master переместится вперёд в соответствии с вашими последними изменениями. Чтобы посмотреть последний коммит на каждой из веток, выполните команду git branch -v:

git branch -v
    iss53   93b412c Fix javascript issue
  * master  7a98805 Merge branch 'iss53'
    testing 782fd34 Add scott to the author list in the readme

Опции --merged и --no-merged могут отфильтровать этот список для вывода только тех веток, которые слиты или ещё не слиты в текущую ветку. Чтобы посмотреть те ветки, которые вы уже слили с текущей, можете выполнить команду git branch --merged :

git branch --merged
    iss53
  * master

Ветка iss53 присутствует в этом списке потому что вы ранее слили её в master. Те ветки из этого списка, перед которыми нет символа *, можно смело удалять командой git branch -d; наработки из этих веток уже включены в другую ветку, так что ничего не потеряется. Чтобы увидеть все ветки, содержащие наработки, которые вы пока ещё не слили в текущую ветку, выполните команду git branch --no-merged:

git branch --no-merged
    testing

Вы увидите оставшуюся ветку. Так как она содержит ещё не слитые наработки, попытка удалить её командой git branch -d приведёт к ошибке:

git branch -d testing
  error: The branch 'testing' is not fully merged.
  If you are sure you want to delete it, run 'git branch -D testing'.

Если вы действительно хотите удалить ветку вместе со всеми наработками, используйте опцию -D, как указано в подсказке.

Если в качестве аргумента не указан коммит или ветка, то опции --merged и --no-merged покажут что уже слито или не слито с вашей текущей веткой соответственно. Вы всегда можете указать дополнительный аргумент для вывода той же информации, но относительно указанной ветки предварительно не извлекая и не переходя на неё.

git checkout testing
git branch --no-merged master
             topicA
             featureB

Переименование ветки

Не переименовывайте ветки, которые всё ещё используются другими участниками.
Предположим, у вас есть ветка с именем bad-branch-name, и вы хотите изменить её на corrected-branch-name, сохранив при этом всю историю. Вместе с этим, вы также хотите изменить имя ветки на удалённом сервере (GitHub, GitLab или др сервер). Как это сделать?

Переименуйте ветку локально с помощью команды git branch --move:

git branch --move bad-branch-name corrected-branch-name

Ветка bad-branch-name будет переименована в corrected-branch-name, но это изменение пока только локальное. Чтобы все остальные увидели исправленную ветку в удалённом репозитории, отправьте её туда:

git push --set-upstream origin corrected-branch-name

Теперь проверим, где мы сейчас находимся:

git branch --all
  * corrected-branch-name
    main
    remotes/origin/bad-branch-name
    remotes/origin/corrected-branch-name
    remotes/origin/main

Обратите внимание, что текущая ветка corrected-branch-name, которая также присутствует и на удалённом сервере. Однако, старая ветка всё ещё по-прежнему там, но её можно удалить с помощью команды:

git push origin --delete bad-branch-name

Изменение имени главной ветки

Переименуйте локальную ветку master в main с помощью следующей команды:

git branch --move master main

Чтобы все остальные могли видеть новую ветку main, вам нужно отправить её в общий репозиторий. Это делает переименованную ветку доступной в удалённом репозитории.

git push --set-upstream origin main

В итоге, состояние репозитория становится следующим:

git branch --all
  * main
    remotes/origin/HEAD -> origin/master
    remotes/origin/main
    remotes/origin/master

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

• Все проекты, которые зависят от текущего, должны будут обновить свой код и/или конфигурацию.

• Обновите конфигурацию всех запускаемых тестов.

• Исправьте скрипты сборки и публикации артефактов.

• Поправьте настройки репозитория на сервере: задайте новую ветку по умолчанию, обновите правила слияния, а также прочие настройки, которые зависят от имени веток.

• Обновите документацию, исправив ссылки, указывающие на старую ветку.

• Слейте или отмените запросы на слияние изменений, нацеленные на старую ветку.

После того, как вы выполнили все эти задачи и уверены, что ветка main работает так же, как ветка master, вы можете удалить ветку master:

git push origin --delete master

Работа с ветками

Долгоживущие ветки

Идея состоит в том, что каждая ветка представляет собой определённый уровень стабильности; как только он повышается, содержимое сливается в ветку уровнем выше.

image

image


Тематические ветки

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

image

image


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

origin — это не специальное название.
Подобно названию ветки master, origin не имеет какого-либо специального значения в Git. В то время как master — это название по умолчанию для ветки при выполнении git init только потому, что часто используется, origin — это название по умолчанию для удалённого сервера, когда вы запускаете git clone. Если вы выполните git clone -o newname, то по умолчанию ветка слежения будет иметь вид newname/master.

image

Если вы сделаете что-то в своей локальной ветке master, а тем временем кто-то отправит изменения на сервер git.ourcompany.com и обновит там ветку master, то ваши истории продолжатся по-разному. Пока вы не свяжетесь с сервером origin ваш указатель origin/master останется на месте.

image

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

image

Чтобы продемонстрировать, как будут выглядеть удалённые ветки в ситуации с несколькими удалёнными серверами, предположим, что у вас есть ещё один внутренний Git-сервер, который используется для разработки только одной из ваших команд разработчиков. Этот сервер находится на git.team1.ourcompany.com. Вы можете добавить его в качестве новой удалённой ссылки для текущего проекта с помощью команды git remote add, как было описано в главе Основы Git. Назовите этот удалённый сервер teamone — это имя будет сокращением вместо полного URL.

image

Теперь вы можете выполнить команду git fetch teamone для получения всех изменений с сервера teamone, которых у вас нет локально. Так как в данный момент на этом сервере есть только те данные, что содержит сервер origin, Git ничего не получит, но создаст ветку слежения с именем teamone/master, которая будет указывать на тот же коммит, что и ветка master на сервере teamone.

image


Отправка изменений

Когда вы хотите поделиться веткой, вам необходимо отправить её на удалённый сервер, где у вас есть права на запись. Ваши локальные ветки автоматически не синхронизируются с удалёнными при отправке — вам нужно явно указать те ветки, которые вы хотите отправить. Таким образом, вы можете использовать свои личные ветки для работы, которую не хотите показывать, а отправлять только те тематические ветки, над которыми вы хотите работать с кем-то совместно. Если у вас есть ветка serverfix, над которой вы хотите работать с кем-то ещё, вы можете отправить её точно так же, как вы отправляли вашу первую ветку. Выполните команду git push <remote> <branch>:

git push origin serverfix

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

Получение локальной ветки из удалённой ветки автоматически создаёт то, что называется веткой слежения (а ветка, за которой следит локальная называется upstream branch). Ветки слежения — это локальные ветки, которые напрямую связаны с удалённой веткой. Если, находясь на ветке слежения, выполнить git pull, то Git уже будет знать с какого сервера получать данные и какую ветку использовать для слияния.

При клонировании репозитория, как правило, автоматически создаётся ветка master, которая следит за origin/master. Однако, при желании вы можете настроить отслеживание и других веток — следить за ветками на других серверах или отключить слежение за веткой master. Вы только что видели простейший пример, что сделать это можно с помощью команды git checkout -b <branch> <remote>/<branch>. Это часто используемая команда, поэтому Git предоставляет сокращённую форму записи в виде флага --track:

git checkout --track origin/serverfix
  Branch serverfix set up to track remote branch serverfix from origin.
  Switched to a new branch 'serverfix'

В действительности, это настолько распространённая команда, что существует сокращение для этого сокращения. Если вы пытаетесь извлечь ветку, которая не существует, но существует только одна удалённая ветка с точно таким же именем, то Git автоматически создаст ветку слежения:

git checkout serverfix
  Branch serverfix set up to track remote branch serverfix from origin.
  Switched to a new branch 'serverfix'

Чтобы создать локальную ветку с именем, отличным от имени удалённой ветки, просто укажите другое имя:

git checkout -b newname origin/serverfix
  Branch sf set up to track remote branch serverfix from origin.
  Switched to a new branch 'newname'

Теперь ваша локальная ветка newname будет автоматически получать изменения из origin/serverfix.

Если у вас уже есть локальная ветка и вы хотите настроить ее на слежение за удалённой веткой, которую вы только что получили, или хотите изменить используемую upstream-ветку, то воспользуйтесь параметрами -u или --set-upstream-to для команды git branch, чтобы явно установить новое значение.

git branch -u origin/serverfix
  Branch serverfix set up to track remote branch serverfix from origin.

Сокращение Upstream

Если у вас настроена отслеживаемая ветка, вы можете ссылаться на нее с помощью сокращений @{upstream} или @{u}. Итак, если вы находитесь на ветке master и она следит за origin/master, при желании вы можете использовать git merge @{u} вместо git merge origin/master.

Если вы хотите посмотреть как у вас настроены ветки слежения, воспользуйтесь опцией -vv для команды git branch. Это выведет список локальных веток и дополнительную информацию о том, какая из веток отслеживается, отстаёт, опережает или всё сразу относительно отслеживаемой.

git branch -vv
    iss53     7e424c3 [origin/iss53: ahead 2] Add forgotten brackets
    master    1ae2a45 [origin/master] Deploy index fix
  * serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] This should do it
    testing   5ea463a Try something new

Итак, здесь мы видим, что наша ветка iss53 следит за origin/iss53 и «опережает» её на два изменения — это значит, что у нас есть два локальных коммита, которые не отправлены на сервер. Мы также видим, что наша ветка master отслеживает ветку origin/master и находится в актуальном состоянии. Далее мы можем видеть, что локальная ветка serverfix следит за веткой server-fix-good на сервере teamone, опережает её на три коммита и отстает на один — это значит, что на сервере есть один коммит, который мы ещё не слили, и три локальных коммита, которые ещё не отправлены на сервер. В конце мы видим, что наша ветка testing не отслеживает удаленную ветку.

Важно отметить, что эти цифры описывают состояние на момент последнего получения данных с каждого из серверов. Эта команда не обращается к серверам, а лишь говорит вам о том, какая информация с этих серверов сохранена в локальном кэше. Если вы хотите иметь актуальную информацию об этих числах, вам необходимо получить данные со всех ваших удалённых серверов перед запуском команды. Сделать это можно вот так:

git fetch --all; git branch -vv

Получение изменений

Команда git fetch получает с сервера все изменения, которых у вас ещё нет, но не будет изменять состояние вашей рабочей копии. Эта команда просто получает данные и позволяет вам самостоятельно сделать слияние. Тем не менее, существует команда git pull, которая в большинстве случаев является командой git fetch, за которой непосредственно следует команда git merge. Если у вас настроена ветка слежения как показано в предыдущем разделе, или она явно установлена, или она была создана автоматически командами clone или checkout, git pull определит сервер и ветку, за которыми следит ваша текущая ветка, получит данные с этого сервера и затем попытается слить удалённую ветку. Обычно, лучше явно использовать команды fetch и merge, поскольку магия git pull может часто сбивать с толку.


Удаление веток на удалённом сервере

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

git push origin --delete serverfix
  To https://github.com/schacon/simplegit
   - [deleted]         serverfix

Всё, что делает эта строка — удаляет указатель на сервере. Как правило, Git сервер хранит данные пока не запустится сборщик мусора, поэтому если ветка была удалена случайно, чаще всего её легко восстановить.


Перебазирование

В Git есть два способа внести изменения из одной ветки в другую: слияние и перебазирование.

image

Как мы выяснили ранее, простейший способ выполнить слияние двух веток — это команда merge. Она осуществляет трёхстороннее слияние между двумя последними снимками сливаемых веток (C3 и C4) и самого недавнего общего для этих веток родительского снимка (C2), создавая новый снимок (и коммит)

image

Тем не менее есть и другой способ: вы можете взять те изменения, что были представлены в C4, и применить их поверх C3. В Git это называется перебазированием.

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

git checkout experiment
git rebase master
  First, rewinding head to replay your work on top of it...
  Applying: added staged command

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

image

После этого вы можете переключиться обратно на ветку master и выполнить слияние перемоткой.

git checkout master
git merge experiment

image

Теперь снимок, на который указывает C4' абсолютно такой же, как тот, на который указывал C5 в примере с трёхсторонним слиянием. Нет абсолютно никакой разницы в конечном результате между двумя показанными примерами, но перебазирование делает историю коммитов чище.

Если вы взглянете на историю перебазированной ветки, то увидите, что она выглядит абсолютно линейной: будто все операции были выполнены последовательно, даже если изначально они совершались параллельно в разных ветках.

Часто вы будете делать так для уверенности, что ваши коммиты могут быть бесконфликтно слиты в удалённую ветку — возможно, в проекте, куда вы пытаетесь внести вклад, но владельцем которого вы не являетесь. В этом случае вам следует работать в своей ветке и затем перебазировать вашу работу поверх origin/master, когда вы будете готовы отправить свои изменения в основной проект. Тогда владельцу проекта не придётся делать никакой лишней работы — всё решится простой перемоткой или бесконфликтным слиянием.

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


Опасности перемещения

Но даже перебазирование, при всех своих достоинствах, не лишено недостатков, которые можно выразить одной строчкой:
Не перемещайте коммиты, уже отправленные в публичный репозиторий

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


Перемещение vs. Слияние

История коммитов в вашем репозитории — это запись того, что на самом деле произошло. Это исторический документ, ценный сам по себе, и его нельзя подделывать. С этой точки зрения изменение истории коммитов практически кощунственно; вы лжёте о том, что на самом деле произошло.

Противоположная точка зрения заключается в том, что история коммитов — это история того, как был сделан ваш проект. Вы не публикуете первый черновик книги или инструкции по поддержке вашего программного обеспечения, так как это нуждается в тщательном редактировании. Сторонники этого лагеря считают использование инструментов rebase и filter-branch способом рассказать историю проекта наилучшим образом для будущих читателей.



К оглавлению

Протоколы

Git умеет работать с четырьмя сетевыми протоколами для передачи данных: локальный, HTTP, Secure Shell (SSH) и Git.


Локальный протокол

Если у вас смонтирована общая файловая система, вы можете клонировать, отправлять и получать изменения из локального репозитория. Чтобы клонировать такой репозиторий или добавить его в качестве удалённого в существующий проект, используйте путь к репозиторию в качестве URL. Например, для клонирования локального репозитория вы можете выполнить что-то вроде этого:

git clone /srv/git/project.git

Или этого:

git clone file:///srv/git/project.git

Чтобы добавить локальный репозиторий в существующий проект, вы можете воспользоваться командой:

git remote add local_proj /srv/git/project.git

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


Протоколы HTTP

Новая версия часто называется Умным (Smart) HTTP, а старая Тупым (Dumb) HTTP. Сначала мы рассмотрим Умный протокол.


Умный HTTP

Умный протокол HTTP поверх стандартных HTTP/S портов и может использовать различные механизмы аутентификации HTTP, это часто проще для пользователя, чем что-то вроде SSH, так как можно использовать аутентификацию по логину/паролю вместо установки SSH-ключей. Наверное, сейчас он стал наиболее популярным способом использования Git, так как может использоваться и для анонимного доступа как протокол git://, и для отправки изменений с аутентификацией и шифрованием как протокол SSH. Вместо использования разных адресов URL для этих целей, можно использовать один URL адрес для всего. Если вы пытаетесь отослать изменения и репозиторий требует аутентификации (обычно так и есть), сервер может спросить логин и пароль. То же касается и доступа на чтение. На самом деле для сервисов вроде GitHub, адрес URL, который вы используете для просмотра репозитория в браузере (например, https://github.com/schacon/simplegit), можно использовать для клонирования или, если у вас есть доступ, для отправки изменений.


Тупой HTTP

Если сервер не отвечает на умный запрос Git по HTTP, клиент Git попытается откатиться на более простой Тупой HTTP-протокол. Тупой протокол ожидает, что голый репозиторий Git будет обслуживаться веб-сервером как набор файлов

Прелесть тупого протокола HTTP — в простоте настройки. По сути, всё, что необходимо сделать — поместить голый репозиторий в корневой каталог HTTP и установить обработчик post-update (смотри Хуки в Git). Теперь каждый может клонировать репозиторий, если имеет доступ к веб-серверу, на котором он был размещен. Таким образом, чтобы открыть доступ на чтение к вашему репозиторию посредством HTTP, нужно сделать что-то наподобие этого:

cd /var/www/htdocs/
git clone --bare /path/to/git_project gitproject.git
cd gitproject.git
mv hooks/post-update.sample hooks/post-update
chmod a+x hooks/post-update

Вот и всё. Обработчик post-update, входящий в состав Git по умолчанию, выполняет необходимую команду (git update-server-info), чтобы получение изменений и клонирование по HTTP работали правильно. Эта команда выполняется, когда вы отправляете изменения в репозиторий (возможно посредством SSH); затем остальные могут клонировать его командой

git clone https://example.com/gitproject.git

Протокол SSH

Часто используемый транспортный протокол для самостоятельного хостинга Git — это SSH. SSH — протокол с аутентификацией.
Чтобы клонировать Git-репозиторий по SSH, вы можете указать префикс ssh:// в URL, например:

git clone ssh://[user@]server/project.git

Или можно использовать для протокола SSH краткий синтаксис наподобие scp:

git clone [user@]server:project.git

Также вы можете не указывать имя пользователя, Git будет использовать то, под которым вы вошли в систему.


Git-протокол

Следующий протокол — Git-протокол. Вместе с Git поставляется специальный демон, который слушает отдельный порт 9418 и предоставляет сервис, схожий с протоколом SSH, но абсолютно без аутентификации. Чтобы использовать Git-протокол для репозитория, вы должны создать файл git-export-daemon-ok, иначе демон не будет работать с этим репозиторием, но следует помнить, что в протоколе отсутствуют средства безопасности. Соответственно, любой репозиторий в Git может быть либо доступен для клонирования всем, либо нет. Как следствие, обычно отправлять изменения по этому протоколу нельзя. Вы можете открыть доступ на запись, но из-за отсутствия аутентификации в этом случае кто угодно, зная URL вашего проекта, сможет его изменить. В общем, это редко используемая возможность.

Достоинства
Git-протокол ― часто самый быстрый из доступных протоколов. Если у вас проект с публичным доступом и большой трафик, или у вас очень большой проект, для которого не требуется аутентификация пользователей для чтения, вам стоит настроить демон Git для вашего проекта. Он использует тот же механизм передачи данных, что и протокол SSH, но без дополнительных затрат на шифрование и аутентификацию.

Недостатки
Недостатком Git-протокола является отсутствие аутентификации



К оглавлению

Распределенный рабочий процесс

Централизованная работа

Центральный хаб или репозиторий может принимать код, а все остальные синхронизируют свою работу с ним. Все разработчики являются узлами (пользователями хаба) и синхронизируются только с ним.

image

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

Достаточно создать один репозиторий и предоставить каждому члену команды push-доступ; Git не позволит перезаписать изменения, сделанные другими.

Предположим, Джон и Джессика начинают работать над проектом одновременно. Джон вносит изменения и отправляет их на сервер. Затем Джессика пытается отправить свои изменения, но сервер их отклоняет. Ей говорят, что она пытается отправить изменения, для которых невозможно выполнить быструю перемотку и она не сможет это сделать пока не получит все новые для неё изменения и не сольёт их. Такой рабочий процесс привлекает большинство людей, так как реализует парадигму, с которой они уже знакомы. Такой подход применим не только к небольшим командам. Используя модель ветвления Git, сотни разработчиков могут одновременно работать над одним проектом, используя при этом десятки веток.


Диспетчер интеграции

Так как Git допускает использование нескольких удалённых репозиториев, то становится возможным организация рабочего процесса, где каждый разработчик имеет доступ на запись в свой публичный репозиторий и доступ на чтение ко всем остальным. При таком сценарии обычно существует канонический репозиторий, который представляет собой официальный проект. Для отправки своих наработок в этот проект следует создать его клон и отправить изменения в него. Затем вы отправляете запрос на слияние ваших изменений сопровождающему основного проекта. В свою очередь он может добавить ваш репозиторий как удаленный, протестировать ваши изменения локально, слить их в соответствующую ветку и отправить в основной репозиторий.

Процесс работает в следующей последовательности:

1. Сопровождающий проекта отправляет изменения в свой публичный репозиторий.
2. Участник клонирует этот репозиторий и вносит изменения.
3. Участник отправляет свои изменения в свой публичный репозиторий.
4. Участник отправляет письмо сопровождающему с запросом на слияние изменений.
5. Сопровождающий добавляет репозиторий участника как удалённый и сливает изменения локально.
6. Сопровождающий отправляет слитые изменения в основной репозиторий.

image


Диктатор и помощники

Это вариант организации рабочего процесса с использованием нескольких репозиториев. В основном такой подход используется на огромных проектах, насчитывающих сотни участников; самый известный пример — ядро Linux. Помощники (lieutenants) — это интеграционные менеджеры, которые отвечают за отдельные части репозитория. Над ними главенствует один диспетчер интеграции, которого называют великодушным диктатором. Репозиторий диктатора выступает как эталонный (blessed), откуда все участники процесса должны получать изменения. Процесс работает следующим образом:

1. Обычные разработчики работают в своих тематических ветках и перебазируют свою работу относительно ветки `master`. Ветка `master` — это ветка эталонного репозитория в которую имеет доступ только диктатор.
2. Помощники сливают тематические ветки разработчиков в свои ветки `master`
3. Диктатор сливает ветки `master` помощников в свою ветку `master`.
4. Наконец, диктатор отправляет свою ветку `master` в эталонный репозиторий, чтобы все остальные могли перебазировать свою работу на основании неё.

image


Участие в проекте

Правила создания коммитов

Для начала, вам не следует отправлять ненужные пробелы. Git предоставляет простой способ проверки — перед коммитом выполните команду git diff --check, которая выведет список ненужных пробелов.

Пишите сообщение коммита в императиве: «Fix bug» а не «Fixed bug» или «Fixes bug».

Вот шаблон хорошего сообщения коммита:

Краткое (не более 50 символов) резюме с заглавной буквы
Более детальный, поясняющий текст, если он требуется.     
Старайтесь не превышать длину строки в 72 символа.        
В некоторых случаях первая строка подразумевается как тема письма, а всё остальное -- как тело письма.        
Пустая строка, отделяющая сводку от тела, имеет решающее 
значение (за исключением случаев, когда детального описания 
нет); в противном случае такие инструменты, как rebase, могут 
вас запутать.      

Сообщения коммитов следует писать используя неопределенную форму глагола совершенного вида
повелительного наклонения: «Fix bug» (Исправить баг).
Это соглашение соответствует сообщениям коммитов, 
генерируемых такими командами, как `git merge` и `git revert`.

Последующие абзацы идут после пустых строк.

- Допускаются обозначения пунктов списка

- Обычно, элементы списка обозначаются с помощью тире или звёздочки, с одним пробелом перед ними, а 
разделяются пустой строкой, но соглашения могут отличаться

- Допускается обратный абзацный отступ.

Небольшая команда

Давайте посмотрим что происходит, когда два разработчика начинают работать вместе и используют общий репозиторий. Первый разработчик Джон клонирует репозиторий, вносит изменения и делает коммит локально. (В последующих примерах сообщения протокола заменены на ... с целью их немного сократить.)

# Компьютер Джона
git clone john@githost:simplegit.git
Cloning into 'simplegit'...
...
cd simplegit/
vim lib/simplegit.rb
git commit -am 'Remove invalid default value' [master 738ee87] Remove invalid default value
   1 files changed, 1 insertions(+), 1 deletions(-)

Второй разработчик Джессика делает то же самое — клонирует репозиторий и делает коммит:

# Компьютер Джессики
git clone jessica@githost:simplegit.git Cloning into 'simplegit'...
...
cd simplegit/
vim TODO
git commit -am 'Add reset task'
[master fbff5bc] Add reset task
   1 files changed, 1 insertions(+), 0 deletions(-)

Затем Джессика отправляет изменения на сервер:

# Компьютер Джессики
git push origin master
...
To jessica@githost:simplegit.git
     1edee6b..fbff5bc  master -> master

Джон вносит некоторые изменения, делает коммит и пытается отправить его на тот же сервер:

# Компьютер Джона
git push origin master
To john@githost:simplegit.git
   ! [rejected]        master -> master (non-fast forward)
  error: failed to push some refs to 'john@githost:simplegit.git'

В данном случае изменения Джона отклонены, так как Джессика уже отправила свои. В Git вы должны сначала слить изменения локально. Джон должен получить изменения Джессики и слить их локально, прежде чем сможет отправить свои. Для начала, Джон получает изменения Джессики (слияния изменений пока что не происходит):

git fetch origin
  ...
  From john@githost:simplegit
   + 049d078...fbff5bc master     -> origin/master

В этот момент локальный репозиторий Джона выглядит примерно так:

image

Теперь Джон может слить полученные изменения Джессики со своей локальной веткой:

git merge origin/master
  Merge made by the 'recursive' strategy.
   TODO |    1 +
   1 files changed, 1 insertions(+), 0 deletions(-)

Процесс слияния проходит гладко — история коммитов у Джона выглядит примерно так:

image

Теперь Джон может протестировать новый код, чтобы убедиться в корректной работе объединённых изменений, после чего он может отправить объединённые изменения на сервер:

git push origin master
  ...
  To john@githost:simplegit.git
     fbff5bc..72bbc59  master -> master

В результате история коммитов у Джона выглядит так:

image

Тем временем Джессика создала тематическую ветку с названием issue54 и сделала в ней три коммита. Тем временем Джессика создала тематическую ветку с названием issue54 и сделала в ней три коммита. Она ещё не получила изменения Джона, поэтому история коммитов у неё выглядит следующим образом:

image

Внезапно Джессика узнаёт, что Джон отправил какие-то изменения на сервер и теперь она хочет на них взглянуть; для этого ей следует получить с сервера все новые изменения:

# Компьютер Джессики
$ git fetch origin
...
From jessica@githost:simplegit
     fbff5bc..72bbc59  master     -> origin/master

Это приводит к получению изменений, отправленных Джоном в репозиторий. Теперь, история коммитов у Джессики выглядит так:

image

Джессика считает, что её тематическая ветка готова, но так же хочет знать какие изменения следует слить со своей работой перед отправкой на сервер. Для прояснения ситуации он выполняет команду git log:

git log --no-merges issue54..origin/master
  commit 738ee872852dfaa9d6634e0dea7a324040193016
  Author: John Smith <[email protected]>
  Date:   Fri May 29 16:01:27 2009 -0700
     Remove invalid default value

issue54..origin/master — это синтаксис фильтра, который указывает Git отображать только список коммитов, которые существуют в последней ветке (в данном случае origin/master), но отсутствуют в первой (в данном случае issue54). Более детально этот синтаксис рассматривается в разделе Диапазоны коммитов главы 7.

В данном случае, в выводе команды мы видим только один коммит, сделанный Джоном и ещё не слитый Джессикой. Если она сольёт origin/master, то это будет единственный коммит, который изменит локальное состояние.

Теперь, Джессика может слить изменения тематической ветки и изменения Джона (origin/master) в свою локальную ветку master, а затем отправить её на сервер.
Для начала (при условии отсутствия изменений в тематической ветке, не включённых в коммит), Джессика переключается на свою ветку master:

git checkout master
  Switched to branch 'master'
  Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.

Обе ветки origin/master и issue54 являются отслеживаемыми, поэтому порядок слияния не важен. Конечный результат будет идентичным вне зависимости от порядка слияния, однако история коммитов будет немного отличаться. Джессика решает слить ветку issue54 первой:

 git merge issue54
  Updating fbff5bc..4af4298
  Fast forward
README | 1+
lib/simplegit.rb | 6 +++++-
2 files changed, 6 insertions(+), 1 deletions(-)

Проблем не возникает; как можно заметить, это простое перемещение вперед. Теперь Джессика заканчивает процесс локального слияния объединяя полученные ранее изменения Джона, находящиеся в ветке origin/master:

git merge origin/master
  Auto-merging lib/simplegit.rb
  Merge made by the 'recursive' strategy.
   lib/simplegit.rb |    2 +-
   1 files changed, 1 insertions(+), 1 deletions(-)

Слияние прошло чисто и теперь история коммитов у Джессики выглядит следующим образом:

image

Теперь Джессика может отправить свою ветку master в origin/master, при условии что Джон больше не отправлял изменений:

git push origin master
  ...
  To jessica@githost:simplegit.git
     72bbc59..8059c15  master -> master

Каждый разработчик сделал коммиты несколько раз и успешно слил изменения другого.

image

В течение некоторого времени вы работаете в тематической ветке, а затем сливаете изменения в ветку master когда всё готово. Чтобы поделиться проделанной работой, вы сливаете её в вашу ветку master, затем получаете и сливаете изменения из ветки origin/master если таковые имеются, и наконец, отправляете все изменения в ветку master на сервере.

image


Команда с руководителем

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

Предположим, что Джон и Джессика вместе работают над одной функцией (назовём её featureA), при этом Джессика и Джози работают над другой (featureB). В этом случае компания использует тип рабочего процесса с менеджером по интеграции, при котором работа отдельных групп интегрируется определёнными инженерами, а ветка master основного репозитория может быть обновлена только этими инженерами. При таком сценарии вся работа ведётся в отдельных ветках для каждой команды, а затем объединяется интегратором.

Давайте рассмотрим рабочий процесс Джессики, так как она работает над двумя функциями, параллельно сотрудничая с разными разработчиками. Предположим, что репозиторий уже клонирован и она решает работать сначала над функцией featureA. Джессика создаёт новую ветку для этой функции и некоторое время работает над ней:

# Компьютер Джессики
git checkout -b featureA
Switched to a new branch 'featureA'
vim lib/simplegit.rb
git commit -am 'Add limit to log function' [featureA 3300904] Add limit to log function
   1 files changed, 1 insertions(+), 1 deletions(-)

В данный момент ей необходимо поделиться проделанной работой с Джоном, поэтому Джессика отправляет ветку featureA на сервер. У Джессики нет доступа на запись в ветку master (он есть только у интеграторов), поэтому для совместной работы с Джоном она отправляет изменения в другую ветку:

git push -u origin featureA
  ...
  To jessica@githost:simplegit.git
   * [new branch]      featureA -> featureA

Джессика отправляет письмо Джону с уведомлением, что внесённые ей изменения уже доступны в ветке featureA. Пока Джессика ждёт ответа от Джона, она решает поработать над другой функцией featureB вместе с Джози. Для начала, Джесика создаёт новую тематическую ветку, базируясь на состоянии ветки master на сервере:

# Компьютер Джессики
git fetch origin
git checkout -b featureB origin/master Switched to a new branch 'featureB'

После этого, Джессика делает несколько коммитов в ветке featureB:

vim lib/simplegit.rb
git commit -am 'Make ls-tree function recursive'
  [featureB e5b0fdc] Make ls-tree function recursive
   1 files changed, 1 insertions(+), 1 deletions(-)
vim lib/simplegit.rb
git commit -am 'Add ls-files'
  [featureB 8512791] Add ls-files
   1 files changed, 5 insertions(+), 0 deletions(-)

image

Джессика готова отправить свою работу, но получает письмо Джози, что начальная работа уже отправлена на сервер в ветку featureBee. Теперь Джессике нужно слить эти изменения со своими перед отправкой на сервер. Изменения Джози она получает командой git fetch:

  $ git fetch origin
  ...
  From jessica@githost:simplegit
   * [new branch]      featureBee -> origin/featureBee

Полагая что Джессика находится в ветке featureB, она может слить полученные изменения Джози со своими при помощи команды git merge:

git merge origin/featureBee
  Auto-merging lib/simplegit.rb
  Merge made by the 'recursive' strategy.
   lib/simplegit.rb |    4 ++++
   1 files changed, 4 insertions(+), 0 deletions(-)

Одна небольшая проблема — ей нужно отправить слитые изменения из локальной ветки featureB в ветку featureBee на сервере. Для этого в команде git push Джессика указывает названия локальной и удалённой веток, разделенных двоеточием:

git push -u origin featureB:featureBee
  ...
  To jessica@githost:simplegit.git
     fba9af8..cd685d1  featureB -> featureBee

Это называется спецификация ссылок. В разделе Спецификации ссылок главы 10 приведено более детальное описание спецификаций ссылок Git и различные способы их использования. Так же обратите внимание на флаг -u; это сокращение для --set-upstream, который настраивает ветки для упрощения отправки и получения изменений в дальнейшем.

После этого, Джессика получает письмо от Джона, в котором он сообщает, что отправил некоторые изменения в ветку featureA и просит их проверить. Джесика выполняет команду git fetch для получения всех новых изменений, включая изменения Джона:

git fetch origin
  ...
  From jessica@githost:simplegit
     3300904..aad881d  featureA   -> origin/featureA

Теперь, она может посмотреть что именно было изменено путём сравнения полученной ветки featureA со своей локальной веткой:

git log featureA..origin/featureA
  commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
  Author: John Smith <[email protected]>
  Date:   Fri May 29 19:57:33 2009 -0700
      Increase log output to 30 from 25

Если Джессику всё устраивает, то она сливает изменения Джона в свою ветку featureA:

git checkout featureA
  Switched to branch 'featureA'
git merge origin/featureA
  Updating 3300904..aad881d
  Fast forward
   lib/simplegit.rb |   10 +++++++++-
  1 files changed, 9 insertions(+), 1 deletions(-)

Джессика решает немного подправить, делает коммит в локальной ветке featureA и отправляет конечный результат на сервер:

git commit -am 'Add small tweak to merged content'
  [featureA 774b3ed] Add small tweak to merged content
   1 files changed, 1 insertions(+), 1 deletions(-)
git push
  ...
  To jessica@githost:simplegit.git
     3300904..774b3ed  featureA -> featureA

В результате история коммитов у Джессики выглядит так:

image

Джессика, Джози и Джон информируют интеграторов, что ветки featureA и featureBee на сервере готовы к слиянию в основную. После того как интеграторы сольют эти ветки в основную, полученные изменения будут содержать коммит слияния, а история коммитов будет иметь вид:

image

Возможность совместной работы небольших подгрупп команды в удалённых ветках без необходимости вовлекать или мешать всей команде — огромное преимущество Git.

image


Форк публичного проекта

Участие в публичном проекте сильно отличается. Так как у вас нет доступа обновлять ветки проекта напрямую, то передавать проделанную работу следует другим способом. В первом примере рассматривается участие в публичном проекте посредством форка на Git платформах, где возможно его простое создание.

Для начала, вам следует клонировать основной репозиторий, создать тематическую ветку для одного или нескольких патчей и работать в ней.

git clone <url>
cd project
git checkout -b featureA
    ... work ...
git commit
    ... work ...
git commit

Когда работа в тематической ветке завершена и вы готовы передать изменения исходному проекту, перейдите на страницу исходного проекта и нажмите кнопку Fork, тем самым создавая доступный для записи форк проекта. Затем нужно добавить URL на созданный проект как второй удалённый репозиторий, в нашем случае с именем myfork:

git remote add myfork <url>

После этого следует отправить проделанную работу в него. Проще отправить вашу тематическую ветку, в которой велась работа, чем сливать изменения в вашу ветку master и отправлять её. Если ваши изменения будут отклонены или какой-то из коммитов будет применен выборочно (команда cherry-pick более детально рассматривается в разделе Схема с перебазированием и отбором главы 5), то вы не сможете вернуть состояние вашей ветки master. Если менеджер проекта сольёт, перебазирует или выборочно применит ваши изменения, то вы сможете их получить из оригинального репозитория.

Отправить свои изменения вы можете командой:

git push -u myfork featureA

Когда ваши изменения отправлены в ваш форк, следует уведомить сопровождающих исходного проекта о том, что у вас есть изменения для интеграции. Обычно, это называется запросом слияния, который вы можете создать используя как веб сайт — GitHub использует собственный механизм запросов слияния, который будет рассмотрен в главе GitHub — так и команду git request-pull, отправив её вывод по почте.

Команда git request-pull принимает в качестве аргументов название базовой ветки, в которую следует влить изменения из вашей тематической ветки, и ссылку на Git репозиторий, из которого следует получать изменения, а результатом будет список всех изменений, которые вы предлагаете внести. Например, если Джессика хочет отправить Джону запрос слияния и она отправила два коммита в тематическую ветку, то ей следует выполнить команду:

git request-pull origin/master myfork
  The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
  Jessica Smith (1):
          Create new function
  are available in the git repository at:
    git://githost/simplegit.git featureA
  Jessica Smith (2):
        Add limit to log function
        Increase log output to 30 from 25
   lib/simplegit.rb |   10 +++++++++-
   1 files changed, 9 insertions(+), 1 deletions(-)

Вывод команды можно отправить сопровождающему проекта — в нём говорится с какого момента велась работа, приводится сводка коммитов и указывается откуда можно получить эти изменения.

В проектах, где вы не являетесь сопровождающим, проще держать ветку master в соответствии с origin/master, а работать в тематических ветках — так вам будет проще отменить изменения, если они будут отклонены. Разделение направлений разработки по изолированным веткам облегчит их перебазирование, когда состояние основного репозитория изменится, а ваши коммиты уже не смогут быть чисто применены. Например, если вы собираетесь отправить исправления на другую тему, не продолжайте работать в той же тематической ветке — создайте новую, базируясь на ветке master основного репозитория:

git checkout -b featureB origin/master
    ... work ...
git commit
git push myfork featureB
git request-pull origin/master myfork
    ... email generated request pull to maintainer ...
git fetch origin

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

image

Предположим, что сопровождающий проекта слил некоторый набор других патчей, а затем пытается применить вашу первую ветку, но она уже не может быть слита без конфликтов. В этом случае вы можете попытаться перебазировать свою ветку относительно origin/master, разрешить конфликты и заново отправить свои изменения:

git checkout featureA
git rebase origin/master
git push -f myfork featureA

Эти действия перепишут историю ваших коммитов, которая станет похожа на История коммитов после работы над featureA.

image

Так как вы перебазировали ветку, то должны указать флаг -f во время отправки на сервер, чтобы переписать историю ветки featureA коммитами, не являющимися её потомками. Альтернативным решением может быть отправка этих исправлений в ветку с другим названием (например, featureAv2).

Давайте рассмотрим ещё один возможный сценарий: сопровождающий посмотрел вашу вторую ветку и ему понравилась идея, но он хочет попросить вас изменить некоторые детали. Возможно, вы так же захотите перебазировать эту ветку относительно текущего состояния ветки master. Вы создаёте новую ветку базируясь на текущей origin/master, сбрасываете все изменения в неё, разрешаете возможные конфликты, делаете изменения в реализации и отправляете её как новую ветку:

git checkout -b featureBv2 origin/master
git merge --squash featureB
    ... change implementation ...
git commit
git push myfork featureBv2

Опция --squash берет все изменения из указанной ветки, объединяет их и создаёт новый коммит в текущей ветке без создания коммита слияния. Это значит, что новый коммит будет иметь только одного родителя и будет включать все изменения из другой ветки, а так же позволяет внести дополнительные изменения до фактического создания коммита. Опция --no-commit указывает Git не создавать новый коммит автоматически. Теперь можно отправить сопровождающему сообщение, что вы сделали запрошенные изменения и они находятся в вашей ветке featureBv2.

image


Публичный проект посредством E-Mail

Существует несколько больших старых проектов, которые принимают патчи посредством почтовых рассылок.
Рабочий процесс похож на предыдущий — вы создаёте тематическую ветку для каждого набора патчей, над которыми собираетесь работать. Основное отличие в способе их передачи проекту. Вместо того, чтобы форкнуть проект и отправить в него свои изменения, вы генерируете почтовую версию для каждого набора коммитов с целью отправки её в список рассылки разработчиков:

git checkout -b topicA
    ... work ...
git commit
    ... work ...
git commit

Сейчас у вас два коммита, которые вы хотите отправить в почтовую рассылку. Используйте команду git format-patch для генерации файлов в формате mbox, которые можно отправить по почте — это обернёт каждый коммит в сообщение e-mail, где первая строка из сообщения коммита будет темой письма, а остальные строки плюс сам патч будут телом письма. Применение патча в формате e-mail, сгенерированного с помощью команды format-patch, сохраняет всю информацию о коммите должным образом.

git format-patch -M origin/master
  0001-add-limit-to-log-function.patch
  0002-increase-log-output-to-30-from-25.patch

Команда format-patch выводит список имён файлов патчей, которые она создаёт. Флаг -M указывает Git искать переименования. В итоге файлы выглядят вот так:

cat 0001-add-limit-to-log-function.patch
  From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
  From: Jessica Smith <[email protected]>
  Date: Sun, 6 Apr 2008 10:17:23 -0700
  Subject: [PATCH 1/2] Add limit to log function
  Limit log functionality to the first 20
  ---
   lib/simplegit.rb |    2 +-
   1 files changed, 1 insertions(+), 1 deletions(-)
   
   diff --git a/lib/simplegit.rb b/lib/simplegit.rb
  index 76f47bc..f9815f1 100644
  --- a/lib/simplegit.rb
  +++ b/lib/simplegit.rb
  @@ -14,7 +14,7 @@ class SimpleGit
     end
     def log(treeish = 'master')
     command("git log #{treeish}")
     command("git log -n 20 #{treeish}")
end
   def ls_tree(treeish = 'master')
--
2.1.0

Вы можете редактировать эти файлы, добавляя информацию для списка рассылки, но которую вы не хотите видеть в сообщении к коммиту. Если добавить текст между строкой --- и началом патча (строка diff --git), то разработчики увидят его, но применяться он не будет.
Для отправки в список рассылки можно либо вставить файлы в почтовую программу, либо отправить их из командной строки.

Git предоставляет утилиту, которая умеет отправлять корректно отформатированные патчи по протоколу IMAP.

Для начала, следует настроить раздел imap в файле ~/.gitconfig. Каждое отдельное значение можно установить вызовом команды git config, а можно указать вручную сразу в файле, но в итоге файл конфигурации должен выглядеть следующим образом:

  [imap]
    folder = "[Gmail]/Drafts"
    host = imaps://imap.gmail.com
    user = [email protected]
    pass = YX]8g76G_2^sFbd
    port = 993
    sslverify = false

Если ваш сервер IMAP не использует SSL, то последние две строки не обязательны, а значение host должно быть imap:// вместо imaps://. Как только все сделано, воспользуйтесь командой git imap-send для помещения ваших патчей в папку Drafts на указанном IMAP сервере:

cat *.patch |git imap-send
  Resolving imap.gmail.com... ok
  Connecting to [74.125.142.109]:993... ok
  Logging in...
  sending 2 messages
  100% (2/2) done

Теперь вы можете перейти в папку Drafts, изменить поле To, указав адрес почтовой рассылки, при необходимости заполнить поле СС, указав адрес сопровождающего или ответственного, и отправить письмо.
Так же вы можете отправить свои патчи используя SMTP сервер. Как и в предыдущем случае, вы можете использовать набор команд git config или создать секцию sendemail в файле ~/.gitconfig:

   [sendemail]
    smtpencryption = tls
    smtpserver = smtp.gmail.com
    smtpuser = [email protected]
    smtpserverport = 587

Отправить патчи можно командой git send-email:

git send-email *.patch
  0001-add-limit-to-log-function.patch
  0002-increase-log-output-to-30-from-25.patch
  Who should the emails appear to be from? [Jessica Smith <[email protected]>]
  Emails will be sent from: Jessica Smith <[email protected]>
  Who should the emails be sent to? [email protected]
  Message-ID to be used as In-Reply-To for the first email? y

Помощь по конфигурации, дополнительные советы и рекомендации, а так же тестовое окружение для отправки патчей по email доступны здесь git-send-email.io


Сопровождение проекта

Сопровождение может включать в себя принятие и применение патчей, сгенерированных с помощью format-patch и отправленных вам по почте, или интеграцию изменений в ветках удалённых репозиториев.


Работа с тематическими ветками

Перед интеграцией новых изменений желательно проверить их в тематической ветке — временной ветке, специально созданной для проверки работоспособности новых изменений. Таким образом, можно применять патчи по одному и пропускать неработающие, пока не найдётся время к ним вернуться.

Если вы создадите ветку с коротким и понятным названием, основанным на тематике изменений, например, ruby_client или что-то похожее, то без труда можно будет вернуться к ней, если пришлось на какое-то время отказаться от работы с ней. Сопровождающему Git проекта свойственно использовать пространство имен для веток, например, sc/ruby_client, где sc — это сокращение от имени того, кто проделал работу. Как известно, ветки можно создавать на основании базовой ветки, например:

git branch sc/ruby_client master

Если вы хотите сразу переключиться на создаваемую ветку, то используйте опцию checkout -b:

git checkout -b sc/ruby_client master

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


Применение патчей, полученных по почте

Если вы получили патч по почте и его нужно интегрировать в проект, то следует проанализировать его, применив сначала в тематической ветке. Существует два варианта применения полученного по почте патча: git apply или git am.


Применение патча командой apply

Если полученный по почте патч был создан командой git diff или Unix командой diff (что не рекомендуется делать), то применить его можно командой git apply. Предположим, патч сохранен здесь /tmp/patch-ruby-client.patch, тогда применить его можно вот так:

git apply /tmp/patch-ruby-client.patch

Это действие модифицирует файлы в вашем рабочем каталоге. Выполнение команды практически эквивалентно выполнению команды patch -p1, однако, является более параноидальным и принимает меньше неточных совпадений, чем patch. При этом обрабатываются добавления, удаления и переименования файлов, указанные в формате git diff, тогда как patch этого не делает.

Наконец, git apply использует модель «применить всё или отменить всё», где изменения либо применяются полностью, либо не применяются вообще, тогда как patch может частично применить патч файлы, приведя ваш рабочий каталог в непонятное состояние. В целом, git apply более консервативен, чем patch. После выполнения команды новый коммит не создаётся и его нужно делать вручную.

Командой git apply можно проверить корректность применения патча до его фактического применения, используя git apply --check:

git apply --check 0001-see-if-this-helps-the-gem.patch
  error: patch failed: ticgit.gemspec:1
  error: ticgit.gemspec: patch does not apply

Если ошибок не выведено, то патч может быть применён без проблем. Так же, в случае ошибки эта команда возвращает отличное от 0 значение, что позволяет использовать её в скриптах.


Применение патча командой am

Если участник проекта пользователь Git и умеет пользоваться командой format-patch для генерации патчей, то вам будет легче, так как в патч включается информация об авторе и сообщение коммита. Если возможно, требуйте от ваших участников использовать команду format-patch вместо diff для генерации патчей. Вам останется использовать git apply только для устаревших патчей и подобного им.

Для применения патча, созданного с помощью format-patch, используйте git am (команда названа am потому что применяет «apply» набор патчей в формате «mailbox»). С технической точки зрения она просто читает mbox-файл, в котором в виде обычного текста хранится одно или несколько электронных писем. Этот файл имеет следующий вид:

   From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
  From: Jessica Smith <[email protected]>
  Date: Sun, 6 Apr 2008 10:17:23 -0700
  Subject: [PATCH 1/2] Add limit to log function
  Limit log functionality to the first 20

Это начало вывода команды format-patch, которая рассматривалась в предыдущем разделе; это так же представляет собой валидный формат mbox. Если кто-то отправил патч, корректно сформированный командой git send-email, и вы сохранили его в формате mbox, то можно указать передать этот файл в качестве аргумента команде git am, которая начнёт применять все найденные в файле патчи. Если вы используете почтовый клиент, который умеет сохранять несколько писем в формате mbox, то можно сохранить сразу серию патчей в один файл, а затем применить их за раз, используя git am.

Так или иначе, если кто-нибудь загрузит созданный с помощью format-patch патч файл в систему управления задачами, то вы сможете сохранить его себе и применить локально с помощью git am:

git am 0001-limit-log-function.patch
  Applying: Add limit to log function

Извлечение удалённых веток

Если участник проекта создал свой Git репозиторий, отправил в него свои изменения, а затем прислал вам ссылку и название ветки, куда были отправлены изменения, то вы можете добавить этот репозиторий как удалённый и провести слияние локально.

К примеру, Джессика отправила вам письмо, в котором сказано, у неё есть новый функционал в ветке ruby-client её репозитория. Добавив удалённый репозиторий и получив изменения из этой ветки, вы можете протестировать изменения извлекая их локально:

git remote add jessica git://github.com/jessica/myproject.git
git fetch jessica
git checkout -b rubyclient jessica/ruby-client

Если она снова пришлёт вам письмо с указанием на новый функционал уже в другой ветке, то для его получения достаточно fetch и checkout, так как удалённый репозиторий уже подключён.

Если вы с кем-то не работаете постоянно, но всё равно хотите использовать удалённый репозиторий, то можно указать ссылку на него в команде git pull. Это приведёт к однократному выполнению, а ссылка на репозиторий сохранена не будет.

git pull https://github.com/onetimeguy/project
  From https://github.com/onetimeguy/project
   * branch            HEAD       -> FETCH_HEAD
  Merge made by the 'recursive' strategy.

Определение применяемых изменений

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

Обычно, полезно просмотреть все коммиты текущей ветки, которые ещё не включены в основную. Вы можете исключить коммиты, которые уже есть в вашей основной ветке добавив опцию --not перед её названием. Это аналогично указанию использовавшегося ранее формата master..contrib. Например, если участник проекта отправил вам два патча, а вы создали ветку с названием contrib и применили их, то можно выполнить следующую команду:

git log contrib --not master
  commit 5b6235bd297351589efc4d73316f0a68d484f118
  Author: Scott Chacon <[email protected]>
  Date:   Fri Oct 24 09:53:59 2008 -0700
      See if this helps the gem
  commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
  Author: Scott Chacon <[email protected]>
  Date:   Mon Oct 22 19:38:36 2008 -0700
      Update gemspec to hopefully work better

Для просмотра изменений, представленных в каждом коммите, можно использовать опцию -p команды git log, которая выведет разницу по каждому коммиту.

Для просмотра полной разницы того, что произойдёт если вы сольёте изменения в другую ветку, вам понадобится использовать возможно странный способ для получения корректных результатов:

git diff master

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

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

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

git merge-base contrib master
  36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
git diff 36c7db

или более кратко:

git diff $(git merge-base contrib master)

Однако это не удобно, поэтому Git предоставляет более короткий способ: синтаксис троеточия ... . При выполнении команды diff, следует поставить три точки после имени ветки для получения разницы между ней и текущей веткой, относительно общего родителя с другой веткой:

git diff master...contrib

Данная команда отобразит проделанную работу только из тематической ветки, относительно общего родителя с веткой master. Полезно запомнить указанный синтаксис.


Интеграция совместной работы

Когда все изменения в текущей тематической ветке готовы к интеграции с основной веткой, возникает вопрос как это сделать.


Схемы слияния

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

Если в репозитории присутствуют две ветки ruby_client и php_client с проделанной работой, как показано на рисунке История с несколькими тематическими ветками, и вы сначала сливаете ветку ruby_client, а затем php_client, то состояние вашего репозитория будет выглядеть как показано на рисунке Слияние тематической ветки.

image

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

Если у вас очень важный проект, то возможно вам стоит использовать двухступенчатый цикл слияния. При таком сценарии у вас имеются две долгоживущие ветки master и develop, где в master сливаются только очень стабильные изменения, а все новые доработки интегрируются в ветку develop. Обе ветки регулярно отправляются в публичный репозиторий. Каждый раз, когда новая тематическая ветка готова к слиянию (Перед слиянием тематической ветки), вы сливаете её в develop (После слияния тематической ветки); затем, когда вы выпускаете релиз, ветка master смещается на стабильное состояние ветки develop (После релиза проекта).

image

Таким образом, люди могут клонировать репозиторий вашего проекта и использовать ветку master для сборки последнего стабильного состояния и получения актуальных изменений или использовать ветку develop, которая содержит самые последние изменения. Вы также можете продолжить эту концепцию, имея интеграционную ветку integrate, в которой объединяется вся работа. После того, как кодовая база указанной ветки стабильна и пройдены все тесты, она сливается в ветку develop, а после того, как стабильность слитых изменений доказана, вы перемещаете состояние ветки master на стабильное.


Схема с большим количеством слияний

В проекте Git присутствуют четыре долгоживущие ветки: master, next, seen (ранее pu — предложенные обновления) для новой работы и maint для поддержки обратной совместимости.

Предложенные участниками проекта наработки накапливаются в тематических ветках основного репозитория по ранее описанному принципу. На этом этапе производится оценка содержимого тематических веток, чтобы определить, работают ли предложенные фрагменты так, как положено, или им требуется доработка. Если все в порядке, тематические ветки сливаются в ветку next, которая отправляется на сервер, чтобы у каждого была возможность опробовать результат интеграции.

image

Если содержимое тематических веток требует доработки, оно сливается в ветку seen. Когда выясняется, что предложенный код полностью стабилен, он сливается в ветку master. Затем ветки next и seen перестраиваются на основании master. Это означает, что master практически всегда двигается только вперед, next время от времени перебазируется, а seen перебазируется ещё чаще.

image

После того, как тематическая ветка окончательно слита в master, она удаляется из репозитория. Репозиторий также содержит ветку maint, которая ответвляется от последнего релиза для предоставления патчей, если требуется поддержка обратной совместимости.

Таким образом, после клонирования проекта у вас будет четыре ветки, дающие возможность перейти на разные стадии его разработки, в зависимости от того, на сколько передовым вы хотите быть или как вы собираетесь участвовать в проекте; вместе с этим, рабочий процесс структурирован, что помогает сопровождающему проекта проверять поступающий код. Рабочий процесс проекта Git специфицирован. Для полного понимания процесса обратитесь к Git Maintainer’s guide


Схема с перебазированием и отбором

Некоторые сопровождающие предпочитают перебазировать или выборочно применять cherry-pick изменения относительно ветки master вместо слияния, что позволяет поддерживать историю проекта в линейном виде. Когда проделанная работа из тематической ветки готова к интеграции, вы переходите на эту ветку и перебазируете её относительно ветки master (или develop и т. д.). Если конфликты отсутствуют, то вы можете просто сдвинуть состояние ветки master, что обеспечивает линейность истории проекта.

Другим способом переместить предлагаемые изменений из одной ветки в другую является их отбор коммитов cherry-pick. Отбор в Git похож на перебазирование для одного коммита. В таком случае формируется патч для выбранного коммита и применяется к текущей ветке. Это полезно, когда в тематической ветке присутствует несколько коммитов, а вы хотите взять только один из них, или в тематической ветке только один коммит и вы предпочитаете использовать отбор вместо перебазирования.

image

Для применения коммита e43a6 к ветке master выполните команду:

  git cherry-pick e43a6
  Finished one cherry-pick.
  [master]: created a0a41a9: "More friendly message when locking the index fails."
   3 files changed, 17 insertions(+), 3 deletions(-)

Это действие применит изменения, содержащиеся в коммите e43a6, но будет сформирован новый коммит с другим значением SHA-1.

image

Теперь тематическую ветку можно удалить, отбросив коммиты, которые вы не собираетесь включать в проект.


Возможность Rerere

Если вы часто производите перебазирование и слияние или поддерживаете долгоживущие тематические ветки, то в Git есть специальная возможность под названием rerere, призванная вам помочь.

Rerere означает reuse recorded resolution (повторно использовать сохранённое решение) — это способ сокращения количества операций ручного разрешения конфликтов. Когда эта опция включена, Git будет сохранять набор образов до и после успешного слияния, а также разрешать конфликты самостоятельно, если аналогичные конфликты уже были разрешены ранее.

Эта возможность реализована как команда и как параметр конфигурации. Параметр конфигурации называется rerere.enabled, который можно включить глобально следующим образом:

git config --global rerere.enabled true

После этого любое разрешение конфликта слияния будет записано на случай повторного использования.

Если нужно, вы можете обращаться к кэшу rerere напрямую, используя команду git rerere. Когда команда вызвана без параметров, Git проверяет базу данных и пытается найти решение для разрешения текущего конфликта слияния (точно так же как и при установленной настройке rerere.enabled в значение true).

Существует множество дополнительных команд для просмотра, что именно будет записано, удаления отдельных записей из кэша, а так же его полной очистки. Более детально rerere будет рассмотрено в разделе Rerere главы 7.


Помечайте свои релизы

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

  git tag -s v1.5 -m 'my signed 1.5 tag'
  You need a passphrase to unlock the secret key for
  user: "Scott Chacon <[email protected]>"
  1024-bit DSA key, ID F721C45A, created 2009-02-09

Если вы используете цифровую подпись при расстановке тегов, то возникает проблема распространения публичной части PGP ключа, использованного при создании подписи. Сопровождающий Git проекта может решить эту проблему добавив в репозиторий свой публичный ключ как бинарный объект и установив ссылающийся на него тег. Чтобы это сделать, выберите нужный ключ из списка доступных, который можно получить с помощью команды gpg --list-keys:

gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub uid sub
1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
               Scott Chacon <[email protected]>
2048g/45D02282 2009-02-09 [expires: 2010-02-09]

Затем экспортируйте выбранный ключ и поместите его непосредственно в базу данных Git при помощи команды git hash-object, которая создаст новый объект с содержимым ключа и вернёт SHA-1 этого объекта:

gpg -a --export F721C45A | git hash-object -w --stdin
  659ef797d181633c87ec71ac3f9ba29fe5775b92

Теперь, когда ваш публичный ключ находится в репозитории, можно поставить указывающий на него тег, используя полученное ранее значение SHA-1:

git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92

Выполнив команду git push --tags, maintainer-pgp-pub тег станет общедоступным. Теперь все, кто захочет проверить вашу подпись, могут импортировать ваш публичный ключ, предварительно получив его из репозитория:

git show maintainer-pgp-pub | gpg --import

После этого можно проверять цифровую подпись ваших тегов. Кроме этого, вы можете включить дополнительные инструкции по проверке вашей подписи в сообщение тега, которое будет отображаться каждый раз при вызове команды git show <tag>


Генерация номера сборки

Git не использует монотонно возрастающие идентификаторы для коммитов, поэтому если вы хотите получить читаемые имена коммитов, то воспользуйтесь командой git describe для нужного коммита. Git вернёт имя ближайшего тега, количество коммитов после него и частичное значение SHA-1 для указанного коммита (с префиксом в виде буквы g — означает Git):

git describe master
  v1.6.2-rc1-20-g8c5b85c

Таким образом, вы можете сделать снимок или собрать сборку и дать ей понятное для человека название. К слову, если вы клонируете репозиторий Git и соберете его из исходного кода, то вывод команды git --version будет примерно таким же. Если попытаться получить имя коммита, которому назначен тег, то результатом будет название самого тега.

По умолчанию, команда git describe поддерживает только аннотированные теги (созданные с использованием опций -a или -s); если вы хотите использовать легковесные (не аннотированные) метки, то укажите команде параметр --tags. Также это название можно использовать при выполнении команд git checkout и git show, но в будущем они могут перестать работать из-за сокращенного значения SHA-1.

К примеру, ядро Linux недавно перешло к использованию 10 символов в SHA-1 вместо 8 чтобы обеспечить уникальность каждого объекта, таким образом предыдущие результаты git describe стали недействительными.


Подготовка релиза

Время делать релиз сборки. Возможно, вы захотите сделать архив последнего состояния вашего кода для тех, кто не использует Git. Для создания архива выполните команду git archive:

git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
  $ ls *.tar.gz
  v1.6.2-rc1-20-g8c5b85c.tar.gz

Открывший этот tarball-архив пользователь получит последнее состояние кода проекта в каталоге project. Точно таким же способом можно создать zip-архив, просто добавив опцию --format=zip для команды git archive:

git archive master --prefix='project/' --format=zip > `git describe master`.zip

В итоге получим tarball- и zip-архивы с релизом проекта, которые можно загрузить на сайт или отправить по почте.


Краткая история (Shortlog)

Сейчас самое время оповестить людей из списка рассылки, которые хотят знать что происходит с вашим проектом. С помощью команды git shortlog можно быстро получить список изменений, внесённых в проект с момента последнего релиза или предыдущей рассылки. Она собирает все коммиты в заданном интервале; например, следующая команда выведет список коммитов с момента последнего релиза с названием v1.0.1:

git shortlog --no-merges master --not v1.0.1
  Chris Wanstrath (6):
        Add support for annotated tags to Grit::Tag
        Add packed-refs annotated tag support.
        Add Grit::Commit#to_patch
        Update version and History.txt
        Remove stray `puts`
        Make ls_tree ignore nils
  Tom Preston-Werner (4):
        fix dates in history
        dynamic version method
        Version bump to 1.0.2
        Regenerated gemspec for version 1.0.2


К оглавлению

Доступ по SSH

На данный момент вы можете подключаться к репозиториям Git используя протокол https:// авторизуясь при помощи только что созданного логина и пароля.

Если же вы хотите использовать SSH доступ, в таком случае вам понадобится добавить публичный SSH ключ. (Если же у вас нет публичного SSH ключа, вы можете его сгенерировать)


Внесение собственного вклада в проекты

Создание ответвлений fork

Если вы хотите вносить свой вклад в уже существующие проекты, в которых у нас нет прав на внесения изменений путём отправки push изменений, вы можете создать своё собственное ответвление fork проекта. Это означает, что GitHub создаст вашу собственную копию проекта, данная копия будет находиться в вашем пространстве имён и вы сможете легко делать изменения путём отправки push изменений.

Таким образом, проекты не обеспокоены тем, чтобы пользователи, которые хотели бы выступать в роли соавторов, имели право на внесение изменений путём их отправки push. Люди просто могут создавать свои собственные ветвления fork, вносить туда изменения, а затем отправлять свои внесённые изменения в оригинальный репозиторий проекта путём создания запроса на принятие изменений Pull Request, сами же запросы на принятие изменений Pull Request будут описаны далее.

Запрос на принятие изменений Pull Request откроет новую ветвь с обсуждением отправляемого кода, и автор оригинального проекта, а так же другие его участники, могут принимать участие в обсуждении предлагаемых изменений до тех пор, пока автор проекта не будет ими доволен, после чего автор проекта может добавить предлагаемые изменения в проект.

Для того, чтобы создать ответвление проекта, зайдите на страницу проекта и нажмите кнопку «Создать ответвление» Fork

Вы будете перенаправлены на собственную новую проектную страницу, содержащую вашу копию, в которой у вас есть права на запись.

Рабочий процесс с использованием GitHub

Рабочий процесс GitHub основан на тематических ветках

Вот как это обычно работает:

1. Создайте форк проекта.
2. Создайте тематическую ветку на основании ветки `master`.
3. Создайте один или несколько коммитов с изменениями, улучшающих проект.
4. Отправьте эту ветку в ваш проект на GitHub.
5. Откройте запрос на слияние на GitHub.
6. Обсуждайте его, вносите изменения, если нужно.
7. Владелец проекта принимает решение о принятии изменений, либо об их отклонении. 
8. Получите обновлённую ветку `master` и отправьте её в свой форк.

Создание запроса на слияние

Тони ищет, чего бы запустить на своём новеньком Arduino. Кажется, он нашёл классный пример на https://github.com/schacon/blink.

Для начала, нажмите кнопку Fork, как было сказано выше, чтобы заполучить собственную копию проекта. Мы зарегистрированы на GitHub под именем tonychacon, так что наша копия окажется по адресу https://github.com/tonychacon/blink, где мы сможем редактировать её. Мы клонируем его, создадим тематическую ветку, внесём необходимые изменения и, наконец, отправим их на GitHub.

$ git clone https://github.com/tonychacon/blink (1) Cloning into 'blink'...
$ cd blink
$ git checkout -b slow-blink (2) Switched to a new branch 'slow-blink'
$ sed -i '' 's/1000/3000/' blink.ino (macOS) (3) # If you're on a Linux system, do this instead: # $ sed -i 's/1000/3000/' blink.ino (3)
$ git diff --word-diff (4)
diff --git a/blink.ino b/blink.ino
index 15b9911..a6cc5a5 100644
--- a/blink.ino
+++ b/blink.ino
@@ -18,7 +18,7 @@ void setup() {
// the loop routine runs over and over again forever: void loop() {
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    [-delay(1000);-]{+delay(3000);+}               // wait for a second
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    [-delay(1000);-]{+delay(3000);+}               // wait for a second
}

$ git commit -a -m 'Change delay to 3 seconds' (5) [slow-blink 5ca509d] Change delay to 3 seconds
   1 file changed, 2 insertions(+), 2 deletions(-)
$ git push origin slow-blink (6)
Username for 'https://github.com': tonychacon
Password for 'https://[email protected]':
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0)
To https://github.com/tonychacon/blink
   * [new branch]      slow-blink -> slow-blink
  1. Клонируем нашу копию
  2. Создаём тематическую ветку
  3. Вносим свои изменения
  4. Проверяем изменения
  5. Фиксируем изменения в тематической ветку
  6. Отправляем новую ветку в нашу копию на GitHub

Теперь, если мы зайдём на страничку нашей копии на GitHub, мы увидим, что GitHub заметил наши изменения и предлагает открыть запрос на слияние с помощью большой зелёной кнопки. Также можно зайти на страницу Branches, по адресу https://github.com/<user>/<project>/branches, найти интересующую ветку и открыть запрос оттуда.

image

Мы увидим список коммитов в нашей тематической ветке, «опередивших» ветку master (в данном случае всего один коммит) и предпросмотр всех изменений, вносимых этими коммитами.

После создания запроса на слияние (путём нажатия кнопки Create pull request на этой странице) владелец форкнутого проекта получит уведомление о предложенных изменениях со ссылкой на страницу с информацией о запросе.


Обработка запроса на слияние

На этом этапе, владелец проекта может просмотреть предложенные изменения, принять, отклонить или прокомментировать их.

Владелец проекта может просмотреть суммарные изменения, вносимые запросом, и прокомментировать любую отдельно взятую строку.

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

GitHub так же проверяет может ли запрос на слияние быть применён без конфликтов и предоставляет кнопку для осуществления слияния на сервере. Эта кнопка отображается только если у вас есть права на запись в репозиторий и возможно простейшее слияние. По нажатию на неё GitHub произведёт non-fast-forward слияние, что значит даже если слияние может быть осуществлено перемоткой вперед, всё равно будет создан коммит слияния.


Продвинутые запросы на слияние

Запросы слияния как Патчи

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

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


Следование за исходным репозиторием

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

Если вы видите что-то вроде Запрос имеет конфликты слияния, то вам следует изменить свою ветку так, чтобы исключить конфликты и сопровождающий не делал лишнюю работу.

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

Большинство разработчиков на GitHub выбирают последний вариант по тем же причинам, что и мы в предыдущем разделе. Важна история и окончательное слияние, а перебазирование не принесёт вам ничего, кроме немного более чистой истории, при этом оно гораздо сложнее и может стать источником ошибок.

Если вы хотите сделать запрос на слияние применяемым, то следует добавить исходный репозиторий как новый удалённый, слить изменения из его основной ветки в вашу тематическую, если имеются исправить все проблемы и, наконец, отправить все изменения в ту ветку, на основании которой был открыт запрос на слияние.

Предположим, что в примере tonychacon, который мы использовали ранее, основной автор сделал изменения, которые конфликтуют с запросом на слияние. Рассмотрим это пошагово.

$ git remote add upstream https://github.com/schacon/blink (1)
 
$ git fetch upstream (2)
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done. Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0) From https://github.com/schacon/blink
   * [new branch]      master     -> upstream/master
    
$ git merge upstream/master (3)
Auto-merging blink.ino
CONFLICT (content): Merge conflict in blink.ino
Automatic merge failed; fix conflicts and then commit the result.

$ vim blink.ino (4)
$ git add blink.ino
$ git commit
[slow-blink 3c8d735] Merge remote-tracking branch 'upstream/master' \
      into slower-blink
      
$ git push origin slow-blink (5)
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done. Total 6 (delta 2), reused 0 (delta 0)
To https://github.com/tonychacon/blink
     ef4725c..3c8d735  slower-blink -> slow-blink
  1. Добавляем исходный репозиторий как удалённый с именем upstream.
  2. Получаем последние изменения из него.
  3. Сливаем основную ветку в нашу тематическую.
  4. Исправляем указанный конфликт.
  5. Отправляем изменения в ту же тематическую ветку.

Как только это будет сделано, запрос на слияние будет автоматически обновлён и перепроверен на возможность слияния.

Одна из замечательных особенностей Git - это то, что вы можете делать это постоянно. Если у вас очень длительный проект, вы можете легко сливать изменения из целевой ветки снова и снова и иметь дело только с конфликтами, возникшими с момента вашего последнего слияния, что делает процесс очень управляемым.

Если вы очень хотите перебазировать ветку, чтобы её почистить, то, конечно, вы можете это сделать, но настоятельно не рекомендуется переписывать ветку, к которой уже открыт запрос на слияние. Если другие люди уже стянули её и проделали много работы, то вы столкнётесь со всеми проблемами, описанными в разделе Опасности перемещения главы 3. Вместо этого, отправьте перебазированную ветку в новую на GiHub и откройте новый запрос на слияние, который указывает на предыдущий, затем закройте исходный.


Ссылки

Возможно, ваш следующий вопрос будет: «Как мне сослаться на предыдущий запрос слияния?»

Оказывается, существует много способов ссылаться на другие вещи практически везде, где у вас есть права записи на GitHub.

Давайте начнём с перекрёстных ссылок для запросов слияния или проблем. Всем запросам слияния и проблемам присваиваются уникальные номера в пределах проекта. Например, у вас не может быть запроса на слияние с номером #3 и проблемы с номером #3. Если вы хотите сослаться на любой запрос слияния или проблему из другого места, просто добавьте #<num> в комментарий или описание.

Так же можно указывать более конкретно, если проблема или запрос слияния находятся где-то ещё; пишите username#<num> если ссылаетесь на проблему или запрос слияния, находящиеся в ответвлённом репозитории, или username/repo#<num> если ссылаетесь на другой репозиторий. Ссылки будут подсвечиваться.

Кроме идентификационных номеров, можно ссылаться на конкретный коммит используя SHA-1. Следует указывать полный 40 символьный хеш SHA-1, но если GitHub увидит его в комментарии, то автоматически подставит ссылку на коммит. Как было сказано выше, вы можете ссылаться на коммиты как в других, так и в ответвлённых репозиториях точно так же, как делали это с Проблемами.


GitHub-версия разметки Markdown

Ссылки на другие Проблемы — это лишь часть интереснейших вещей, которые вы можете делать в текстовых полях на GitHub. Для проблемы или запроса слияния в полях описания, комментария, комментария кода и других вы можете использовать так называемую GitHub-версию разметки Markdown.

Списки задач

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

Список задач можно добавить следующим образом:

  - [X] Write the code
  - [ ] Write all the tests
  - [ ] Document the code

Отрывки кода

В комментарии так же можно вставлять отрывки кода.

Для добавления отрывка кода следует обрамить его обратными кавычками.

for(int i=0 ; i < 5 ; i++)
{
   System.out.println("i is : " + i);
} 

Если вы укажете название языка, как показано на примере, GitHub попробует применить к нему подсветку синтаксиса.


Цитирование

Если вы отвечаете только на часть большого комментария, то можно цитировать только выбранную часть, предваряя её символом >. Это настолько часто используется, что даже существует комбинация клавиш для этого. Если в комментарии выделить текст, на который вы собираетесь ответить, и нажать клавишу r, то выделенный текст будет включён как цитата в ваш комментарий.

  > Whether 'tis Nobler in the mind to suffer
  > The Slings and Arrows of outrageous Fortune,
  How big are these slings and in particular, these arrows?

Картинки

Технически, картинки не относятся к разметке GitHub, но их использование очень полезно. В дополнение к ссылкам на картинки в комментариях, GitHub позволяет встраивать картинки в комментарии. При добавлении картинки в комментарий она автоматически загружается на облако git и будет видна и доступна по отдельной ссылке.


Поддержание GitHub репозитория в актуальном состоянии

После создания форка, ваш репозиторий будет существовать независимо от оригинального репозитория. В частности, при появлении в оригинальном репозитории новых коммитов GitHub информирует вас следующим сообщением: This branch is 5 commits behind progit:master.

При этом GitHub никогда не обновляет ваш репозиторий — это вы должны делать сами. К счастью, это очень просто сделать. Первый способ не требует конфигурации. Например, если вы сделали форк репозитория https://github.com/progit/progit2.git, то актуализировать ветку master можно следующим образом:

$ git checkout master (1)
$ git pull https://github.com/progit/progit2.git (2) 
$ git push origin master (3)
  1. Если вы находитесь на другой ветке — перейти на ветку master.
  2. Получить изменения из репозитория https://github.com/progit/progit2.git и слить их с веткой master.
  3. Отправить локальную ветку master в ваш форк origin.

Каждый раз писать URL репозитория для получения изменений достаточно утомительно. Этот процесс можно автоматизировать слегка изменив настройки:

$ git remote add progit https://github.com/progit/progit2.git (1) 
$ git fetch progit (2)
$ git branch --set-upstream-to=progit/master master (3)
$ git config --local remote.pushDefault origin 4
  1. Добавить исходный репозиторий как удалённый и назвать его progit.
  2. Получить ветки репозитория progit, в частности ветку master.
  3. Настроить локальную ветку master на получение изменений из репозитория progit.
  4. Установить origin как репозиторий по умолчанию для отправки.

После этого, процесс обновления становится гораздо проще:

$ git checkout master 1 $ git pull 2
$ git push 3
  1. Если вы находитесь на другой ветке — перейти на ветку master.
  2. Получить изменения из репозитория progit и слить их с веткой master.
  3. Отправить локальную ветку master в ваш форк origin.

Данный подход не лишён недостатков. Git будет молча выполнять указанные действия и не предупредит вас в случае, когда вы добавили коммит в master, получили изменения из progit и отправили всё вместе в origin — все эти операции абсолютно корректны. Поэтому вам стоит исключить прямое добавление коммитов в ветку master, поскольку эта ветка фактически принадлежит другому репозиторию.


Сопровождение проекта

Все проекты на GitHub доступны как по HTTP https://github.com/<user>/<project_name>, так по SSH [email protected]:<user>/<project_name>

Обычно, для общедоступного проекта предпочтительнее использовать HTTPS ссылки, так как это не требует наличия GitHub аккаунта для клонирования репозитория. При этом, для использования SSH ссылки у пользователя должен быть GitHub аккаунт и его SSH ключ должен быть добавлен в ваш проект. Так же HTTPS ссылка полностью совпадает с URL адресом, который пользователи могут вставить в браузер для просмотра вашего репозитория.


Добавление участников

Если вы работаете с другими людьми, которым вы хотите предоставить доступ для отправки коммитов, то вам следует добавить их как «участников».

Это предоставит им push доступ; это означает, что они будут иметь права доступа как на чтение, так и на запись в проект и Git репозиторий. Перейдите по ссылке Settings

Затем выберите Collaborators в меню слева. Напишите имя пользователя в поле для ввода и нажмите кнопку Add collaborator.


Управление запросами на слияние

Сейчас у вас есть проект с некоторым кодом и, возможно, несколько участников с push доступом, давайте рассмотрим ситуацию, когда вы получаете запрос на слияние.

Запрос на слияние может быть как из ветки вашего репозитория, так и из ветки форка вашего проекта. Отличаются они тем, что вы не можете отправлять изменения в ветки ответвлённого проекта, а его владельцы не могут отправлять в ваши, при этом для внутренних запросов на слияние характерно наличие доступа к ветке у обоих пользователей. Для последующих примеров предположим, что вы tonychacon и создали новый проект для Arduino с названием fade.

Email уведомления Кто-то вносит изменения в ваш код и отправляет вам запрос на слияние. Вы должны получить письмо с уведомлением о новом запросе слияния, которое выглядит как на Email уведомление о новом запросе слияния.

Если вы видите строку с текстом git pull <url> patch-1, то это самый простой способ слить удалённую ветку без добавления удалённого репозитория. Это кратко описывалось в Извлечение удалённых веток. Если хотите, то можно сначала переключиться в тематическую ветку и только потом выполнить эту команду для изменений запроса слияния.


Взаимодействие по запросу слияния

Когда вы готовы слить код, вы можете стянуть его себе и слить локально, слить используя команду git pull <url> <branch>, которую мы видели ранее, или добавив ответвлённый репозиторий как удалённый получить и слить изменения.

Если слияние тривиально, то можно просто нажать кнопку Merge на сайте GitHub. Это всегда приводит с созданию коммита слияния, даже если доступно слияние перемоткой вперёд. Это значит, что в любом случае создаётся коммит слияния, как только вы нажимаете кнопку Merge.

Если вы решаете не сливать запрос, то вы можете просто закрыть запрос на слияние, а открывший его участник будет уведомлен.


Ссылки на запрос слияния

Если у вас много запросов слияния и вы не хотите добавлять пачку удалённых репозиториев или постоянно делать однократный pull, то у GitHub есть хитрый трюк, позволяющий это делать. Этот трюк очень сложный, но полезный и мы рассмотрим его немного позже в Спецификации ссылок.

Фактически, GitHub представляет ветки запросов слияния как псевдо ветки на сервере. По умолчанию, они не копируются при клонировании, а существуют в замаскированном виде и вы можете легко получить доступ к ним.

В качестве примера мы используем низкоуровневую команду ls-remote (часто упоминается как plumbing команда). Обычно, эта команда не используется в повседневных Git операциях, но сейчас поможет нам увидеть какие ссылки присутствуют на сервере.

Если выполнить её относительно использованного ранее репозитория blink, мы получим список всех веток, тегов и прочих ссылок в репозитории.

 $ git ls-remote https://github.com/schacon/blink
10d539600d86723087810ec636870a504f4fee4d HEAD
10d539600d86723087810ec636870a504f4fee4d refs/heads/master
6a83107c62950be9453aac297bb0193fd743cd6e refs/pull/1/head
afe83c2d1a70674c9505cc1d8b7d380d5e076ed3 refs/pull/1/merge
3c8d735ee16296c242be7a9742ebfbc2665adec1 refs/pull/2/head

Аналогично, если вы, находясь в своём репозитории, выполните команду git ls-remote origin или укажете любой другой удалённый репозиторий, то результат будет схожим.

Если репозиторий находится на GitHub и существуют открытые запросы слияния, то эти ссылки будут отображены с префиксами refs/pull/. По сути это ветки, но так как они находятся не в refs/heads/, то они не копируются при клонировании или получении изменений с сервера — процесс получения изменений игнорирует их по умолчанию.

Для каждого запроса слияния существует две ссылки, одна из которых записана в /head и указывает на последний коммит в ветке запроса на слияние. Таким образом, если кто-то открывает запрос на слияние в наш репозиторий из своей ветки bug-fix, которая указывает на коммит a5a775, то в нашем репозитории не будет ветки bug-fix (так как она находится в форке), при этом у нас появится pull/<pr#>/head, которая указывает на a5a775. Это означает, что мы можем стянуть все ветки запросов слияния одной командой не добавляя набор удалённых репозиториев. Теперь можно получить ссылки напрямую.

$ git fetch origin refs/pull/958/head
  From https://github.com/libgit2/libgit2
  * branch            refs/pull/958/head -> FETCH_HEAD

Эта команда указывает Git: «Подключись к origin репозиторию и скачай ссылку refs/pull/958/head». Git с радостью слушается и выкачивает всё необходимое для построения указанной ссылки, а так же устанавливает указатель на коммит в .git/FETCH_HEAD.

Далее, вы можете слить изменения в нужную ветку при помощи команды git merge FETCH_HEAD, однако сообщение коммита слияния будет выглядеть немного странно. Так же это становится утомительным, если вы просматриваете много запросов на слияние.

Существует способ получать все запросы слияния и поддерживать их в актуальном состоянии при подключении к удалённому репозиторию. Откройте файл .git/config в текстовом редакторе и обратите внимание на секцию удалённого репозитория origin. Она должна выглядеть как-то так:

   [remote "origin"]
      url = https://github.com/libgit2/libgit2
      fetch = +refs/heads/*:refs/remotes/origin/*

Строка, начинающаяся с fetch =, является спецификацией ссылок refspec. Это способ сопоставить названия в удалённом репозитории и названиями в локальном каталоге .git. Конкретно эта строка говорит Git: «все объекты удалённого репозитория из refs/heads должны сохраняться локально в refs/remotes/origin. Вы можете изменить это поведение добавив ещё одну строку спецификации:

  [remote "origin"]
      url = https://github.com/libgit2/libgit2.git
      fetch = +refs/heads/*:refs/remotes/origin/*
      fetch = +refs/pull/*/head:refs/remotes/origin/pr/*

Последняя строка говорит Git: «Все ссылки, похожие на refs/pull/123/head, должны быть сохранены локально как refs/remotes/origin/pr/123». Теперь, если сохранить файл и выполнить команду git fetch, вы получите:

 $ git fetch #...
 * [new ref] refs/pull/1/head -> origin/pr/1
 * [new ref] refs/pull/2/head -> origin/pr/2
 * [new ref] refs/pull/4/head -> origin/pr/4
#...

Все запросы слияния из удалённого репозитория представлены в локальном репозитории как ветки слежения; они только для чтения и обновляются каждый раз при выполнении git fetch. Таким образом, локальное тестирование кода запроса слияния становится очень простым:

  $ git checkout pr/2
  Checking out files: 100% (3769/3769), done.
  Branch pr/2 set up to track remote branch pr/2 from origin.
  Switched to a new branch 'pr/2'

Запросы слияния на запросы слияния

Вы можете открыть запрос слияния не только в ветку master, запросы слияния могут указывать на любую ветку любого репозитория в сети. По сути, вы можете даже открыть запрос слияния, указывающий на другой запрос слияния.

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

При открытии запроса на слияние вверху страницы вы увидите меню для выбора целевой и исходной веток. Если нажать кнопку Edit справа, то станет доступным выбор не только исходной ветки, а ещё и форка.

Здесь можно указать вашу новую ветку для слияния с другим запросом слияния или другим форком проекта.

Упоминания и уведомления

В любом комментарии можно написать символ @, что автоматически вызовет список автодополнения с именами пользователей, которые включены в проект или просто участвуют в нём.

Так же можно упомянуть пользователя, не указанного в выпадающем списке, но с помощью автодополнения это можно сделать быстрее. Как только вы оставите комментарий с упоминанием пользователя, ему будет отправлено уведомление.


README

Если GitHub увидит такой файл в вашем исходном коде, то отобразит его на заглавной странице проекта.

Большинство команд используют его для поддержания актуальной информации о проекте для новичков. Как правило, он включает следующее: • Для чего предназначен проект • Инструкции по конфигурации и установке • Примеры использования • Используемую лицензию

• Правила участия в проекте В этот файл можно встраивать изображения или ссылки для простоты восприятия информации.


CONTRIBUTING

Следующий файл - это CONTRIBUTING. Если в вашем репозитории будет файл CONTRIBUTING с любым расширением, то GitHub будет показывать ссылку на него при создании любого запроса на слияние.

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


Управление проектом

Изменение основной ветки

Если вы используете в качестве основной другую ветку, отличную от master, и хотите, чтобы пользователи открывали запросы на слияние к ней, то это можно изменить в настройках репозитория на закладке Options.

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

Передача проекта

Если вы хотите передать проект другому пользователю или организации на GitHub, то это можно сделать нажатием кнопки Transfer ownership в настройках репозитория на закладке Options.

Эта опция полезна, когда вы хотите отказаться от проекта, а кто-то другой хочет им заниматься, или когда ваш проект растёт и вы хотите передать его какой-нибудь организации.

Это действие приведёт не только к передаче репозитория со всеми его подписчиками и звёздами, но и добавит перенаправление с вашего URL на новый. Кроме этого, изменятся ссылки для клонирования и получения изменений из Git, а не только для веб запросов.


Управление организацией

Создать новую организацию очень легко; просто нажмите на иконку + в правом верхнем углу страницы GitHub и выберите пункт New organization из меню.


Команды

Организации связаны с отдельными людьми посредством команд, которые представляют собой сгруппированные аккаунты индивидуальных пользователей, репозиториев внутри организации и того, какой доступ эти люди имеют в этих репозиториях.

Например, у вашей компании есть три репозитория: frontend, backend и deployscripts. Вы бы хотели, чтобы ваши разработчики HTML/CSS/JavaScript имели доступ к frontend и возможно к backend, а ваши администраторы имели доступ к backend и deployscripts. С помощью команд это легко реализовать не настраивая доступ к каждому репозиторию для каждого участника.

Для управления командами нужно перейти на закладку Teams справа вверху на странице Страница организации. Это приведёт вас на страницу где можно добавлять пользователей в команду, добавлять команде репозитории или управлять настройками и правами доступа.

Каждая команда может иметь только следующие уровни доступа к репозиториям: «только чтение», «чтение/запись» или «администратор». Уровень доступа может быть изменен нажатием кнопки Settings на странице Страница команды.

Упоминания команд @mentions, такие как @acmecorp/frontend, работают точно так же как и упоминания отдельных пользователей, за исключением того, что уведомляются все члены команды.


Журнал аудита

Организации так же предоставляют владельцам информацию о всех происходящих событиях внутри них. Перейдя на закладку Audit Log вы можете увидеть произошедшие события на уровне организации, кто участвовал в них.


К оглавлению

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

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

Одиночные ревизии Конечно, вы можете ссылаться на коммит по его SHA-1 хешу


Сокращённый SHA-1

Git достаточно умен, чтобы понять какой коммит имеется ввиду по нескольким первым символам его хеша, если указанная часть SHA-1 имеет в длину по крайней мере четыре символа и однозначна — то есть в текущем репозитории существует только один объект с таким частичным SHA-1. Например, предположим, чтобы найти некоторый коммит, вы выполнили команду git log и нашли коммит, в которой добавили определённую функциональность:

   $ git log
  commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
  Author: Scott Chacon <[email protected]>
  Date:   Thu Dec 11 14:58:32 2008 -0800
      Add some blame and merge stuff

Предположим, что в нашем примере это коммит 1c002dd..... Если вы хотите выполнить для него git show, то следующие команды эквиваленты (предполагается, что сокращения однозначны):

$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d

Git может вычислить уникальные сокращения для ваших значений SHA-1. Если вы передадите опцию --abbrev-commit команде git log, в выводе будут использоваться сокращённые значения, сохраняющие уникальность; по умолчанию используется семь символов, но для сохранения уникальности SHA-1 могут использоваться более длинные значения.

$ git log --abbrev-commit --pretty=oneline
  ca82a6d Change the version number
  085bb3b Remove unnecessary test code
  a11bef0 Initial commit

Диапазоны коммитов

Теперь вы умеете указывать отдельные коммиты, давайте посмотрим как указывать диапазоны коммитов. Это в частности полезно для управления вашими ветками — если у вас есть множество веток, вы можете использовать указание диапазонов коммитов для ответа на вопрос «Что было сделано в этой ветке, что я ещё не слил в основную ветку?»


Две точки

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

Вы хотите посмотреть что находится в вашей экспериментальной ветке, которая ещё не была слита в основную. Вы можете попросить Git отобразить в логе только такие коммиты, используя запись master..experiment — она означает «все коммиты, которые доступны из ветки experiment, но не доступны из ветки master». Для краткости и наглядности в этих примерах вместо настоящего вывода лога мы будем использовать для коммитов их буквенные обозначения из диаграммы, располагая их в должном порядке:

  $ git log master..experiment
  D
  C

С другой стороны, если вы хотите наоборот увидеть все коммиты ветки master, которых нет в ветке experiment, вы можете поменять имена веток в команде. При использовании записи experiment..master будут отображены все коммиты ветки master, недоступные из ветки experiment:

  $ git log experiment..master
  F
  E

Это полезно если вы хотите сохранить ветку experiment в актуальном состоянии и просмотреть, какие изменения нужно в нее слить. Другое частое использование такого синтаксиса — просмотр того, что будет отправлено в удалённый репозиторий.

  $ git log origin/master..HEAD

Такая команда покажет вам все коммиты вашей текущей ветки, которые отсутствуют в ветке master удалённого репозитория origin. Если вы выполните git push, находясь на ветке, отслеживающей origin/master, то коммиты, отображённые командой git log origin/master..HEAD, будут теми коммитами, которые отправятся на сервер.


Множественная выборка

Запись с двумя точками полезна как сокращение, но, возможно, вы захотите использовать более двух веток для указания нужной ревизии, например, для того, чтобы узнать какие коммиты присутствуют в любой из нескольких веток, но отсутствуют в ветке, в которой вы сейчас находитесь. Git позволяет сделать это, используя символ ^ или опцию --not, перед любой ссылкой, доступные коммиты из которой вы не хотите видеть. Таким образом, следующие три команды эквивалентны:

  $ git log refA..refB
  $ git log ^refA refB
  $ git log refB --not refA

Этот синтаксис удобен, так как позволяет указывать в запросе более двух ссылок, чего не позволяет сделать синтаксис с двумя точками. Например, если вы хотите увидеть все коммиты, доступные из refA и refB, но не доступные из refC, вы можете использовать одну из следующих команд:

  $ git log refA refB ^refC
  $ git log refA refB --not refC

Три точки

Последний основной способ выбора ревизий — это синтаксис с тремя точками, который обозначает все коммиты, доступные хотя бы из одной ссылки, но не из обеих сразу. Вспомните пример истории коммитов в Пример истории для выбора диапазонов коммитов. Если вы хотите узнать какие коммиты есть либо в ветке master, либо в experiment, но не в обеих сразу, вы можете выполнить:

  $ git log master...experiment
  F
  E
  D
  C

Эта команда снова выводит обычный журнал коммитов, но в нем содержится информация только об этих четырёх коммитах, традиционно отсортированная по дате коммитов.

В таких случаях с командой log часто используют опцию --left-right, которая отображает сторону диапазона, с которой был сделан каждый из коммитов. Это делает данную информацию более полезной:

$ git log --left-right master...experiment 
  <F
  <E
  >D
  >C

Припрятывание и очистка

Часто пока вы работаете над одной частью вашего проекта и всё находится в беспорядке, у вас возникает желание сменить ветку и поработать над чем-то ещё. Сложность при этом заключается в том, что вы не хотите фиксировать наполовину сделанную работу только для того, чтобы иметь возможность вернуться к ней позже. Справиться с ней помогает команда git stash.

Операция stash берет изменённое состояние вашего рабочего каталога, то есть изменённые отслеживаемые файлы и проиндексированные изменения, и сохраняет их в хранилище незавершённых изменений, которые вы можете в любое время применить обратно.


Переход на git stash push

В конце октября 2017 года в списке рассылки Git проходило обширное обсуждение, по итогам которого команда git stash save признана устаревшей в пользу существующей альтернативы git stash push. Основная причина этого заключается в том, что в git stash push есть возможность сохранить выбранные спецификации пути, что не поддерживает git stash save.

Команда git stash save не исчезнет в ближайшее время, поэтому не беспокойтесь о её внезапной пропаже. Но вы можете начать переход на push для использования новой функциональности.


Припрятывание ваших наработок

Для примера, предположим, что вы перешли в свой проект, начали работать над несколькими файлами и, возможно, добавили в индекс изменения одного из них. Если вы выполните git status, то увидите ваше изменённое состояние:

$ git status
  Changes to be committed:
    (use "git reset HEAD <file>..." to unstage)
      modified:   index.html
  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:   lib/simplegit.rb

Теперь вы хотите сменить ветку, но пока не хотите фиксировать ваши текущие наработки; поэтому вы припрячете эти изменения. Для того, чтобы припрятать изменение в выделенное для этого специальное хранилище, выполните git stash или git stash push:

  $ git stash
  Saved working directory and index state \
    "WIP on master: 049d078 Create index file"
  HEAD is now at 049d078 Create index file
  (To restore them type "git stash apply")

Теперь вы можете увидеть, что рабочая копия не содержит изменений:

  $ git status
  # On branch master
  nothing to commit, working directory clean

В данный момент вы можете легко переключать ветки и работать в любой; ваши изменения сохранены. Чтобы посмотреть список припрятанных изменений, вы можете использовать git stash list:

  $ git stash list
  stash@{0}: WIP on master: 049d078 Create index file
  stash@{1}: WIP on master: c264051 Revert "Add file_size"
  stash@{2}: WIP on master: 21d80a5 Add number to log

В данном примере, предварительно были припрятаны два изменения, поэтому теперь вам доступны три различных отложенных наработки. Вы можете применить только что припрятанные изменения, используя команду, указанную в выводе исходной команды: git stash apply.

Если вы хотите применить одно из предыдущих припрятанных изменений, вы можете сделать это, используя его имя, вот так: git stash apply stash@{2}. Если вы не укажете имя, то Git попытается восстановить самое последнее припрятанное изменение:

$ git stash apply
  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
      modified:   lib/simplegit.rb
  no changes added to commit (use "git add" and/or "git commit -a")

Как видите, Git восстановил в файлах изменения, которые вы отменили ранее, когда прятали свои наработки. В данном случае при применении отложенных наработок ваш рабочий каталог был без изменений, а вы пытались применить их в той же ветке, в которой вы их и сохранили; но отсутствие изменений в рабочем каталоге и применение их в той же ветке не являются необходимыми условиями для успешного восстановления припрятанных наработок.

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

Спрятанные изменения будут применены к вашим файлам, но файлы, которые вы ранее добавляли в индекс, не будут добавлены туда снова. Для того, чтобы это было сделано, вы должны запустить git stash apply с опцией --index, при которой команда попытается восстановить изменения в индексе. Если вы выполните команду таким образом, то полностью восстановите ваше исходное состояние:

$ git stash apply --index
  On branch master
  Changes to be committed:
    (use "git reset HEAD <file>..." to unstage)
      modified:   index.html
  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:   lib/simplegit.rb

Команда apply только пытается восстановить припрятанные наработки — при этом они останутся в хранилище. Для того, чтобы удалить их, вы можете выполнить git stash drop, указав имя удаляемых изменений:

$ git stash list
  stash@{0}: WIP on master: 049d078 Create index file
  stash@{1}: WIP on master: c264051 Revert "Add file_size"
  stash@{2}: WIP on master: 21d80a5 Add number to log
  $ git stash drop stash@{0}
  Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

Вы также можете выполнить git stash pop, чтобы применить припрятанные изменения и тут же удалить их из хранилища.


Необычное припрятывание

У припрятанных изменений есть несколько дополнительных вариантов использования, которые также могут быть полезны. Первый — это использование довольно популярной опции --keep-index с командой git stash. Она просит Git не только припрятать то, что вы уже добавили в индекс, но одновременно оставить это в индексе.

$ git status -s
  M  index.html
  M lib/simplegit.rb
$ git stash --keep-index
  Saved working directory and index state WIP on master: 1b65b17 added the index file
  HEAD is now at 1b65b17 added the index file
$ git status -s
  M  index.html

Другой распространённый вариант, который вы, возможно, захотите использовать — это припрятать помимо отслеживаемых файлов также и неотслеживаемые. По умолчанию git stash будет сохранять только изменённые и проиндексированные отслеживаемые файлы. Если вы укажете опцию --include-untracked или -u, Git также припрячет все неотслеживаемые файлы, которые вы создали. Однако включение этой опции по-прежнему не будет прятать файлы с явным игнорированием; чтобы дополнительно припрятать игнорируемые файлы, используйте --all (или просто -a).

$ git status -s
  M  index.html
  M lib/simplegit.rb
  ?? new-file.txt
$ git stash -u
  Saved working directory and index state WIP on master: 1b65b17 added the index file
  HEAD is now at 1b65b17 added the index file
$ git status -s

Создание ветки из припрятанных изменений

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

Если вам нужен более простой способ снова протестировать припрятанные изменения, вы можете выполнить команду git stash branch, которая создаст для вас новую ветку, перейдёт на коммит, на котором вы были, когда прятали свои наработки, применит на нём эти наработки и затем, если они применились успешно, удалит эти припрятанные изменения:

$ git stash branch testchanges
  M   index.html
  M   lib/simplegit.rb
  Switched to a new branch 'testchanges'
  On branch testchanges
  Changes to be committed:
    (use "git reset HEAD <file>..." to unstage)
      modified:   index.html
  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:   lib/simplegit.rb
  Dropped refs/stash@{0} (29d385a81d163dfd45a452a2ce816487a6b8b014)

Это удобное сокращение для того, чтобы легко восстановить припрятанные изменения и поработать над ними в новой ветке.


Очистка рабочего каталога

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

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

Даже если вы передумаете, очень часто нельзя восстановить содержимое таких файлов. Более безопасным вариантом является использование команды git stash --all для удаления всего, но с сохранением этого в виде припрятанных изменений.

Предположим, вы хотите удалить мусор и очистить ваш рабочий каталог; вы можете сделать это с помощью git clean. Для удаления всех неотслеживаемых файлов в вашем рабочем каталоге, вы можете выполнить команду git clean -f -d, которая удалит все файлы и также все каталоги, которые в результате станут пустыми.

Параметр -f (сокращение от слова force — заставить) означает принудительное удаление, подчеркивая, что вы действительно хотите это сделать, и требуется, если переменная конфигурации Git clean.requireForce явным образом не установлена в false.

Если вы хотите только посмотреть, что будет сделано, вы можете запустить команду с опцией -n, которая означает имитируй работу команды и скажи мне, что ты будешь удалять.

$ git clean -d -n
  Would remove test.o
  Would remove tmp/

По умолчанию команда git clean будет удалять только неотслеживаемые файлы, которые не добавлены в список игнорируемых.

Если вы не знаете, что сделает при запуске команда git clean, всегда сначала выполняйте её с опцией -n, чтобы проверить дважды, перед заменой -n на -f и выполнением настоящей очистки. Другой способ, который позволяет вам более тщательно контролировать сам процесс — это выполнение команды с опцией -i (в «интерактивном» режиме). Ниже выполнена команда очистки в интерактивном режиме.

$ git clean -x -i
Would remove the following items:
  build.TMP  test.o
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers
4: ask each 5: quit 6: help
What now>

Поиск

Вне зависимости от размера кодовой базы, часто возникает необходимость поиска места вызова/определения функции или получения истории изменения метода. Git предоставляет несколько полезных утилит, с помощью которых легко и просто осуществлять поиск по коду и коммитам. Мы обсудим некоторые из них.

Команда git grep

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

По умолчанию эта команда ищет по файлам в рабочем каталоге. В качестве первого варианта вы можете использовать любой из параметров -n или --line-number, чтобы распечатать номера строк, в которых Git нашел совпадения:

$ git grep -n gmtime_r
compat/gmtime.c:3:#undef gmtime_r
compat/gmtime.c:8:      return git_gmtime_r(timep, &result);
compat/gmtime.c:11:struct tm *git_gmtime_r(const time_t *timep, struct tm *result)
compat/gmtime.c:16:     ret = gmtime_r(timep, result);

Например, вместо того, чтобы печатать все совпадения, вы можете попросить git grep обобщить выводимые командой данные, показав только те файлы, в которых обнаружены совпадения, вместе с количеством этих совпадений в каждом файле. Для этого потребуется параметр -c или --count:

$ git grep --count gmtime_r
  compat/gmtime.c:4
  compat/mingw.c:1
  compat/mingw.h:1
  date.c:3
  git-compat-util.h:2

Если вас интересует контекст строки поиска, можно показать метод или функцию, в котором присутствует совпадение с помощью параметра -p или --show-function:

$ git grep -p gmtime_r *.c
  date.c=static int match_multi_number(timestamp_t num, char c, const char *date,
  date.c:         if (gmtime_r(&now, &now_tm))
  date.c=static int match_digit(const char *date, struct tm *tm, int *offset, int
  *tm_gmt)
  date.c:         if (gmtime_r(&time, tm)) {
  date.c=int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset)
  date.c:         /* gmtime_r() in match_digit() may have clobbered it */

Вы также можете искать сложные комбинации строк, используя опцию --and, которая гарантирует, что будут отображены только строки, имеющие сразу несколько совпадений.

Например, давайте поищем любые строки, которые определяют константу, имя которой содержит любую из подстрок LINK или BUF_MAX, особенно в более старой версии кодовой базы Git, представленной тегом v1.8.0 (мы добавим параметры --break и --heading, которые помогут вывести результаты в более читаемом виде):

$ git grep --break --heading \
      -n -e '#define' --and \( -e LINK -e BUF_MAX \) v1.8.0
 v1.8.0:builtin/index-pack.c
62:#define FLAG_LINK (1u<<20)
v1.8.0:cache.h
73:#define S_IFGITLINK  0160000
74:#define S_ISGITLINK(m)
(((m) & S_IFMT) == S_IFGITLINK)

Поиск в журнале Git

Возможно, вы ищете не где присутствует некоторое выражение, а когда оно существовало или было добавлено. Команда git log обладает некоторыми мощными инструментами для поиска определённых коммитов по содержимому их сообщений или содержимому сделанных в них изменений. Например, если вы хотите найти, когда была добавлена константа ZLIB_BUF_MAX, то вы можете с помощью опции -S попросить Git показывать только те коммиты, в которых была добавлена или удалена эта строка.

$ git log -S ZLIB_BUF_MAX --oneline
  e01503b zlib: allow feeding more than 4GB in one go
  ef49a7a zlib: zlib can only process 4GB at a time

Если мы посмотрим на изменения, сделанные в этих коммитах, то увидим, что в ef49a7a константа была добавлена, а в e01503b — изменена. Если вам нужно найти что-то более сложное, вы можете с помощью опции -G передать регулярное выражение.


Поиск по журналу изменений строки

Другой, довольно продвинутый, поиск по истории, который бывает чрезвычайно полезным — поиск по истории изменений строки. Просто запустите git log с параметром -L, и он покажет вам историю изменения функции или строки кода в вашей кодовой базе.

Например, если мы хотим увидеть все изменения, произошедшие с функцией git_deflate_bound в файле zlib.c, мы можем выполнить git log -L :git_deflate_bound:zlib.c. Эта команда постарается определить границы функции, выполнит поиск по истории и покажет все изменения, которые были сделаны с функцией, в виде набора патчей в обратном порядке до момента создания функции.

Если для вашего языка программирования Git не умеет правильно определять функции и методы, вы можете передать ему регулярное выражение. Например, следующая команда выполнит такой же поиск как и предыдущая git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c. Также вы можете передать интервал строк или номер определённой строки и в этом случае вы получите похожий результат.


Перезапись истории

В частности, можно изменить порядок коммитов, сообщения или изменённые в коммитах файлы, объединить вместе или разбить на части, полностью удалить коммит — но только до того, как вы поделитесь своими наработками с другими.

Изменение последнего коммита

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

Если вы хотите изменить только сообщение вашего последнего коммита, это очень просто:

$ git commit --amend

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

Если вы создали коммит и затем хотите изменить зафиксированный снимок, добавив или изменив файлы (возможно, вы забыли добавить вновь созданный файл, когда совершали изначальный коммит), то процесс выглядит в основном так же. Вы добавляете в индекс необходимые изменения, редактируя файл и выполняя для него git add или git rm для отслеживаемого файла, а последующая команда git commit --amend берет вашу текущую область подготовленных изменений и делает её снимок для нового коммита.

Вы должны быть осторожными, используя этот приём, так как при этом изменяется SHA-1 коммита. Поэтому, как и с операцией rebase — не изменяйте ваш последний коммит, если вы уже отправили его в общий репозиторий.

С другой стороны, если изменения незначительны (исправление опечаток, добавление в коммит забытого файла), то текущее сообщение вполне можно оставить; чтобы лишний раз не вызывать редактор, просто добавьте измененные файлы в индекс и выполните команду

$ git commit --amend --no-edit

Удаление коммита

Если вы хотите избавиться от какого-либо коммита, то удалить его можно во время интерактивного перебазирования rebase -i. Напишите слово drop перед коммитом, который хотите удалить, или просто удалите его из списка:

  pick 461cb2a This commit is OK
  drop 5aecc10 This commit is broken

Из-за того, как Git создаёт объекты коммитов, удаление или изменение коммита влечёт за собой перезапись всех последующих коммитов. Чем дальше вы вернётесь в историю ваших коммитов, тем больше коммитов потребуется переделать. Это может вызвать множество конфликтов слияния, особенно если у вас много последующих коммитов, которые зависят от удалённого.

Если во время подобного перебазирования вы поняли, что это была не очень хорошая идея, то всегда можно остановиться. Просто выполните команду git rebase --abort и ваш репозиторий вернётся в то состояние, в котором он был до начала перебазирования.

Если вы завершили перебазирование, а затем решили, что полученный результат это не то, что вам нужно — воспользуйтесь командой git reflog, чтобы восстановить предыдущую версию вашей ветки. Дополнительную информацию по команде reflog можно найти в разделе Восстановление данных главы 10.

Дрю Дево создал практическое руководство с упражнениями по использованию git rebase. Найти его можно здесь: https://git-rebase.io/


Раскрытие тайн reset

Перед тем, как перейти к более специализированными утилитам, давайте поговорим о reset и checkout. Эти команды кажутся самыми непонятными из всех, которые есть в Git, когда вы в первый раз сталкиваетесь с ними. Они делают так много, что попытки по-настоящему их понять и правильно использовать кажутся безнадёжными.


Три дерева

Разобраться с командами reset и checkout будет проще, если считать, что Git управляет содержимым трёх различных деревьев. Здесь под «деревом» мы понимаем «набор файлов», а не специальную структуру данных. (В некоторых случаях индекс ведет себя не совсем так, как дерево, но для наших текущих целей его проще представлять именно таким.) В своих обычных операциях Git управляет тремя деревьями

Дерево          Назначение
HEAD            Снимок последнего коммита, родитель следующего
Индекс          Снимок следующего намеченного коммита
Рабочий Каталог Песочница

Указатель HEAD

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

Индекс

Индекс — это ваш следующий намеченный коммит. Мы также упоминали это понятие как «область подготовленных изменений» Git — то, что Git просматривает, когда вы выполняете git commit. Git заполняет индекс списком изначального содержимого всех файлов, выгруженных в последний раз в ваш рабочий каталог. Затем вы заменяете некоторые из таких файлов их новыми версиями и команда git commit преобразует изменения в дерево для нового коммита.

Технически, индекс не является древовидной структурой, на самом деле, он реализован как сжатый список flattened manifest — но для наших целей такого представления будет достаточно.


Рабочий Каталог

Наконец, у вас есть рабочий каталог. Два других дерева сохраняют свое содержимое эффективным, но неудобным способом внутри каталога .git. Рабочий Каталог распаковывает их в настоящие файлы, что упрощает для вас их редактирование. Считайте Рабочий Каталог песочницей, где вы можете опробовать изменения перед их коммитом в индекс (область подготовленных изменений) и затем в историю.

$ tree 
.
├── README
├── Rakefile 
└── lib
     └── simplegit.rb 
1 directory, 3 files

Технологический процесс

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

image

Давайте рассмотрим этот процесс: пусть вы перешли в новый каталог, содержащий один файл. Данную версию этого файла будем называть v1 и изображать голубым цветом. Выполним команду git init, которая создаст Git-репозиторий, у которого ссылка HEAD будет указывать на ещё несуществующую ветку (master пока не существует).

image

На данном этапе только дерево Рабочего Каталога содержит данные. Теперь мы хотим закоммитить этот файл, поэтому мы используем git add для копирования содержимого Рабочего Каталога в Индекс.

image

Затем, мы выполняем команду git commit, которая сохраняет содержимое Индекса как неизменяемый снимок, создает объект коммита, который указывает на этот снимок, и обновляет master так, чтобы он тоже указывал на этот коммит.

image

Если сейчас выполнить git status, то мы не увидим никаких изменений, так как все три дерева одинаковые.

Теперь мы хотим внести изменения в файл и закоммитить его. Мы пройдём через всё ту же процедуру; сначала мы отредактируем файл в нашем рабочем каталоге. Давайте называть эту версию файла v2 и обозначать красным цветом.

image

Если сейчас мы выполним git status, то увидим, что файл выделен красным в разделе «Изменения, не подготовленные к коммиту», так как его представления в Индексе и Рабочем Каталоге различны. Затем мы выполним git add для этого файла, чтобы поместить его в Индекс.

image

Если сейчас мы выполним git status, то увидим, что этот файл выделен зелёным цветом в разделе «Изменения, которые будут закоммичены», так как Индекс и HEAD различны — то есть, наш следующий намеченный коммит сейчас отличается от нашего последнего коммита. Наконец, мы выполним git commit, чтобы окончательно совершить коммит.

image

Сейчас команда git status не показывает ничего, так как снова все три дерева одинаковые.

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


Назначение команды reset

Команда reset становится более понятной, если рассмотреть её с учётом вышеизложенного. В следующих примерах предположим, что мы снова изменили файл file.txt и закоммитили его в третий раз.

image

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

Шаг 1: Перемещение указателя HEAD

Первое, что сделает reset — переместит то, на что указывает HEAD. Обратите внимание, изменяется не сам HEAD (что происходит при выполнении команды checkout); reset перемещает ветку, на которую указывает HEAD. Таким образом, если HEAD указывает на ветку master (то есть вы сейчас работаете с веткой master), выполнение команды git reset 9e5e6a4 сделает так, что master будет указывать на 9e5e6a4.

image

Не важно с какими опциями вы вызвали команду reset с указанием коммита (reset также можно вызывать с указанием пути), она всегда будет пытаться сперва сделать данный шаг. При вызове reset --soft на этом выполнение команды и остановится.

Теперь взгляните на диаграмму и постарайтесь разобраться, что случилось: фактически была отменена последняя команда git commit. Когда вы выполняете git commit, Git создает новый коммит и перемещает на него ветку, на которую указывает HEAD. Если вы выполняете reset на HEAD~ (родителя HEAD), то вы перемещаете ветку туда, где она была раньше, не изменяя при этом ни Индекс, ни Рабочий Каталог. Вы можете обновить Индекс и снова выполнить git commit, таким образом добиваясь того же, что делает команда git commit --amend (смотрите Изменение последнего коммита).

Шаг 2: Обновление Индекса --mixed

Заметьте, если сейчас вы выполните git status, то увидите отмеченные зелёным цветом изменения между Индексом и новым HEAD. Следующим, что сделает reset, будет обновление Индекса содержимым того снимка, на который указывает HEAD.

image

Если вы указали опцию --mixed, выполнение reset остановится на этом шаге. Такое поведение также используется по умолчанию, поэтому если вы не указали совсем никаких опций (в нашем случае git reset HEAD~), выполнение команды также остановится на этом шаге.

Снова взгляните на диаграмму и постарайтесь разобраться, что произошло: отменен не только ваш последний commit, но также и добавление в индекс всех файлов. Вы откатились назад до момента выполнения команд git add и git commit.

Шаг 3: Обновление Рабочего Каталога --hard Третье, что сделает reset — это приведение вашего Рабочего Каталога к тому же виду, что и Индекс. Если вы используете опцию --hard, то выполнение команды будет продолжено до этого шага.

image

Давайте разберемся, что сейчас случилось. Вы отменили ваш последний коммит, результаты выполнения команд git add и git commit, а также все изменения, которые вы сделали в рабочем каталоге.

Важно отметить, что только указание этого флага --hard делает команду reset опасной, это один из немногих случаев, когда Git действительно удаляет данные. Все остальные вызовы reset легко отменить, но при указании опции --hard команда принудительно перезаписывает файлы в Рабочем Каталоге. В данном конкретном случае, версия v3 нашего файла всё ещё остаётся в коммите внутри базы данных Git и мы можем вернуть её, просматривая наш reflog, но если вы не коммитили эту версию, Git перезапишет файл и её уже нельзя будет восстановить.


Резюме

Команда reset в заранее определённом порядке перезаписывает три дерева Git, останавливаясь тогда, когда вы ей скажете:

  1. Перемещает ветку, на которую указывает HEAD (останавливается на этом, если указана опция --soft)
  2. Делает Индекс таким же как и HEAD (останавливается на этом, если не указана опция --hard)
  3. Делает Рабочий Каталог таким же как и Индекс.

Reset с указанием пути

Основной форме команды reset (без опций --soft и --hard) вы также можете передавать путь, с которым она будет оперировать. В этом случае, reset пропустит первый шаг, а на остальных будет работать только с указанным файлом или набором файлов. Первый шаг пропускается, так как HEAD является указателем и не может ссылаться частично на один коммит, а частично на другой. Но Индекс и Рабочий Каталог могут быть изменены частично, поэтому reset выполняет шаги 2 и 3.

Итак, предположим вы выполнили команду git reset file.txt. Эта форма записи (так как вы не указали ни SHA-1 коммита, ни ветку, ни опций --soft или --hard) является сокращением для git reset --mixed HEAD file.txt, которая:

  1. Перемещает ветку, на которую указывает HEAD (будет пропущено)
  2. Делает Индекс таким же как и HEAD (остановится здесь) То есть, фактически, она копирует файл file.txt из HEAD в Индекс.

image

Это создает эффект отмены индексации файла. Если вы посмотрите на диаграммы этой команды и команды git add, то увидите, что их действия прямо противоположные.

image

Именно поэтому в выводе git status предлагается использовать такую команду для отмены индексации файла. (Смотрите подробности в Отмена индексации файла.)

Мы легко можем заставить Git «брать данные не из HEAD», указав коммит, из которого нужно взять версию этого файла. Для этого мы должны выполнить следующее git reset eb43bf file.txt.

image

Можно считать, что, фактически, мы в Рабочем Каталоге вернули содержимое файла к версии v1, выполнили для него git add, а затем вернули содержимое обратно к версии v3 (в действительности все эти шаги не выполняются). Если сейчас мы выполним git commit, то будут сохранены изменения, которые возвращают файл к версии v1, но при этом файл в Рабочем Каталоге никогда не возвращался к такой версии.

Заметим, что как и команде git add, reset можно указывать опцию --patch для отмены индексации части содержимого. Таким способом вы можете избирательно отменять индексацию или откатывать изменения.


Слияние коммитов

Давайте посмотрим, как, используя вышеизложенное, сделать кое-что интересное — слияние коммитов. Допустим, у вас есть последовательность коммитов с сообщениями вида «упс.», «В работе» и «позабыл этот файл». Вы можете использовать reset для того, чтобы просто и быстро слить их в один. (В разделе Объединение коммитов главы 7 представлен другой способ сделать то же самое, но в данном примере проще воспользоваться reset.)

Предположим, у вас есть проект, в котором первый коммит содержит один файл, второй коммит добавляет новый файл и изменяет первый, а третий коммит снова изменяет первый файл. Второй коммит был сделан в процессе работы и вы хотите слить его со следующим.

image

Вы можете выполнить git reset --soft HEAD~2, чтобы вернуть ветку HEAD на какой-то из предыдущих коммитов (на первый коммит, который вы хотите оставить):

image

Затем просто снова выполните git commit:

image

Теперь вы можете видеть, что ваша «достижимая» история (история, которую вы впоследствии отправите на сервер), сейчас выглядит так — у вас есть первый коммит с файлом file-a.txt версии v1, и второй, который изменяет файл file-a.txt до версии v3 и добавляет file-b.txt. Коммита, который содержал файл версии v2 не осталось в истории.


Сравнение с checkout

Наконец, вы можете задаться вопросом, в чем же состоит отличие между checkout и reset. Как и reset, команда checkout управляет тремя деревьями Git, и также её поведение зависит от того указали ли вы путь до файла или нет.

Без указания пути Команда git checkout [branch] очень похожа на git reset --hard [branch], в процессе их выполнения все три дерева изменяются так, чтобы выглядеть как [branch]. Но между этими командами есть два важных отличия.

Во-первых, в отличие от reset --hard, команда checkout бережно относится к рабочему каталогу, и проверяет, что она не трогает файлы, в которых есть изменения. В действительности, эта команда поступает немного умнее — она пытается выполнить в Рабочем Каталоге простые слияния так, чтобы все файлы, которые вы не изменяли, были обновлены. С другой стороны, команда reset --hard просто заменяет всё целиком, не выполняя проверок.

Второе важное отличие заключается в том, как эти команды обновляют HEAD. В то время как reset перемещает ветку, на которую указывает HEAD, команда checkout перемещает сам HEAD так, чтобы он указывал на другую ветку.

Например, пусть у нас есть ветки master и develop, которые указывают на разные коммиты и мы сейчас находимся на ветке develop (то есть HEAD указывает на неё). Если мы выполним git reset master, сама ветка develop станет ссылаться на тот же коммит, что и master. Если мы выполним git checkout master, то develop не изменится, но изменится HEAD. Он станет указывать на master.

Итак, в обоих случаях мы перемещаем HEAD на коммит A, но важное отличие состоит в том, как мы это делаем. Команда reset переместит также и ветку, на которую указывает HEAD, а checkout перемещает только сам HEAD.

image


С указанием пути

Другой способ выполнить checkout состоит в том, чтобы указать путь до файла. В этом случае, как и для команды reset, HEAD не перемещается. Эта команда как и git reset [branch] file обновляет файл в индексе версией из коммита, но дополнительно она обновляет и файл в рабочем каталоге. То же самое сделала бы команда git reset --hard [branch] file (если бы reset можно было бы так запускать) — это небезопасно для рабочего каталога и не перемещает HEAD.

Также как git reset и git add, команда checkout принимает опцию --patch для того, чтобы позволить вам избирательно откатить изменения содержимого файла по частям.

Заключение Ниже приведена памятка того, как эти команды воздействуют на каждое из деревьев. В столбце HEAD указывается REF если эта команда перемещает ссылку (ветку), на которую HEAD указывает, и HEAD если перемещается только сам HEAD. Обратите особое внимание на столбец «Сохранность РК» — если в нем указано NO, то хорошенько подумайте прежде чем выполнить эту команду

image


Продвинутое слияние

Философия Git заключается в том, чтобы быть умным, когда слияние разрешается однозначно, но если возникает конфликт, он не пытается сумничать и разрешить его автоматически.

Конфликты слияния

Во-первых, если есть возможность, перед слиянием, в котором может возникнуть конфликт, позаботьтесь о том, чтобы ваша рабочая копия была без локальных изменений. Если у вас есть несохранённые наработки, либо припрячьте их, либо сохраните их во временной ветке. Таким образом, вы сможете легко отменить любые изменения, которые сделаете в рабочем каталоге. Если при выполнении слияния вы не сохраните сделанные изменения, то некоторые из описанных ниже приёмов могут привести к утрате этих наработок.


Прерывание слияния

В данный момент у нас есть несколько вариантов дальнейших действий. Во-первых, давайте рассмотрим как выйти из этой ситуации. Если вы, возможно, не были готовы к конфликтам и на самом деле не хотите связываться с ними, вы можете просто отменить попытку слияния, используя команду git merge --abort

$ git status -sb
  ## master
  UU hello.rb
  
$ git merge --abort

$ git status -sb
  ## master

Эта команда пытается откатить ваше состояние до того, что было до запуска слияния. Завершиться неудачно она может только в случаях, если перед запуском слияния у вас были не припрятанные или не зафиксированные изменения в рабочем каталоге, во всех остальных случаях всё будет хорошо.

Если по каким-то причинам вы обнаружили себя в ужасном состоянии и хотите просто начать всё сначала, вы можете также выполнить git reset --hard HEAD (либо вместо HEAD указав то, куда вы хотите откатиться). Но помните, что это откатит все изменения в рабочем каталоге, поэтому удостоверьтесь, что никакие из них вам не нужны.

Игнорирование пробельных символов

В данном конкретном случае конфликты связаны с пробельными символами. Мы знаем это, так как это простой пример, но в реальных ситуациях это также легко определить при изучении конфликта, так как каждая строка в нем будет удалена и добавлена снова. По умолчанию Git считает все эти строки изменёнными и поэтому не может слить файлы. Стратегии слияния, используемой по умолчанию, можно передать аргументы, и некоторые из них предназначены для соответствующей настройки игнорирования изменений пробельных символов. Если вы видите, что множество конфликтов слияния вызваны пробельными символами, то вы можете прервать слияние и запустить его снова, но на этот раз с опцией -Xignore-all-space или -Xignore-space-change. Первая опция игнорирует изменения в любом количестве существующих пробельных символов, вторая игнорирует вообще все изменения пробельных символов.

$ git merge -Xignore-space-change whitespace
  Auto-merging hello.rb
  Merge made by the 'recursive' strategy.
   hello.rb | 2 +-
   1 file changed, 1 insertion(+), 1 deletion(-)

Поскольку в этом примере реальные изменения файлов не конфликтуют, то при игнорировании изменений пробельных символов всё сольётся хорошо. Это значительно облегчает жизнь, если кто-то в вашей команде любит временами заменять все пробелы на табуляции или наоборот.


История при слиянии

Другой полезный инструмент при разрешении конфликтов слияния — это команда git log. Она поможет вам получить информацию о том, что могло привести к возникновению конфликтов. Временами может быть очень полезным просмотреть историю, чтобы понять почему в двух ветках разработки изменялась одна и та же область кода.

Для получения полного списка всех уникальных коммитов, которые были сделаны в любой из сливаемых веток, мы можем использовать синтаксис «трёх точек», который мы изучили в Три точки.

 $ git log --oneline --left-right HEAD...MERGE_HEAD
  < f1270f7 Update README
  < 9af9d3b Create README
  < 694971d Update phrase to 'hola world'
  > e3eb223 Add more tests
  > 7cff591 Create initial testing script
  > c3ffff1 Change text to 'hello mundo'

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

Мы также можем сократить его, попросив предоставить нам более специализированную информацию. Если мы добавим опцию --merge к команде git log, то она покажет нам только те коммиты, в которых изменялся конфликтующий в данный момент файл.

$ git log --oneline --left-right --merge
  < 694971d Update phrase to 'hola world'
  > c3ffff1 Change text to 'hello mundo'

Если вы выполните эту команду с опцией -p, то получите только список изменений файла, на котором возник конфликт. Это может быть действительно полезным для быстрого получения информации, которая необходима, чтобы понять почему что-либо конфликтует и как наиболее правильно это разрешить.


Комбинированный формат изменений

Так как Git добавляет в индекс все успешные результаты слияния, то при вызове git diff в состоянии конфликта слияния будет отображено только то, что сейчас конфликтует. Это может быть полезно, так как вы сможете увидеть какие ещё конфликты нужно разрешить.

Если вы выполните git diff сразу после конфликта слияния, то получите информацию в довольно своеобразном формате.

$ git diff
  diff --cc hello.rb
  index 0399cd5,59727f0..0000000
  --- a/hello.rb
  +++ b/hello.rb
  @@@ -1,7 -1,7 +1,11 @@@
    #! /usr/bin/env ruby
    def hello
  ++<<<<<<< HEAD
   +  puts 'hola world'
  ++=======
  +   puts 'hello mundo'
  ++>>>>>>> mundo
end hello()

Такой формат называется «комбинированным» (Combined Diff), для каждого различия в нем содержится два раздела с информацией. В первом разделе отображены различия строки (добавлена она или удалена) между «вашей» веткой и содержимым вашего рабочего каталога, а во втором разделе содержится то же самое, но между «их» веткой и рабочим каталогом.

Таким образом, в данном примере вы можете увидеть строки <<<<<<< и >>>>>>> в файле в вашем рабочем каталоге, хотя они отсутствовали в сливаемых ветках. Это вполне оправдано, потому что, добавляя их, инструмент слияния предоставляет вам дополнительную информацию, но предполагается, что мы удалим их.

Если мы разрешим конфликт и снова выполним команду git diff, то получим ту же информацию, но в немного более полезном представлении.

$ vim hello.rb
$ git diff
  diff --cc hello.rb
  index 0399cd5,59727f0..0000000
  --- a/hello.rb
  +++ b/hello.rb
  @@@ -1,7 -1,7 +1,7 @@@
    #! /usr/bin/env ruby
    def hello
  -   puts 'hola world'
   -  puts 'hello mundo'
  ++  puts 'hola mundo'
end hello()

В этом выводе указано, что строка hola world при слиянии присутствовала в «нашей» ветке, но отсутствовала в рабочей копии, строка hello mundo была в «их» ветке, но не в рабочей копии, и, наконец, hola mundo не была ни в одной из сливаемых веток, но сейчас присутствует в рабочей копии. Это бывает полезно просмотреть перед коммитом разрешения конфликта.

Такую же информацию вы можете получить и после выполнения слияния с помощью команды git log, узнав таким образом как был разрешён конфликт. Git выводит информацию в таком формате, если вы выполните git show для коммита слияния или вызовете команду git log -p с опцией --cc (без неё данная команда не показывает изменения для коммитов слияния).

$ git log --cc -p -1
  commit 14f41939956d80b9e17bb8721354c33f8d5b5a79
  Merge: f1270f7 e3eb223
  Author: Scott Chacon <[email protected]>
  Date:   Fri Sep 19 18:14:49 2014 +0200
      Merge branch 'mundo'
      Conflicts:
          hello.rb
  diff --cc hello.rb
  index 0399cd5,59727f0..e1d0799
  --- a/hello.rb
  +++ b/hello.rb
  @@@ -1,7 -1,7 +1,7 @@@
    #! /usr/bin/env ruby
    def hello
  -   puts 'hola world'
   -  puts 'hello mundo'
  ++  puts 'hola mundo'
end hello()

Отмена слияний

Коммит слияния не исключение. Допустим, вы начали работать в тематической ветке, случайно слили ее в master, и теперь ваша история коммитов выглядит следующим образом:

image

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

Исправление ссылок

Если нежелаемый коммит слияния существует только в вашем локальном репозитории, то простейшее и лучшее решение состоит в перемещении веток так, чтобы они указывали туда куда вам нужно. В большинстве случаев, если вы после случайного git merge выполните команду git reset --hard HEAD~, то указатели веток восстановятся так, что будут выглядеть следующим образом:

image

Здесь небольшое напоминание: reset --hard обычно выполняет три шага:

  1. Перемещает ветку, на которую указывает HEAD. В данном случае мы хотим переместить master туда, где она была до коммита слияния (C6).
  2. Приводит индекс к такому же виду что и HEAD.
  3. Приводит рабочий каталог к такому же виду, что и индекс.

Недостаток этого подхода состоит в изменении истории, что может привести к проблемам в случае совместно используемого репозитория. Загляните в Опасности перемещения, чтобы узнать что именно может произойти; кратко говоря, если у других людей уже есть какие-то из изменяемых вами коммитов, вы должны отказаться от использования reset. Этот подход также не будет работать, если после слияния уже был сделан хотя бы один коммит; перемещение ссылки фактически приведёт к потере этих изменений.


Отмена коммита

Если перемещение указателей ветки вам не подходит, Git предоставляет возможность сделать новый коммит, который откатывает все изменения, сделанные в другом. Git называет эту операцию «восстановлением» («revert»), в данном примере вы можете вызвать её следующим образом:

  $ git revert -m 1 HEAD
  [master b1d8379] Revert "Merge branch 'topic'"

Опция -m 1 указывает какой родитель является «основной веткой» и должен быть сохранен. Когда вы выполняете слияние в HEAD (git merge topic), новый коммит будет иметь двух родителей: первый из них HEAD (C6), а второй — вершина ветки, которую сливают с текущей (C4). В данном случае, мы хотим отменить все изменения, внесённые слиянием родителя #2 (C4), и сохранить при этом всё содержимое из родителя #1 (C6).

История с коммитом восстановления (отменой коммита слияния) выглядит следующим образом:

image

Новый коммит ^M имеет точно такое же содержимое как C6, таким образом, начиная с нее всё выглядит так, как будто слияние никогда не выполнялось, за тем лишь исключением, что «теперь уже не слитые» коммиты всё также присутствуют в истории HEAD. Git придет в замешательство, если вы вновь попытаетесь слить topic в ветку master:

$ git merge topic
  Already up-to-date.

В ветке topic нет ничего, что ещё недоступно из ветки master. Плохо, что в случае добавления новых наработок в topic, при повторении слияния Git добавит только те изменения, которые были сделаны после отмены слияния:

image

Лучшим решением данной проблемы является откат коммита отмены слияния, так как теперь вы хотите внести изменения, которые были отменены, а затем создание нового коммита слияния:

$ git revert ^M
  [master 09f0126] Revert "Revert "Merge branch 'topic'""
$ git merge topic

image

В этом примере, M и ^M отменены. В коммите ^^M, фактически, сливаются изменения из C3 и C4, а в C8 — изменения из C7, таким образом, ветка topic полностью слита.


Rerere

Функциональность git rerere — частично скрытый компонент Git. Её имя является сокращением для «reuse recorded resolution» («повторно использовать сохранённое решение»). Как следует из названия, эта функциональность позволяет попросить Git запомнить то, как вы разрешили некоторую часть конфликта, так что в случае возникновения такого же конфликта, Git сможет его разрешить автоматически.


Создание пакетов

Помимо рассмотренных ранее основных способов передачи данных Git по сети (HTTP, SSH и т.п.), существует ещё один способ, который обычно не используется, но в некоторых случаях может быть весьма полезным.

Git умеет «упаковывать» свои данные в один файл. Это может быть полезным в разных ситуациях. Может быть, ваша сеть не работает, а вы хотите отправить изменения своим коллегам. Возможно, вы работаете откуда-то извне офиса и не имеете доступа к локальной сети по соображениям безопасности. Может быть, ваша карта беспроводной/проводной связи просто сломалась. Возможно, у вас в данный момент нет доступа к общему серверу, а вы хотите отправить кому-нибудь по электронной почте обновления, но передавать 40 коммитов с помощью format-patch не хотите.

В этих случаях вам может помочь команда git bundle. Она упакует всё, что в обычной ситуации было бы отправлено по сети командой git push, в бинарный файл, который вы можете передать кому-нибудь по электронной почте или поместить на флешку и затем распаковать в другом репозитории.

Рассмотрим простой пример. Допустим, у вас есть репозиторий с двумя коммитами:

$ git log
  commit 9a466c572fe88b195efd356c3f2bbeccdb504102
  Author: Scott Chacon <[email protected]>
  Date:   Wed Mar 10 07:34:10 2010 -0800
      Second commit
  commit b1ec3248f39900d2a406049d762aa68e9641be25
  Author: Scott Chacon <[email protected]>
  Date:   Wed Mar 10 07:34:01 2010 -0800
First commit

Если вы хотите отправить кому-нибудь этот репозиторий, но не имеете доступа на запись к общей копии репозитория или просто не хотите его настраивать, то вы можете упаковать его командой git bundle create.

$ git bundle create repo.bundle HEAD master
  Counting objects: 6, done.
  Delta compression using up to 2 threads.
  Compressing objects: 100% (2/2), done.
  Writing objects: 100% (6/6), 441 bytes, done.
  Total 6 (delta 0), reused 0 (delta 0)

В результате вы получили файл repo.bundle, в котором содержатся все данные, необходимые для воссоздания ветки master репозитория. Команде bundle необходимо передать список или диапазон коммитов, которые вы хотите добавить в пакет. Если вы намереваетесь использовать пакет для того, чтобы клонировать репозиторий где-нибудь ещё, вы должны добавить в этот список HEAD, как это сделали мы. Вы можете отправить файл repo.bundle кому-нибудь по электронной почте или скопировать его на USB-диск, тем самым легко решив исходную проблему.

С другой стороны, допустим, вы получили файл repo.bundle и хотите поработать над этим проектом. Вы можете клонировать репозиторий из бинарного файла в каталог, почти также как вы делаете это при использовании URL.

$ git clone repo.bundle repo
  Cloning into 'repo'...
  ...
$ cd repo
$ git log --oneline
  9a466c5 Second commit
  b1ec324 First commit

Если при создании пакета вы не указали в списке ссылок HEAD, то при распаковке вам потребуется указать -b master или какую-либо другую ветку, включённую в пакет, иначе Git не будет знать, на какую ветку ему следует переключиться.

Теперь предположим, что вы сделали три коммита и хотите отправить их обратно в виде пакета на USB-флешке или по электронной почте.

$ git log --oneline
  71b84da Last commit - second repo
  c99cf5b Fourth commit - second repo
  7011d3d Third commit - second repo
  9a466c5 Second commit
  b1ec324 First commit

Во-первых, нам нужно определить диапазон коммитов, которые мы хотим включить в пакет. В отличие от сетевых протоколов, которые сами выясняют минимальный набор данных, который нужно передать по сети, в данном случае мы должны сделать это сами вручную. В данном примере вы можете сделать, как раньше и упаковать полностью весь репозиторий, но будет лучше упаковать только изменения — три коммита, сделанные локально.

Таким образом, команда git bundle может быть, действительно, полезной для организации совместной работы или для выполнения сетевых операций, когда у вас нет доступа к соответствующей сети или общему репозиторию.


Хранилище учётных данных

Если для подключения к удалённым серверам вы используете протокол SSH, то вы можете использовать ключ вместо пароля, что позволит вам безопасно передавать данные без ввода логина и пароля. Однако, это невозможно при использовании HTTP-протоколов — каждое подключение требует пары логин, пароль. Всё ещё сложнее для систем с двухфакторной аутентификацией, когда выражение, которое вы используете в качестве пароля, генерируется случайно и его сложно воспроизвести.

К счастью, в Git есть система управления учётными данными, которая может помочь в этом. В Git «из коробки» есть несколько опций:

• По умолчанию Git не кеширует учётные данные совсем. Каждое подключение будет запрашивать у вас логин и пароль.

• В режиме cache учётные данные сохраняются в памяти в течение определённого периода времени. Ни один из паролей никогда не сохраняется на диск и все они удаляются из кеша через 15 минут.

• В режиме store учётные данные сохраняются на неограниченное время в открытом виде в файле на диске. Это значит что, до тех пор пока вы не измените пароль к Git-серверу, вам не потребуется больше вводить ваши учётные данные. Недостатком такого подхода является то, что ваш пароль хранится в открытом виде в файле в вашем домашнем каталоге.

• На случай если вы используете Mac, в Git есть режим osxkeychain, при использовании которого учётные данные хранятся в защищённом хранилище, привязанному к вашему системному аккаунту. В этом режиме учётные данные сохраняются на диск на неограниченное время, но они шифруются с использованием той же системы, с помощью которой сохраняются HTTPS-сертификаты и автозаполнения для Safari.

Вы можете выбрать один из этих методов, изменив настройки Git:

$ git config --global credential.helper cache

К оглавлению

Конфигурация Git

В главе Введение кратко упоминалось, что вы можете настроить Git, используя команду git config. Первое, что вы делали, это установили своё имя и e-mail адрес:

$ git config --global user.name "John Doe"
$ git config --global user.email [email protected]

Сейчас вы познакомитесь с несколькими наиболее интересными опциями, которые можно установить для настройки поведения Git.

Кратко: Git использует набор конфигурационных файлов для изменения стандартного поведения, если это необходимо. Вначале, Git ищет настройки в файле /etc/gitconfig, который содержит настройки для всех пользователей в системе и всех репозиториев. Если передать опцию --system команде git config, то операции чтения и записи будут производиться именно с этим файлом.

Следующее место, куда смотрит Git — это файл ~/.gitconfig (или ~/.config/git/config), который хранит настройки конкретного пользователя. Вы можете указать Git читать и писать в него, используя опцию --global.

Наконец, Git ищет параметры конфигурации в файле настроек в каталоге Git (.git/config) текущего репозитория. Эти значения относятся только к текущему репозиторию и доступны при передаче параметра --local команде git config. (Если уровень настроек не указан явно, то подразумевается локальный.)

Каждый из этих уровней (системный, глобальный, локальный) переопределяет значения предыдущего уровня, например, значения из .git/config важнее значений из /etc/gitconfig.

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


Базовая конфигурация клиента

Конфигурационные параметры Git разделяются на две категории: настройки клиента и настройки сервера. Большая часть — клиентские, для настройки ваших личных предпочтений в работе.

Для просмотра полного списка настроек, поддерживаемых вашей версией Git, выполните команду:

$ man git-config

Эта команда выведет список доступных настроек с довольно подробным описанием. Так же, соответствующую документацию можно найти здесь https://git-scm.com/docs/git-config.html.

core.editor По умолчанию, Git использует ваш редактор по умолчанию ($VISUAL или $EDITOR), если значение не задано — переходит к использованию редактора vi при создании и редактировании сообщений коммитов или тегов. Чтобы изменить редактор по умолчанию, воспользуйтесь настройкой core.editor:

$ git config --global core.editor emacs

Теперь, вне зависимости от того, какой редактор является основным для вашего окружения, Git будет вызывать Emacs для редактирования сообщений.

commit.template Если указать путь к существующему файлу, то он будет использован как сообщение по умолчанию при создании коммита. Смысл создания шаблона сообщения коммита в том, чтобы лишний раз напомнить себе (или другим) о требованиях к формату или стилю оформления сообщения коммита. Например, предположим что вы создали файл ~/.gitmessage.txt, который выглядит так:

Subject line (try to keep under 50 characters)
Multi-line description of commit,
feel free to be detailed.
[Ticket: X]

Обратите внимание, что шаблон напоминает коммитеру о том, чтобы строка заголовка сообщения была короткой (для поддержки однострочного вывода команды git log -- oneline), что дополнительную информацию в сообщении следует располагать ниже, а так же о том, что было бы неплохо при наличии добавить ссылку на номер задачи или сообщения в системе отслеживания ошибок.

Чтобы заставить Git отображать содержимое этого файла в редакторе каждый раз при выполнении команды git commit, следует установить значение параметра commit.template:

$ git config --global commit.template ~/.gitmessage.txt
$ git commit

Теперь, при создании коммита, в вашем редакторе будет отображаться сообщение изменённого вида:

  Subject line (try to keep under 50 characters)
  Multi-line description of commit,
  feel free to be detailed.
  [Ticket: X]
  # Please enter the commit message for your changes. Lines starting
  # with '#' will be ignored, and an empty message aborts the commit.
  # On branch master
  # Changes to be committed:
  #   (use "git reset HEAD <file>..." to unstage)
  #
  # modified:   lib/test.rb
  #
  ~
  ~
  ".git/COMMIT_EDITMSG" 14L, 297C

Если ваша команда придерживается требований к сообщениям коммитов, то создание шаблона такого сообщения и настройка Git на его использование увеличит вероятность соответствия заданным требованиям.

core.excludesfile В разделе Игнорирование файлов главы 2 сказано, что вы можете указывать шаблоны исключений в файле .gitignore вашего проекта, чтобы Git не отслеживал их и не добавлял в индекс при выполнении команды git add.

Однако, иногда вам нужно игнорировать определенные файлы во всех ваших репозиториях. Если на вашем компьютере работает Mac OS X, вероятно вы знакомы с файлами .DS_Store. Если вы используете Emacs или Vim, то вы знаете про файлы, имена которых заканчиваются на ~ или .swp.

Данная настройка позволяет вам определить что-то вроде глобального файла .gitignore. Если вы создадите файл ~/.gitignore_global с содержанием:

  *~
  .*.swp
  .DS_Store

... и выполните команду git config --global core.excludesfile ~/.gitignore_global, то Git больше не потревожит вас на счёт этих файлов.


Экспорт репозитория

Атрибуты Git так же позволяют вам делать некоторые интересные вещи при экспорте вашего проекта. export-ignore

Вы можете указать Git игнорировать определённые файлы и каталоги при создании архива. Если в вашем проекте есть файл или каталог, которые вам нужны, но вы не хотите включать их в архив при экспорте, то можно присвоить им атрибут export-ignore.

Например, у вас есть несколько файлов в каталоге test/ и совершенно нет смысла включать их в архив вашего проекта. В этом случае достаточно добавить следующую строку в файл .gitattributes:

  test/ export-ignore

Теперь, при создании архива проекта командой git archive, каталог test/ не будет включен в архив.


Хуки в Git

Как и многие другие системы контроля версий, Git предоставляет возможность запуска пользовательских скриптов в случае возникновения определённых событий. Такие действия называются хуками и разделяются на две группы: серверные и клиентские. Если хуки на стороне клиента запускаются такими операциями как слияние или создание коммита, то на стороне сервера они инициируются сетевыми операциями, такими как получение отправленного коммита. Хуки часто используются для широкого круга задач.

Установка хука

Хуки хранятся в подкаталоге hooks относительно основного каталога Git. Для большинства проектов это .git/hooks. Когда вы инициализируете новый репозиторий командой git init, Git наполняет каталог hooks примерами скриптов, большинство из которых готовы к использованию, при этом каждый из них содержит документацию по используемым входным данным. Все примеры представлены в виде шелл скриптов, содержащими код на Perl, но вы можете использовать любой язык для написания скриптов — главное правильно именовать исполняемые файлы. Если вы решите использовать какой-либо из предустановленных скриптов, то достаточно его просто переименовать, убрав суффикс .sample.

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

Клиентские Хуки

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

Хуки на сервере

В дополнение к хукам на стороне клиента, как системный администратор вы можете использовать несколько важных хуков на сервере для вашего проекта, тем самым обеспечив выполнение практически любой политики. Эти скрипты выполняются до и после отправки на сервер. Pre-хуки могут возвращать ненулевой код в любой момент, что отменит передачу и отправит сообщение об ошибке клиенту; таким образом вы можете реализовать сколь угодно сложную политику.


К оглавлению

Графические интерфейсы

Стоит помнить, что всё, что можно сделать с помощью графического интерфейса, может быть выполнено и из консоли; командная строка по прежнему является местом, где у вас больше всего возможностей и контроля над вашими репозиториями.

Установив Git, вы также получаете два графических инструмента: gitk и git-gui.

gitk — это графический просмотрщик истории. Что-то типа улучшенных git log и git grep. Это тот инструмент, который вы будете использовать для поиска событий или визуализации истории. Проще всего вызвать Gitk из командной строки: Просто перейдите в каталог с репозиторием и наберите:

$ gitk [git log options]

Gitk принимает много различных опций, большинство из которых транслируются в используемый git log. Возможно, наиболее полезная опция — --all, которая указывает Gitk выводить коммиты, доступные из любой ссылки, а не только HEAD

Интерфейс на картинке похож на вывод git log --graph; каждая точка соответствует коммиту, линии отражают родство коммитов, а ссылки изображены цветными прямоугольниками. Жёлтая точка обозначает HEAD, а красная — изменения, которые попадут в следующий коммит. В нижней части расположены элементы интерфейса для просмотра выделенного коммита: слева показаны изменения и комментарий, а справа — общая информация по изменённым файлам. В центре расположены элементы для поиска по истории.

git-gui, в отличие от gitk — это инструмент редактирования отдельных коммитов. Его тоже очень просто вызвать из консоли:

$ git gui

Другие инструменты

Существует огромное множество других графических инструментов для работы с Git, начиная от специализированных, выполняющих одну задачу, заканчивая «комбайнами» покрывающими всю функциональность Git. На официальном сайте Git поддерживается в актуальном состоянии список наиболее популярных оболочек: https://git-scm.com/downloads/guis. Более подробный список доступен на Git вики: https://git.wiki.kernel.org/index.php/Interfaces,_frontends,_and_tools#Graphical_Interfaces.


Git в Bash

Если вы используете Bash, то можете задействовать некоторые из его фишек для облегчения работы с Git. К слову, Git поставляется с плагинами для нескольких командных оболочек, но они выключены по умолчанию.

Для начала, скачайте файл contrib/completion/git-completion.bash из репозитория с исходным кодом Git. Поместите его в укромное место — например, в ваш домашний каталог — и добавьте следующие строки в .bashrc:

. ~/git-completion.bash

Как только закончите с этим, перейдите в каталог с Git репозиторием и наберите:

$ git chec<tab>

...и Bash дополнит строку до git checkout. Эта магия работает для всех Git команд, их параметров, удалённых репозиториев и имён ссылок там, где это возможно.

Возможно, вам также пригодится отображение информации о репозитории, расположенном в текущем каталоге. Вы можете выводить сколь угодно сложную информацию, но обычно достаточно названия текущей ветки и статуса рабочего каталога. Чтобы снабдить строку приветствия этой информацией, скачайте файл contrib/completion/git-prompt.sh из репозитория с исходным кодом Git и добавьте примерно такие строки в .bashrc:

   . ~/git-prompt.sh
  export GIT_PS1_SHOWDIRTYSTATE=1
  export PS1='\w$(__git_ps1 " (%s)")\$ '

Часть \w означает текущий рабочий каталог, \$ — индикатор суперпользователя (обычно $ или #), а __git_ps1 " (%s)" вызывает функцию, объявленную в git-prompt.sh, с аргументом (%s) — строкой форматирования. Теперь ваша строка приветствия будет похожа на эту, когда вы перейдёте в каталог с Git репозиторием:

image

Оба вышеперечисленных скрипта снабжены полезной документацией, загляните внутрь git-completion.bash и git-prompt.sh чтобы узнать больше.


Git в Zsh

Git поставляется с поддержкой автодополнения для Zsh. Чтобы начать им пользоваться, просто добавьте строку autoload -Uz compinit && compinit в ваш .zshrc файл. Интерфейс Zsh более функциональный чем в Bash:

$ git che<tab>
check-attr        -- display gitattributes information
check-ref-format  -- ensure that a reference name is well formed
checkout          -- checkout branch or paths to working tree
checkout-index    -- copy files from index to working directory
cherry            -- find commits not merged upstream
cherry-pick       -- apply changes introduced by some existing commits

Возможные варианты автодополнения не просто перечислены; они снабжены полезными описаниями и вы можете выбрать нужный вариант из предложенных, перемещаясь по ним нажатием клавиши Tab. Это работает не только для команд Git, но и для их аргументов, названий объектов внутри репозитория (например, ссылки и удалённые репозитории), а также для имён файлов и прочего.

В состав Zsh входит фреймворк vcs_info, предназначенный для извлечения информации из систем контроля версий. Чтобы отобразить имя текущей ветки в правой строке приветствия, добавьте следующие строки в файл ~/.zshrc:

  autoload -Uz vcs_info
  precmd_vcs_info() { vcs_info }
  precmd_functions+=( precmd_vcs_info )
  setopt prompt_subst
  RPROMPT=\$vcs_info_msg_0_
  # PROMPT=\$vcs_info_msg_0_'%# '
  zstyle ':vcs_info:git:*' formats '%b'

В результате вы будете видеть имя текущей ветки в правой части окна терминала каждый раз, как перейдёте внутрь Git репозитория. (Для отображения названия ветки слева используйте PROMPT вместо RPROMPT)

Дополнительную информацию о vcs_info можно найти в документации zshcontrib(1) или онлайн http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#Version-Control-Information.

Возможно, вы предпочтёте использовать поставляемый вместе с Git скрипт настройки git-prompt.sh; детали использования приведены в комментариях к файлу https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh. Скрипт git-prompt.sh совместим с обеими оболочками Bash и Zsh.

Zsh настолько конфигурируем, что существуют целые фреймворки, посвящённые его улучшению. Пример такого проекта, называемый oh-my-zsh, расположен на https://github.com/robbyrussell/oh-my-zsh. Система плагинов этого проекта включает в себя мощнейший набор правил автодополнения для Git, а многие «темы» (служащие для настройки строк приветствия) отображают информацию из различных систем контроля версий. Пример темы oh-my-zsh — лишь один из многих вариантов применения.

image

progit-mynotes's People

Contributors

artemiosdev avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.