Книга Git сообщества ##Добро пожаловать в Git## Добро пожаловать в быструю, распределенную систему контроля версий Git. Эта книга задумывалась как отправная точка для новичков в Git, чтобы помочь им изучить его максимально легко и быстро. Эта книга начинается со знакомства читателя с тем, каким образом Git хранит данные, чтобы обеспечить понимание, почему это сделано иначе, чем в остальных средствах контроля версий. Это должно занять примерно 20 минут. Далее мы рассмотрим **Базовое использование Git** - команды, которые вы будете использовать 90% времени. Это даст вам хорошую основу, для использования Git в большей части того, для чего вы и планировали его использовать. Для прочтения этой главы вам потребуется около получаса. Затем мы изучим **Использование Git на Среднем Уровне** - немного более сложные вещи, которые могут заменить некоторые из базовых команд, освоенных в первом разделе. По большей части это будут некоторые трюки и команды, которые позволят вам чувствовать себя немного комфортней после знакомства с базовыми командами. После того как вы освоите все это, мы охватим **Продвинутое использование Git**, где рассмотрим такие команды, которые большинство людей вряд ли используют так уж часто, но именно они могут быть крайне полезными в определенных ситуациях. Изучение этих команд завершит ваше обучение и вы станете настоящим мастером Git! Теперь, когда вы уже знаете Git, мы перейдем к разделу **Работая с Git**. Здесь пора перейти к практическому использованию Git в скриптах, совместно со средствами разработки, редакторами и т. п. Этот раздел предназначен для того, чтобы помочь вам интегрировать Git в ваше окружение. Напоследок мы представим несколько статей о **низкоуровневой организации** Git, которые могут помочь интересующимся узнать, что происходит внутри самого Git и его протоколах. ### Обратная связь и сотрудничество ### Если вы заметите ошибку в любом месте этой книги или у вас появятся мысли по улучшению, вы можете написать мне [http://github.com/schacon/gitbook](http://github.com/schacon/gitbook) или модифицировав исходный код этой книги по адресу [http://github.com/schacon/gitbook](http://github.com/schacon/gitbook) , прислать мне патч или git:pull-request. ### Ссылки ### Большая часть этой книги была собрана из различных разрозненных источников. Если вы хотите посмотреть оригинальные ресурсы, посетите их и поблагодарите авторов: * [Git User Manual](http://www.kernel.org/pub/software/scm/git/docs/user-manual.html) * [The Git Tutorial](http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html) * [The Git Tutorial pt 2](http://www.kernel.org/pub/software/scm/git/docs/gittutorial-2.html) * ["My Git Workflow" blog post](http://osteele.com/archives/2008/05/my-git-workflow) #Введение ## Объектная модель Git ## ### Алгоритм SHA ### Вся информация, необходимая для представления истории проекта, находится в файлах с 40-символьным именем, которое выглядит примерно так: 6ff87c4664981e4397625791c8ea3bbb5f2279a3 В Git вы постоянно будете встречаться с этими 40-символьными строками. В каждом случае это имя является хешем содержимого объекта и генерируется с помощью криптографической хеш-функции по алгоритму SHA1. Для нас это значит, что практически невозможно найти два различных объекта с одним и тем же именем. Отсюда ряд преимуществ, среди которых: - Git может быстро определить, являются ли два объекта идентичными или нет, просто сравнив их имена. - Так как имена объектов вычисляются одним и тем же способом в каждом репозитории, то объекты с одинаковым содержимым в двух репозиториях будут сохранены под одним и тем же именем. - Git может выявлять ошибки чтения объекта, сравнив его имя и вычислив хеш содержимого по алгоритму SHA1. ### Объекты ### Каждый объект содержит три атрибута: **тип**, **размер** и **содержимое**. _Размер_ - это просто размер содержимого, которое зависит от типа объекта. Существуют четыре различных типа объекта: "blob", "tree", "commit" и "tag". - **"blob"** (сырые двоичные данные) используется для хранения содержимого файла, собственно говоря, это и есть сам файл. - **"tree"** (дерево) в основном похоже на директорию (папку в файловой системе) - это ссылки на множество других объектов типа tree и/или blob (т.е. файлов и/или поддиректорий). - **"commit"** (коммит, фиксация изменений) указывает на объект типа tree, который показывает, как выглядел проект в определенный момент времени. Он содержит метаинформацию об этом моменте времени, например, о времени создания/модификции этого объекта, авторе изменений, ссылку на предыдущую фиксацию, и т.д. - **"tag"** (тег, метка) - это способ указать определенную фиксацию (commit) как особый в некотором смысле. Обычно он используется для отметки некоторых объектов типа commit как особых релизов или что-то в этом роде. Почти все в Git построено на управлении этой простой структурой из четырех различных типов объектов. Это похоже на небольшую файловую систему, находящуюся поверх файловой системы компьютера. ### Отличия от SVN ### Важно заметить, что Git сильно отличается от большинства систем контроля версий, с которыми вы можете быть знакомы. Subversion, CVS, Perforce, Mercurial и прочие используют систему _хранения изменений_ - они хранят различия между двумя последовательными фиксациями. Git поступает по-другому - он сохраняет снимок (snapshot) всех файлов вашего проекта каждый раз, как вы фиксируете изменения. При использовании Git это очень важная для понимания концепция. ### Обект типа blob ### Обект типа blob обычно хранит содержимое файла. [fig:object-blob] Вы можете использовать linkgit:git-show[1] для просмотра содержимого любого объекта типа blob. Предположив, что что у нас есть хеш объекта типа blob, мы можем просмотреть его содержимое так: $ git show 6ff87c4664 Note that the only valid version of the GPL as far as this project is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. ... Объект типа blob есть ни что иное, как фрагмент двоичных данных. Он не имеет ни на что ссылок и не имеет никаких атрибутов, даже имени файла. Так как объект типа blob полностью определен своими данными, то если два файла в дереве директорий (или в различных версиях репозитория) имеют одинаковое содержимое, то они будут разделять один и тот же blob-объект. Этот объект полностью независим от своего расположения в дереве директорий, и переименование файла не изменит объект, с которым этот файл связан. ### Объект типа tree ### Объект типа tree - это объект, который хранит ссылки на blob-объекты и другие tree-объекты - и обычно он хранит содержимое директории или поддиректории. [fig:object-tree] Многогранная команда linkgit:git-show[1] может быть также использована для просмотра tree-объектов, но linkgit:git-ls-tree[1] покажет вам больше подробностей. Предположив, что у нас есть хеш tree-объекта, мы можем просмотреть его так: $ git ls-tree fb3a8bdd0ce 100644 blob 63c918c667fa005ff12ad89437f2fdc80926e21c .gitignore 100644 blob 5529b198e8d14decbe4ad99db3f7fb632de0439d .mailmap 100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3 COPYING 040000 tree 2fb783e477100ce076f6bf57e4a6f026013dc745 Documentation 100755 blob 3c0032cec592a765692234f1cba47dfdcc3a9200 GIT-VERSION-GEN 100644 blob 289b046a443c0647624607d471289b2c7dcd470b INSTALL 100644 blob 4eb463797adc693dc168b926b6932ff53f17d0b1 Makefile 100644 blob 548142c327a6790ff8821d67c2ee1eff7a656b52 README ... Как вы можете видеть, tree-объект содержит отсортированный по имени список элементов, каждый из которых содержит: права доступа, тип объекта, хеш и имя файла. Этот объект представляет одну директорию. Объект, на который ссылается tree-объект, может быть blob-объектом, представляющим содержимое файла, или другим tree-объектом, представляющим содержимое поддиректории. Так как объектам типа tree и blob имена даются с помощью хеш-функции по алгоритму SHA1 на основании их содержимого, то два tree-объекта будут иметь одинаковое SHA1-имя тогда и только тогда, когда их содержимое (рекурсивно включая содержимое поддиректорий) будет идентично. Это позволяет Git быстро определять различия между двумя родственными tree-объектами, так как он может отождествлять элементы с одинаковыми именами. (Замечание: в случае присутствия субмодулей, tree-объекты могут также содержать в себе commit-объекты. Смотрите раздел **Субмодули**.) Следует отметить, что файлы имеют права 644 или 755: git обращает внимание на бит исполнимости. ### Объекты типа commit ### Объект типа commit связывает физическое состояние tree-объекта с описанием того, как мы его получили и почему. [fig:object-commit] Вы можете использовать ключ --pretty=raw команд linkgit:git-show[1] или linkgit:git-log[1], чтобы просмотреть свой любимый commit-объект: $ git show -s --pretty=raw 2be7fcb476 commit 2be7fcb4764f2dbcee52635b91fedb1b3dcf7ab4 tree fb3a8bdd0ceddd019615af4d57a53f43d8cee2bf parent 257a84d9d02e90447b149af58b271c19405edb6a author Dave Watson 1187576872 -0400 committer Junio C Hamano 1187591163 -0700 Fix misspelling of 'suppress' in docs Signed-off-by: Junio C Hamano Как видите, commit-объект определяется с помощью: - **tree-объекта**: SHA1-имя tree-объекта (как определено ниже) представляет содержимое директории в определенный момент времени. - **parent(ы)** (родитель, родители): SHA1-имена некоторого числа commit-объектов, которое непосредственно представляет шаги, совершенные ранее в истории проекта. Пример, представленный выше, имеет одного родителя; слияние commit-объектов может иметь более одного. Commit-объект без родителя называется "корнем" и представляет собой исходную версию проекта. Каждый проект должен иметь хотя бы один корень. Проект также может иметь несколько корней, хотя это мало распространено (или не является хорошей идеей). - **author**: Имя человека, ответственного за это изменение, вместе с датой изменения. - **commiter**: имя человека, в действительности создавшего commit-объект, и дата, когда это произошло. Может отличаться от имени автора (author); например, если автор создал патч и прислал его другому человеку, который использовал патч для создания commit-объекта. - **comment**: комментарий, описывающий этот commit-объект. Заметьте, что commit-объект сам по себе не содержит какой-либо информации о совершенных изменениях: все изменения рассчитываются при сравнении содержания tree-объекта, относящегося к данному commit-объекту, с содержимым tree-объектов, являющимися его родителями. В частности, git не пытается записывать переименования файлов явно, хотя может определить, где существование подобного файла данных при изменении путей предполагает его переименование (См., например, -M опцию у linkgit:git-diff[1]). Обычно с помощью linkgit:git-commit[1] создаётся commit-объект, чьим родителем является HEAD, и чьё дерево создаётся из содержимого ранее хранимого индекса. ### Объектная модель ### Итак, когда мы посмотрели на 3 основных типа объектов (blob, tree и commit), давайте посмотрим как они взаимодействуют. Если бы у нас был простой проект со следующей структурой директорий: $>tree . |-- README `-- lib |-- inc | `-- tricks.rb `-- mylib.rb 2 директории, 3 файла И мы зафиксировали это в Git репозитории, что могло бы быть представлено как: [fig:objects-example] Как вы видите, мы создали объект **tree** для каждой директории (включая корневую) и объект **blob** для каждого файла. Также у нас есть объект **commit**, указывающий на корень. Таким образом мы можем проследить, как выглядел наш проект, когда он был впервые зафиксирован. ### Объект типа тег ### [fig:object-tag] Объект типа тег содержит имя объекта (называют просто "объект"), тип объекта, имя тега, имя человека ("tagger"), создавшего тег, и сообщение, которое может содержать подпись, как при использовании linkgit:git-cat-file[1]: $ git cat-file tag v1.5.0 object 437b1b20df4b356c9342dac8d38849f24ef44f27 type commit tag v1.5.0 tagger Junio C Hamano 1171411200 +0000 GIT 1.5.0 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQBF0lGqwMbZpPMRm5oRAuRiAJ9ohBLd7s2kqjkKlq1qqC57SbnmzQCdG4ui nLE/L9aUXdWeTFPron96DLA= =2E+0 -----END PGP SIGNATURE----- См. команду linkgit:git-tag[1], чтобы научиться создавать и сверять объекты типа тег. (Заметьте, что linkgit:git-tag[1] может быть также использован для создания "легких тегов", которые не являются объектами типа тег вообще, а просто представляют собой ссылки, чьи названия начинаются как "refs/tags/"). ### Git директория и рабочая директория ### ### Git директория ### Git директория - директория, которая хранит всю историю git'а и метаинформацию вашего проекта, включая все объекты (объекты типа commit, tree, blob, tag), все ссылки на различные ветки и т.д. Существует только одна Git директория на проект (противоположно одной на подкаталог как в SVN или CVS), и эта директория (по умолчанию, хотя необязательно) '.git' в корне вашего проекта. Если вы посмотрите на содержание этой директории, то увидите все необходимые файлы: $>tree -L 1 . |-- HEAD # указатель на вашу текущую ветку |-- config # ваши конфигурационные установки |-- description # описание вашего проекта |-- hooks/ # ловушки до/после действия |-- index # индексный файл (см. след. раздел) |-- logs/ # история, где были ваши ветки |-- objects/ # ваши объекты (объекты типа commit, tree, blob, tag) `-- refs/ # указатели на ваши ветки (там могут быть и другие файлы/директории, но на данный момент они несущественны) ### Рабочая директория ### "Рабочая директория" Git - каталог, содержащий текущую выписку файлов, над которыми вы работаете. Файлы в этом каталоге часто удаляются или перезаписываются Git-ом по мере того, как вы меняете ветки - это нормально. Вся ваша история хранится в Git директории; рабочая директория является просто временным местом выписок, где вы можете изменять файлы до следующей фиксации изменений. ### Индекс Git ### Индекс используется как промежуточная область между вашей рабочей директорией и репозиторием. Вы можете использовать индекс для создания множества изменений, которые хотите зафиксировать совместно. Когда вы создаёте коммит, фиксируется содержимое индекса, а не то что находится в вашей рабочей директории. ### Смотрим на индекс ### Самый легкий способ посмотреть, что находится в индексе - использовать команду linkgit:git-status[1]. При просмотре git status, вы увидите, какие файлы подготовлены к коммиту (находятся в индексе на данный момент), какие - модифицированы, но ещё не подготовлены, и которые не отслеживаются. $>git status #В ветке master #Ваша ветвь отстаёт от 'origin/master' на 11 коммитов и может быть синхронизирована методом fast-forward (без конфликтов). # #изменения, которые будут сохранены # (используйте "git reset HEAD ..." для отмены) # # изменён: daemon.c # #изменено, но не обновлено # (use "git add ..." to update what will be committed) # # modified: grep.c # modified: grep.h # # Неотслеживаемые файлы: # (use "git add ..." to include in what will be committed) # # blametree # blametree-init # git-gui/git-citool Если вы полностью удалили индекс, то, в общем, не потеряли никакой информации, пока у вас есть имя описываемого дерева. Вместе с тем, у вас уже должно быть довольно хорошее понимание основ того, что Git делает "за занавесом", и почему он немного отличается от большинства других систем контроля версий. Не волнуйтесь, если ещё не всё полностью понимаете; мы снова рассмотрим эти темы в последующих разделах. Теперь мы готовы приступить к установке, конфигурации и использованию Git. ### Установка Git ### # Впервые ### Установка из исходников ### Короче, на UNIX-подобных системах, вы можете загрузить исходный код Git из [Git Download Page](http://git-scm.com/download), и выполнив в консоле команды: $ make prefix=/usr all ;# с правами пользователя $ make prefix=/usr install ;# с правами администратора Вам понадобятся библиотеки [expat] (http://expat.sourceforge.net/), [curl](http://curl.linux-mirror.org), [zlib](http://www.zlib.net), and [openssl](http://www.openssl.org) хотя, за исключением, возможно, *expat*, они уже будут установлены в системе. ### Linux ### Если Вы используете Linux, то Вы легко можете установить Git, используя встроенную систему управления пакетами. $ yum install git-core $ apt-get install git-core Если это не сработает, .deb или .rpm пакеты можно скачать отсюда [RPM пакеты] (http://kernel.org/pub/software/scm/git/RPMS/) [Стабильная ветка Debs](http://www.backports.org/debian/pool/main/g/git-core/) Если Вы предпочитаете самостоятельно собирать ПО в Linux, то Вам может пригодиться вот эта статья: helpful: [Статья: Установка Git в Ubuntu] (http://chrisolsen.org/2008/03/10/installing-git-on-ubuntu/) ### Mac 10.4 ### В Мак 10.4 и 10.5 Git можно установить с использованием [MacPorts](http://www.macports.org/), который, в свою очередь можно установить [отсюда] (http://www.macports.org/install.php). После установки MacPorts все, что нужно сделать, это набрать $ sudo port install git-core Если Вы предпочитаете самостоятельно собирать ПО, Вам пригодятся вот эти статьи: [Статья: Установка Git в Tiger] (http://rails.wincent.com/wiki/Installing_Git_1.5.2.3_on_Mac_OS_X_Tiger) [Статья: Установка Git и git-svn в Tiger из исходного кода](http://larrytheliquid.com/2007/12/29/compiling-git-and-git-svn-on-osx-tiger/) ### Mac 10.5 ### В Mac OS X Leopard, кроме установки с использованием MacPorts, у Вас есть возможность использовать инсталлятор, который можно скачать отсюда [Git OSX инсталлятор] (http://code.google.com/p/git-osx-installer/downloads/list?can=3) Если же Вы предпочитаете самостоятельно собирать ПО, то следующие руководства могут быть особенно полезны: [Статья: Установка Git в OSX Leopard](http://solutions.treypiepmeier.com/2008/02/25/installing-git-on-os-x-leopard/) [Статья: установка Git в OS 10.5](http://dysinger.net/2007/12/30/installing-git-on-mac-os-x-105-leopard/) ### Windows ### Установка Git в Windows довольно проста. Просто скачайте и установите пакет [msysGit] (http://code.google.com/p/msysgit/downloads/list). В главе *Git в Windows* Вы можете найти скринкаст с демонстрацией установки и использования Git в Windows. ## Настройка и инициализация ## ### Конфигурирование Git ### Вероятно, первым делом вы захотите задать свое имя и адрес электронной почты, для того чтобы Git использовал их для подписи коммитов. $ git config --global user.name "Scott Chacon" $ git config --global user.email "schacon@gmail.com" Данные комманды создадут в вашем домашнем каталоге файл, который может быть использован в любом вашем проекте. По умолчанию, этот файл имеет имя *~/.gitconfig*, и в нем содержится следующая информация: [user] name = Scott Chacon email = schacon@gmail.com Если в каком-либо проекте вы захотите использовать другие настройки (например, использовать рабочий почтовый электронный адрес), вы можете запустить команду *git config* без опции *--global*, находясь в каталоге этого проекта. Данная команда добавит в файл *.git/config*, находящийся в корне этого проекта, секцию [user], аналогичную приведенной выше. # Базовое использование Git # ## Получение Git-репозитория ## Теперь, после всех приготовлений, нам нужен репозиторий Git. Мы можем получить его двумя путями: или клонировать существующий, или инициализировать новый, используя для этого файлы, не находящиеся под управлением системы контроля версий или пустой каталог. ### Клонирование Репозитория ### Чтобы получить копию проекта, Вам необходимо знать Git-ссылку на этот проект, другими словами, месторасположение репозитория. Git может работать поверх нескольких разных протоколов, так что ссылка может начинаться с ssh://, http(s)://, git://, или просто с имени пользователя (в этом случае Git выберет протокол ssh). Доступ к некоторым репозиториям можно осуществить при помощи сразу нескольких протоколов. Например, исходный код самого Git можно клонировать по git-протоколу: git clone git://git.kernel.org/pub/scm/git/git.git или по http: git clone http://www.kernel.org/pub/scm/git/git.git Протокол git:// более быстр и эффективен, но иногда фаерволы ограничивают нас протоколом http. В любом случае, в результате у Вас должна появится новая директория 'git', которая содержит весь исходный код Git вместе с историей. Практически, это полная копия того, что было на сервере. По умолчанию, в качестве имени директории, в которую Git поместил склонированный исходный код, будет использована часть URL клонируемого проекта, идущая непосредственно перед '.git' (например, команда *git clone http://git.kernel.org/linux/kernel/git/torvalds/linux-2.6.git* создаст новую директорию с именем 'linux-2.6') ### Инициализация нового репозитория ### Допутим, у вас есть tar-архив project.tar.gz с вашими наработками. Вы можете поместить его в систему контроля версий Git следующим образом: $ tar xzf project.tar.gz $ cd project $ git init Git выведет Initialized empty Git repository in .git/ Вы инициализировали рабочую директорию. Вы могли заметить, что в ней была создана новая директория '.git'. [gitcast:c1_init](GitCast #1 - настройка, инициализация и клонирование) ## Типичная работа ## Изменяете какие-то файлы, затем добавляете их в индекс: $ git add file1 file2 file3 Все готово для фиксации изменений. Чтобы узнать, что именно Вы собираетесь закоммитить, наберите linkgit:git-diff[1] с опцией --cached option: $ git diff --cached (Без этой опции ("--cached"), linkgit:git-diff[1] покажет все изменения, которые вы еще не добавили в индекс.) Вы также можете получить краткий обзор текущего состояния командой linkgit:git-status[1]: $ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # modified: file1 # modified: file2 # modified: file3 # Если необходимо внести еще какие-либо изменения, сделайте это, а затем добавьте их в индекс. Теперь можно зафиксировать изменения следующей командой: $ git commit Вам будет предложено ввести описание изменений, после чего они будут записаны, породив таким образом новую версию проекта. Как вариант, вместо выполнения команды "git add" перед фиксацией изменений, Вы можете сразу выполнить команду: $ git commit -a в результате работы которой, все измененные (но не вновь созданные) файлы попадут сначала в индекс, а затем и в фиксацию изменений, за один шаг. Пару слов о том как составлять описание фиксаций (коммитов). Хорошим тоном будет начать описание с одной короткой (до 50 знаков) строки, в которой кратко изложить суть изменений, далее, после пустой строки, дописать подробное описание. При отправке оповещений об изменениях по электронной почте, первая строка такого описания будет использована в качестве темы, а подробное описание станет телом письма. #### Git отслеживает содержимое, а не файлы #### Во многих системах контроля версий существует команда "добавить" ("add"), которая указывает системе начать следить за изменениями в указанном файле. В Git команда "add" и проще и мощнее: 'git add' одновременно используется для добавления и новых и измененных файлов. В обоих случаях она создает слепок этих файлов и помещает его в индекс, готовый стать частью фиксации. [gitcast:c2_normal_workflow]("GitCast #2: Normal Workflow") ## Основы ветвления и слияния ## Один git-репозиторий может содержать множество веток разработки. Чтобы создать новую ветвь с названием "experimental", используйте: $ git branch experimental Если вы выполните: $ git branch то получите список всех существующих веток: experimental * master Ветвь "experimental", это та, которую вы только что создали, а ветвь "master" была создана для вас автоматически. Символ звёздочки указывает на текущую ветку. Наберите: $ git checkout experimental чтобы переключиться на экспериментальную ветку. Теперь отредактируйте файл, подтвердите изменения и переключитесь на главную ветвь: (редактируем файл) $ git commit -a $ git checkout master Удостоверьтесь, что сделанные изменения сейчас не видны, так как они были сделаны в экспериментальной ветке, а вы уже на главной. Вы можете внести другие изменения в главной (master) ветке: (редактируем файл) $ git commit -a На данный момент, две ветви разошлись с разными изменениями, сделанными в каждой из них. Чтобы cлить изменения из экспериментальной ветви с главной, выполните: $ git merge experimental Если изменения не приведут к конфликту, всё будет сделано. Если обнаружатся конфликты, в проблемных файлах будут оставлены отметки, указывающие на конфликт; $ git diff покажет их. Когда вы отредактируете файлы, разрешив конфликты $ git commit -a зафиксирует слияние. Под конец, $ gitk покажет красивое графическое представление истории репозитория. После этого вы можете удалить экспериментальную ветку командой: $ git branch -d experimental Данная команда проверит, что изменения в экспериментальной ветке уже есть в текущей. Если вы вели разработку в ветке "crazy-idea", но потом забыли о ней, то можете всегда удалить её, скомандовав: $ git branch -D crazy-idea Ведение веток разработки "дешево" и просто, так что это отличный способ опробовать какую-либо идею. ### Как производить слияние ### Вы можете вновь объединить две разошедшиеся ветки разработки, используя linkgit:git-merge[1]: $ git merge branchname совмещает изменения, сделанные в ветке branchname в текущую ветку. Если существуют конфликты — например, если один и тот же файл изменён двумя разными способами и в удалённой ветке, и в локальной ветке — вы будете предупреждены; вывод команды может выглядеть примерно так: $ git merge next 100% (4/4) done Auto-merged file.txt CONFLICT (content): Merge conflict in file.txt Автоматическое слияние неудачно; исправьте конфликты и подтвердите результат. Отметки о конфликтах установлены в проблемных файлах, и после того как вы разрешите конфликты вручную, вы можете обновить индекс с новым содержанием и запустить git commit, как вы обычно делаете, когда изменяете файл. Если вы проверите полученную в итоге фиксацию с помощью gitk, вы увидите, что у неё два родителя: один, указывающий на верхушку текущей ветки, и другой — на верхушку другой ветки. ### Разрешение конфликтов ### Если слияние невозможно провести автоматически, Git оставляет индекс и рабочее дерево в особом состоянии, которое позволяет получить всю информацию, необходимую для разрешения конфликтов. Файлы с конфликтами слияния отмечаются в индексе особым образом, так что, пока вы не устраните проблему и не обновите индекс, linkgit:git-commit[1] будет выдавать ошибку $ git commit file.txt: needs merge Также, linkgit:git-status[1] будет отображать такие файлы как "unmerged" (не прошедшие слияние), а в файлы с конфликтами будут добавлены маркеры конфликтов, например: <<<<<<< HEAD:file.txt Hello world ======= Goodbye >>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt Всё, что вам необходимо сделать, это отредактировать файлы для устранения конфликтов, а затем выполнить $ git add file.txt $ git commit Заметьте, что сообщение о фиксации (коммите) уже будет содержать некоторую информацию о слиянии. Вы можете использовать это сообщение по умолчанию неизменённым или добавить дополнительные комментарии по своему усмотрению. Описанного выше вполне достаточно для разрешения простого слияния. Но git предоставляет больше информации для помощи в устранении конфликтов: ### Отклонение слияния ### Если вы застрянете и, забросив всё, решите сдаться, вы всегда можете вернуться к состоянию, которое было до слияния с помощью команды $ git reset --hard HEAD Или, если вы уже зафиксировали слияние, которое хотите отменить, $ git reset --hard ORIG_HEAD Тем не менее, эта последняя команда может быть опасна в некоторых случаях - никогда не отменяйте коммита, если он находится в состоянии слияния с другой веткой, т.к., поступив подобным образом, можно привести в беспорядок будущие слияния. ### Перемотка слияний вперед ### Существует особый случай, если не учитывать вышеупомянутый, когда поступают иначе. Обычно результатом слияния является объект типа commit с двумя родителями, соответствующими двум линиям разработки, участвовавшим в слиянии. Тем не менее, если текущая ветвь не разошлась с другой (так что каждая фиксация, существующая в текущей ветке, уже содержится и в другой), то git просто осуществляет "перемотку вперед"; головная ревизия текущей ветки перемещается вперёд, на место головы присоединённой ветки, без создания каких либо новых фиксаций. [gitcast:c6-branch-merge]("GitCast #6: Branching and Merging") ### Просмотр истории - Лог Git ### Команда linkgit:git-log[1] может показать списки коммитов (фиксаций). Сама по себе, она отображает все фиксации, доступные из родительского коммита, но вы можете создать более специфические запросы: $ git log v2.5.. # фиксации после (недоступные из) v2.5 $ git log test..master # фиксации, доступные из master, но не из test $ git log master..test # фиксации, доступные из test, но не из master $ git log master...test # фиксации, доступные или из test, или # из master, но не из обоих. $ git log --since="2 weeks ago" # фиксации за последние 2 недели $ git log Makefile # фиксации, модифицирующие Makefile $ git log fs/ # фиксации, которые изменяют какой-либо файл в каталоге fs/ $ git log -S'foo()' # фиксации, которые добавляют или удаляют какие-либо данные из файла, # совпадающие со строкой 'foo()' $ git log --no-merges #не показывает фиксации слияния И, конечно, вы можете комбинировать их (команды); данная команда находит фиксации после v2.5, которые затрагивают Makefile или любой файл в директории fs: $ git log v2.5.. Makefile fs/ Лог Git покажет список всех фиксаций, начиная с более новых, которые соответствуют аргументам, переданным команде log. commit f491239170cb1463c7c3cd970862d6de636ba787 Author: Matt McCutchen Date: Thu Aug 14 13:37:41 2008 -0400 git format-patch documentation: clarify what --cover-letter does commit 7950659dc9ef7f2b50b18010622299c508bfdfc3 Author: Eric Raible Date: Thu Aug 14 10:12:54 2008 -0700 bash completion: 'git apply' should use 'fix' not 'strip' Bring completion up to date with the man page. Вы можете также запросить для просмотра патчи: $ git log -p commit da9973c6f9600d90e64aac647f3ed22dfd692f70 Author: Robert Schiele Date: Mon Aug 18 16:17:04 2008 +0200 adapt git-cvsserver manpage to dash-free syntax diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index c2d3c90..785779e 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -11,7 +11,7 @@ SYNOPSIS SSH: [verse] -export CVS_SERVER=git-cvsserver +export CVS_SERVER="git cvsserver" 'cvs' -d :ext:user@server/path/repo.git co pserver (/etc/inetd.conf): ### Статистика по коммитам ### Если вы добавите опцию --stat к команде 'git log', то вам будет показано, какие файлы были изменены и как много сток было добавлено и удалено в каждом. $ git log --stat commit dba9194a49452b5f093b96872e19c91b50e526aa Автор: Junio C Hamano Date: Sun Aug 17 15:44:11 2008 -0700 Start 1.6.0.X maintenance series Documentation/RelNotes-1.6.0.1.txt | 15 +++++++++++++++ RelNotes | 2 +- 2 файла изменены, 16 вставок(+), 1 удаление(-) ### Форматирование логов ### Вы также можете применять форматирование к выводу логов практически всегда, когда хотите. Опция '--pretty' может принимать номер предустановленного формата, такого как 'oneline': $ git log --pretty=oneline a6b444f570558a5f31ab508dc2a24dc34773825f dammit, this is the second time this has reverted 49d77f72783e4e9f12d1bbcacc45e7a15c800240 modified index to create refs/heads if it is not 9764edd90cf9a423c9698a2f1e814f16f0111238 Add diff-lcs dependency e1ba1e3ca83d53a2f16b39c453fad33380f8d1cc Add dependency for Open4 0f87b4d9020fff756c18323106b3fd4e2f422135 merged recent changes: * accepts relative alt pat f0ce7d5979dfb0f415799d086e14a8d2f9653300 updated the Manifest file или вы можете использовать 'укороченный' формат: $ git log --pretty=short commit a6b444f570558a5f31ab508dc2a24dc34773825f Автор: Scott Chacon dammit, this is the second time this has reverted commit 49d77f72783e4e9f12d1bbcacc45e7a15c800240 Author: Scott Chacon modified index to create refs/heads if it is not there commit 9764edd90cf9a423c9698a2f1e814f16f0111238 Author: Hans Engel Add diff-lcs dependency Вы также можете использовать форматы 'medium', 'full', 'fuller', 'email' или 'raw'. Если среди них отсутствует именно то, что вам нужно, вы можете создать свой собственный с помощью опции '--pretty=format' (см. linkgit:git-log[1] документацию для всех опций форматирования). $ git log --pretty=format:'%h was %an, %ar, message: %s' a6b444f was Scott Chacon, 5 days ago, message: dammit, this is the second time this has re 49d77f7 was Scott Chacon, 8 days ago, message: modified index to create refs/heads if it i 9764edd was Hans Engel, 11 days ago, message: Add diff-lcs dependency e1ba1e3 was Hans Engel, 11 days ago, message: Add dependency for Open4 0f87b4d was Scott Chacon, 12 days ago, message: merged recent changes: Другая интересная вещь, которую вы можете сделать, это визуализировать граф фиксаций при помощи опции '--graph': $ git log --pretty=format:'%h : %s' --graph * 2d3acf9 : ignore errors from SIGCHLD on trap * 5e3ee11 : Merge branch 'master' of git://github.com/dustin/grit |\ | * 420eac9 : Added a method for getting the current branch. * | 30e367c : timeout code and tests * | 5a09431 : add timeout protection to grit * | e1193f8 : support for heads with slashes in them |/ * d6016bc : require time for xmlschema Это создаст довольно милое ASCII представление истории фиксаций. ### Упорядочивание лога ### Вы также можете просматривать записи лога в нескольких других последовательностях. Заметьте, что лог git начинается с самой недавней фиксации и проходит в обратную сторону по родителям; тем не менее, т.к. история git может содержать несколько независимых линий разработки, определённый порядок, в каком фиксации перечислены, может быть произвольным. Если вы желаете указать определённый порядок, вы можете добавить опцию порядка к команде git log. По умолчанию, фиксации показываются в обратном хронологическом порядке. Несмотря на это, вы также можете указать '--topo-order', что даст возможность просмотреть фиксации (коммиты) в топологическом порядке (т.е. коммиты-приемники (потомки) показываются до своих родителей). Если посмотреть лог репозитория Grit в топологическом порядке, то можно видеть, что производственные строки сгруппированы вместе. $ git log --pretty=format:'%h : %s' --topo-order --graph * 4a904d7 : Merge branch 'idx2' |\ | * dfeffce : merged in bryces changes and fixed some testing issues | |\ | | * 23f4ecf : Clarify how to get a full count out of Repo#commits | | * 9d6d250 : Appropriate time-zone test fix from halorgium | | |\ | | | * cec36f7 : Fix the to_hash test to run in US/Pacific time | | * | decfe7b : fixed manifest and grit.rb to make correct gemspec | | * | cd27d57 : added lib/grit/commit_stats.rb to the big list o' files | | * | 823a9d9 : cleared out errors by adding in Grit::Git#run method | | * | 4eb3bf0 : resolved merge conflicts, hopefully amicably | | |\ \ | | | * | d065e76 : empty commit to push project to runcoderun | | | * | 3fa3284 : whitespace | | | * | d01cffd : whitespace | | | * | 7c74272 : oops, update version here too | | | * | 13f8cc3 : push 0.8.3 | | | * | 06bae5a : capture stderr and log it if debug is true when running commands | | | * | 0b5bedf : update history | | | * | d40e1f0 : some docs | | | * | ef8a23c : update gemspec to include the newly added files to manifest | | | * | 15dd347 : add missing files to manifest; add grit test | | | * | 3dabb6a : allow sending debug messages to a user defined logger if provided; tes | | | * | eac1c37 : pull out the date in this assertion and compare as xmlschemaw, to avoi | | | * | 0a7d387 : Removed debug print. | | | * | 4d6b69c : Fixed to close opened file description. Вы также можете использовать '--date-order', которая упорядочивает коммиты по дате их фиксации. Эта опция аналогична '--topo-order' в том смысле, что родитель не предшествует своему потомку, но в остальном всё упорядочено по меткам времени фиксаций. Вы можете видеть, что производственные строки здесь не сгруппированы, а разбросаны в порядке, в котором происходила параллельная разработка: $ git log --pretty=format:'%h : %s' --date-order --graph * 4a904d7 : Merge branch 'idx2' |\ * | 81a3e0d : updated packfile code to recognize index v2 | * dfeffce : merged in bryces changes and fixed some testing issues | |\ | * | c615d80 : fixed a log issue |/ / | * 23f4ecf : Clarify how to get a full count out of Repo#commits | * 9d6d250 : Appropriate time-zone test fix from halorgium | |\ | * | decfe7b : fixed manifest and grit.rb to make correct gemspec | * | cd27d57 : added lib/grit/commit_stats.rb to the big list o' file | * | 823a9d9 : cleared out errors by adding in Grit::Git#run method | * | 4eb3bf0 : resolved merge conflicts, hopefully amicably | |\ \ | * | | ba23640 : Fix CommitDb errors in test (was this the right fix? | * | | 4d8873e : test_commit no longer fails if you're not in PDT | * | | b3285ad : Use the appropriate method to find a first occurrenc | * | | 44dda6c : more cleanly accept separate options for initializin | * | | 839ba9f : needed to be able to ask Repo.new to work with a bar | | * | d065e76 : empty commit to push project to runcoderun * | | | 791ec6b : updated grit gemspec * | | | 756a947 : including code from github updates | | * | 3fa3284 : whitespace | | * | d01cffd : whitespace | * | | a0e4a3d : updated grit gemspec | * | | 7569d0d : including code from github updates Наконец, вы можете изменить порядок лога на обратный при помощи опции '--reverse'. [gitcast:c4-git-log]("GitCast #4: Git Log") ## Сравнение фиксаций - Git Diff ## Вы можете запрашивать изменения между любыми двумя версиями вашего проекта, используя linkgit:git-diff[1]: $ git diff master..test Это создаст diff (разность) между концами двух ветвей. Если вы желаете найти разность, начиная с их общего предка до test, вы можете использовать три точки вместо двух: $ git diff master...test linkgit:git-diff[1] невероятно полезна для определения изменений, произошедших между любыми двумя точками в истории проекта, или для просмотра того, что люди хотят реализовать в новых ветках, и т.д.. ### Что вы будете фиксировать ### Вы часто будете пользоваться linkgit:git-diff[1], чтобы узнать об отличиях между вашей последней фиксацией, вашим индексом и вашей текущей рабочей директорией. Элементарное использование - просто набрать $ git diff Это покажет вам изменения в рабочей директории, которые ещё не подготовлены к следующей фиксации. Если вы хотите увидить то, что _уже_ подготовили для следующего коммита, вы можете запустить $ git diff --cached что покажет разницу между индексом и вашей последней фиксацией; что вы будете фиксировать, если наберёте "git commit" без опции "-a". Наконец, вы можете набрать $ git diff HEAD что показывает изменения, произошедшие в рабочей директории со времени совершения последней фиксации; что вы бы зафиксировали, если набрали "git commit -a'. ### Другие опции diff ### Если вы желаете посмотреть, насколько текущая рабочая директория отличается от состояния проекта в другой ветке, вы можете набрать $ git diff test Это покажет, чем отличается текущая директория от снимка ветви 'test'. Вы также можете ограничить сравнение определённым файлом или подкаталогом, добавив *ограничитель пути*: $ git diff HEAD -- ./lib Эта команда покажет все изменения между вашей текущей рабочей директорией и последней фиксацией (или, точнее, верхушкой текущей ветки), ограничивая сравнение файлами из подкаталога 'lib'. Если вы не хотите видеть весь патч, добавьте опцию '--stat', что ограничит выходные данные файлами, которые изменились, вместе с небольшим текстовым графиком, изображающим количество изменённых строк в каждом файле. $>git diff --stat layout/book_index_template.html | 8 ++- text/05_Installing_Git/0_Source.markdown | 14 ++++++ text/05_Installing_Git/1_Linux.markdown | 17 +++++++ text/05_Installing_Git/2_Mac_104.markdown | 11 +++++ text/05_Installing_Git/3_Mac_105.markdown | 8 ++++ text/05_Installing_Git/4_Windows.markdown | 7 +++ .../1_Getting_a_Git_Repo.markdown | 7 +++- .../0_ Comparing_Commits_Git_Diff.markdown | 45 +++++++++++++++++++- .../0_ Hosting_Git_gitweb_repoorcz_github.markdown | 4 +- 9 files changed, 115 insertions(+), 6 deletions(-) Иногда это облегчает восприятие изменений в целом, чтобы встряхнуть вашу память. ## Распределённый процесс разработки ## Предположим, что Алиса начала новый проект с репозиторием git в /home/alice/project, и Боб, у которого есть домашний каталог на том же компьютере, хочет внести свой вклад. Боб начинает с: $ git clone /home/alice/project myrepo Это создаёт новую директорию "myrepo", содержащую копию репозитория Алисы. Копия имеет ту же основу, что и оригинальный проект, обладая своей собственной копией истории оригинала. Тогда Боб делает некоторые изменения и фиксирует их: (редактировать файлы) $ git commit -a (повторить при необходимости) Когда он готов, он говорит Алисе запросить изменения из репозитория /home/bob/myrepo. Она делает это с помощью: $ cd /home/alice/project $ git pull /home/bob/myrepo master Это вызывает слияние изменений из ветки "master" Боба с текущей веткой Алисы. Если Алиса, тем временем, внесла свои изменения, тогда ей придётся исправить возникающие конфликты вручную. (Заметьте, что аргумент "master" в вышеприведённой команде на самом деле не является необходимым, т.к. подразумевается по умолчанию.) Таким образом команда "pull" выполняет две операции: выбирает изменения из удалённой ветки и присоединяет их к текущей ветке. Когда вы работаете в тесно связанной группе, то часто приходится взаимодействовать с тем же самым репозиторием снова и снова. Это можно облегчить, задав сокращение 'удалённому' репозиторию : $ git remote add bob /home/bob/myrepo Поступив таким образом, Алиса может совершить первую операцию используя команду "git fetch" без слияния со своей веткой, набрав: $ git fetch bob По сравнению с обычной формой, когда Алиса создаёт выборку от Боба, используя сокращение, заданное с помощью `git remote`, выборка сохраняется в удалённой ветке, в данном случае `bob/master`. Поэтому, после этого: $ git log -p master..bob/master показывает список всех изменений, совершённых Бобом с момента создания ветки от мастер-ветки Алисы. После осмотра этих изменений, Алиса может объединить их с её мастер-веткой: $ git merge bob/master Это `слияние` также может быть совершено, 'запрашивая изменения из её удалённой отслеживаемой ветки', таким образом: $ git pull . remotes/bob/master Заметьте, что git pull всегда создаёт слияние с текущей веткой, независимо от того, что ещё задано в командной строке. Позднее Боб может обновить свой репозиторий последними изменениями Алисы, набрав: $ git pull Заметьте, что ему не надо задавать путь до репозитория Алисы; когда Боб клонировал её репозиторий, git сохранил его местонахождение в конфигурационных файлах репозитория, и это место расположения используется для осуществления выборок: $ git config --get remote.origin.url /home/alice/project (Полную конфигурацию, созданную git-копией, можно увидеть, набрав "git config -l"; man страница команды linkgit:git-config[1] объясняет значение каждой опции.) Git также хранит первоначальную копию мастер-ветки Алисы под названием "origin/master": $ git branch -r origin/master Если позднее Боб решит работать с другого хоста, он всё ещё сможет создавать копии и выборки, используя протокол ssh: $ git clone alice.org:/home/alice/project myrepo Как вариант, git имеет собственный протокол, или может использовать rsync или http; см. linkgit:git-pull[1]. Git также может быть использован в CVS-подобном режиме, с центральным репозиторием, куда пользователи отправляют свои изменения; см. linkgit:git-push[1] и linkgit:gitcvs-migration[1]. ###Открытые репозитории git ### Другой способ отправки изменений - сказать владельцу проекта забрать изменения из вашего репозитория, используя linkgit:git-pull[1]. Таким способом можно получить дополнения из репозитория "main", что также хорошо работает в обратном направлении. Если вы и владелец проекта имеете учётные записи на одном компьютере, то вы можете брать изменения прямо из репозиториев друг друга; команды, принимающие в качестве аргументов URL репозитория, также примут имя локального каталога: $ git clone /path/to/repository $ git pull /path/to/other/repository или ssh URL: $ git clone ssh://yourhost/~you/repository Для проектов с несколькими разработчиками, или для синхронизации нескольких закрытых репозиториев, это может быть то, что надо. Тем не менее, обычно создают отдельный открытый репозиторий (обычно на другом хосте), откуда можно брать изменения. Обычно это более удобно и позволяет отделить текущую личную работу от публично видимой. Вы будете продолжать свою ежедневную работу в личном репозитории, но периодически отправлять изменения из вашего закрытого репозитория в открытый, тем самым позволяя другим разработчикам брать оттуда дополнения. Итак, поток изменений в ситуации, где имеется один разработчик с открытым репозиторием, выглядит так: вы отправляете ваш закрытый репозиторий ------------------> ваш открытый репозиторий ^ | | | | вы забираете | они забирают | | | | | они отправляют V их открытый репозиторий <------------------- их репозиторий ### Отправление изменений в открытый репозиторий ### Заметьте, что экспорт через http или git позволяет другим разработчикам брать ваши последние изменения, но не даёт права записи. Для этого вам потребуется обновить открытый репозиторий последними изменениями, созданными в закрытом репозитории. Самый легкий способ сделать это - использовать linkgit:git-push[1] и ssh; чтобы обновить удаленную ветку "master" до состояния вашей ветки "master", введите: $ git push ssh://yourserver.com/~you/proj.git master:master или просто $ git push ssh://yourserver.com/~you/proj.git master Как и в случае с git-fetch, git-push будет жаловаться, если это не приведёт к "перемотке вперёд"; см. следующий раздел для решения этой проблемы. Заметьте, что целью "push" обычно является пустой репозиторий. Вы также можете отправлять изменения в репозиторий, имеющий отлаженное рабочее дерево, но оно не будет обновлено. Это может привести к неожиданным результатам, если ветка, куда вы отправляете обновления, является отлаживаемой. Также как и с git-fetch, вы можете установить некоторые опции, чтобы меньше печатать; так, например, после $ cat >>.git/config <.url, branch..remote, и remote..push в linkgit:git-config[1]. ### Что делать если команда push не сработала ### Если push не приведёт удаленную ветку к "перемотке вперёд", то выдаст следующую ошибку: error: remote 'refs/heads/master' is not an ancestor of (удалённый 'refs/heads/master' не является предком) local 'refs/heads/master' (локального 'refs/heads/master') Возможно у вас не последняя версия и нужно сначала обновится? error: failed to push to 'ssh://yourserver.com/~you/proj.git' (ошибка: не удалось закачать в 'ssh://yourserver.com/~you/proj.git') Это может произойти, например, если вы: - используете `git-reset --hard` чтобы удалить уже опубликованные фиксации, или - используете `git-commit --amend` чтобы заменить уже опубликованные фиксации, или - используете `git-rebase` чтобы перебазировать уже опубликованные фиксации. Вы можете заставить git-push совершить обновление, добавив перед именем ветки знак плюс: $ git push ssh://yourserver.com/~you/proj.git +master Обычно, когда голова ветки в открытом репозитории модифицируется, то это происходит таким образом, чтобы голова указывала на потомка фиксации, на которую она указывала ранее. Совершая push, в данном случае вы нарушаете такое поведение. Тем не менее, это типичное поведение людей, которым нужен простой способ опубликовать находящийся в работе ряд исправлений и это допустимый компромисс, если вы уведомите других разработчиков о том, как вы намерены управлять веткой. Также возможно, что отправление изменений потерпит неудачу, когда другие люди имеют право совершать push в тот же репозиторий. В таком случае, правильное решение - повторить отправку, предварительно обновив свою ветку изменениями из главной ветки: или используя pull, или fetch с последующим rebase; см. следующий раздел и linkgit:gitcvs-migration[7]. [gitcast:c8-dist-workflow]("GitCast #8: Распределённый трудовой процесс") ### Git Tag ### ### Легкие теги ### Мы можем создать тег, ссылающийся на конкретную фиксацию, запустив linkgit:git-tag[1] без аргументов. $ git tag stable-1 1b2e1d63ff После этого мы можем использовать stable-1 для обращения к фиксации 1b2e1d63ff. Это создаёт "легкий" тег, по правде говоря, ветку, которая никогда не перемещается. Если вы захотите также включить в состав тега комментарий, и, возможно, криптографически подписать, тогда вместо этого следует создать объект типа тег. ### Объекты типа Тег ### Если один из **-a**, **-s**, or **-u ** передан, команда создаёт объект типа тег, и нуждается в сообщении. За исключением случаев, когда заданы -m или -F , запускается текстовый редактор для набора пользователем комментария для тега. Когда это происходит, новый объект добавляется в базу данных объектов Git и ссылка тега указывает на тот _объект типа тег_, а не на саму фиксацию. Сильная сторона такого подхода в том, что вы можете подписать тег, чтобы в будущем удостовериться в корректности фиксации. Вы можете создать объект типа тег следующим образом: $ git tag -a stable-1 1b2e1d63ff Фактически, можно присвоить тег любому объекту, но обычно это совершается для commit-объектов. (В исходных текстах ядра Linux первый tag-объект ссылается на дерево, а не на фиксацию) ### Подписанные теги ### Если у вас есть установка GPG ключей, вы с легкостью можете создать подписанные теги. Для начала, вы наверное захотите указать ID ключа в вашем _.git/config_ или _~.gitconfig_ файле. [user] signingkey = Вы также можете установить его с помощью $ git config (--global) user.signingkey Теперь вы можете создать подписанный тег просто заменив **-a** на **-s**. $ git tag -s stable-1 1b2e1d63ff Если вы не указали своего GPG ключа в конфигурационном файле, вы можете достичь того же результата следующим образом: $ git tag -u stable-1 1b2e1d63ff ## Игнорирование файлов ## Проект будет часто генерировать файлы, которые вы не захотите отслеживать при помощи git. Обычно это файлы, сгенерированные во время сборки или временные резервные копии, созданные текстовым редактором. Конечно, 'не' слежение за файлами это просто 'не' совершение "`git-add`" над ними. Но это быстро начинает раздражать, т.к. эти разбросанные неотслеживаемые файлы делают "`git add .`" и"`git commit -a`" практически бесполезными, и они показываются в выводе команды "`git status`". Вы можете сказать git, чтобы он игнорировал конкретные файлы, создав файл .gitignore на верхнем уровне вашей рабочей директории со следующим содержимым: # Строки, начинающиеся с '#' - комментарии. # Игнорировать любой файл foo.txt. foo.txt # Игнорировать (генерируемые) html файлы, *.html # except foo.html which is maintained by hand. (исключая foo.html который поддерживается вручную) !foo.html # Ignore objects and archives. (игнорировать объекты и архивы) *.[oa] Просмотрите linkgit:gitignore[5] для детального уточнения синтаксиса. Вы также можете поместить файлы .gitignore в другие директории в рабочем дереве, и они будут применяться к этим директориям и их под-директориям. Файлы `.gitignore` могут быть добавлены в ваш репозиторий, точно так же, как и любые другие файлы (просто запустите `git add .gitignore` и `git commit`, как обычно), что удобно, когда игнорирование конкретных файлов (такие как образцы для выходные файлов при сборке) так же может иметь смысл для других пользователей, которые используют ваш репозиторий. Если вы хотите, чтобы образцы исключений влияли только на определенные репозитории (вместо всех репозиториев для данного проекта), вы можете вместо этого положить их в файл в вашем репозитории с именем .git/info/exclude или в любой файл, указанный в конфигаруционной переменной`core.excludesfile`. Некоторые команды Git также могут принимать образцы исключений непосредственно в командной строке. Смотрите linkgit:gitignore[5] для подробностей. # Средний уровень использования ## Перебазирование ## Предположим, что вы создаёте ветку "mywork" на наблюдаемой удалённой ветке "origin". $ git checkout -b mywork origin [fig:rebase0] Теперь, поработав, вы создаёте две новые фиксации. $ vi file.txt $ git commit $ vi otherfile.txt $ git commit ... Тем временем, кто-то также создаёт две новые фиксации на ветке "origin". Это значит, что обе ветки 'origin' и 'mywork' продвинулись вперёд, т.е. работа стала разветвляться. [fig:rebase1] На данном этапе, вы могли бы использовать "pull" для обратного слияния изменений; результат создал бы такую фиксацию слияния: [fig:rebase2] Тем не менее, если вы предпочитаете хранить историю в mywork как простой ряд фиксаций без слияний, вместо этого вы можете использовать linkgit:git-rebase[1]: $ git checkout mywork $ git rebase origin Это уберёт каждую вашу фиксацию из mywork, временно сохраняя их как патчи (в директории ".git/rebase"); обновит mywork до состояния последней версии origin, затем применит к полученному коду каждый из сохранённых патчей. [fig:rebase3] Когда ссылка ('mywork') будет обновлена до состояния только что созданного commit-объекта, ваши прежние фиксации будут забыты. Они, скорее всего, будут убраны, если вы произведёте упрощённую сборку мусора. (см. linkgit:git-gc[1]) [fig:rebase4] Теперь мы можем посмотреть разницу истории использования merge и использования rebase: [fig:rebase5] В процессе перебазирования могут быть обнаружены конфликты. В таком случае процесс будет остановлен и вам будет предоставлена возможность исправить конфликты; исправив конфликты, используйте "git-add", чтобы обновить индекс их содержанием, и тогда, вместо использования git-commit, наберите $ git rebase --continue и git продолжит применение остальных патчей. В любой момент вы можете использовать опцию `--abort` чтобы прекратить этот процесс и возвратить mywork к состоянию до процесса перебазирования: $ git rebase --abort [gitcast:c7-rebase]("GitCast #7: Перебазирование") ## Интерактивное перебазирование ## Вы также можете осуществить перебазирование интерактивно. Это часто используется чтобы переписать ваши собственные commit-объекты перед их отправлением куда-либо. Это удобный способ деления, слияния или переупорядочивания фиксаций перед совместным использованием с другими людьми. Вы также можете использовать перебазирование, чтобы привести в порядок фиксации, забранные вами от кого-либо, при применении их локально. Если у вас есть несколько фиксаций, которые вы хотели бы каким-то образом модифицировать во время перебазирования, вы можете запустить интерактивный режим, передав '-i' или '--interactive' команде 'git rebase'. $ git rebase -i origin/master Это вызовет интерактивный режим процесса перебазировки для всех фиксаций, созданных со времени последнего отправления (или слияния из базового репозитория (origin)). Чтобы узнать о предварительных фиксациях, вы можете использовать команду log следующим образом: $ git log github/master.. Когда вы используете команду 'rebase -i', откроется выбранный вами текстовый редактор с подобным содержанием: pick fc62e55 added file_size pick 9824bf4 fixed little thing pick 21d80a5 added number to log pick 76b9da6 added the apply command pick c264051 Revert "added file_size" - not implemented correctly Rebase f408319..b04dc3d onto f408319 # # Команды: # p, pick = использовать коммит # e, edit = использовать коммит предварительно внеся поправки # s, squash = использовать коммит, но соединить его с предыдущей фиксацией # # Если вы удалите здесь строку ЭТА ФИКСАЦИЯ БУДЕТ ПОТЕРЯНА. # Тем не менее, если вы удалите всё, перебазировка будет отменена. # Это значит, что существует 5 фиксаций со времени совершения вами последней отправки изменений, и это даёт вам одну строку на фиксацию со следующим форматом: (действие)(неполный sha-код)(короткое сообщение фиксации) Теперь вы можете изменить действие (которое по умолчанию 'pick') на 'edit' или 'squash', или оставить его как 'pick'. Вы также можете изменить порядок фиксаций, переместив строки по вашему желанию. Затем, когда вы закроете текстовый редактор, git попытается применить фиксации в заданной последовательности и совершить указанное действие. Если указан 'pick', он попробует применить патч и сохранить фиксацию с таким же сообщением как прежде. Если установлен 'squash', git объединит эту фиксацию с предыдущей для создания новой фиксации. Это снова предоставит вам текстовый редактор для слияния сообщений обоих фиксаций, которые мы сжимаем вместе. Итак, если вы выйдете из текстового редактора со следующим содержанием: pick fc62e55 добавлен file_size squash 9824bf4 исправлена мелкая проблема squash 21d80a5 в лог добавлено число squash 76b9da6 добавлена команда apply squash c264051 Возвращение к "добавлен file_size" - некорректная реализация Тогда вам необходимо создать одно сообщение фиксации из следующего: # Это объединение 5 фиксаций # Сообщение первой фиксации: добавлен file_size # Это сообщение второй фиксации: исправлена мелкая проблема # Это сообщение третьей фиксации: в лог добавлено число # Это сообщение четвертой фиксации: добавлена команда apply # Это сообщение пятой фиксации: Возвращение к "добавлен file_size" - некорректная реализация Это возвратит фиксацию fc62e5543b195f18391886b9f663d5a7eca38e84 к прежнему состоянию. После того, как вы составили описание объединенного коммита и вышли из текстового редактора, фиксация будет сохранена с вашим новым сообщением. Если установлен 'edit', произойдет то же самое, но приостановится перед переходом к следующей фиксации и предоставит вам командную строку, чтобы вы могли исправить коммит, или изменить его содержимое каким-либо образом. Если бы вы хотели, например, разделить фиксацию, вы бы указали 'edit' для этой фиксации: pick fc62e55 добавлен file_size pick 9824bf4 исправлена мелкая проблема edit 21d80a5 в лог добавлено число pick 76b9da6 добавлена команда apply pick c264051 Возвращение к "добавлен file_size" - некорректная реализация Когда вам будет предоставлена командная строка, вы восстановите эту фиксацию и создадите две (или более) новых. Например, 21d80a5 модифицировал два файла, file1 и file2, и вы захотели разделить их на две отдельные фиксации. Вы могли бы сделать это после того, как rebase отправит вас к командной строке: $ git reset HEAD^ $ git add file1 $ git commit 'first part of split commit' $ git add file2 $ git commit 'second part of split commit' $ git rebase --continue Теперь вместо 5 фиксаций у вас было бы 6. Последнее полезное дело, которое может сделать интерактивный rebase, - это удаление фиксации. Если вместо того, чтобы выбрать 'pick', 'squash' или 'edit', вы просто уберёте строку, git удалит фиксацию из истории. ## Интерактивное добавление ## Интерактивное добавление - очень хороший способ работы и визуализации индекса Git. Для начала просто наберите 'git add -i'. Git покажет вам все модифицированные файлы и их статус. $>git add -i staged unstaged path 1: unchanged +4/-0 assets/stylesheets/style.css 2: unchanged +23/-11 layout/book_index_template.html 3: unchanged +7/-7 layout/chapter_template.html 4: unchanged +3/-3 script/pdf.rb 5: unchanged +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> В данном случае, мы видим, что есть 5 модифицированных файлов, которые не были ещё добавлены в наш индекс, и даже количество строк добавленных или удаленных из каждого. Тогда Git покажет нам интерактивное меню с тем, что можно сделать в данном режиме. Если мы хотим добавить файлы к индексу, для режима обновления следует набрать '2' или 'u'. Тогда я могу указать, какие файлы я хочу добавить к индексу (stage), набрав номера файлов (в данном случае, 1-4) What now> 2 staged unstaged path 1: unchanged +4/-0 assets/stylesheets/style.css 2: unchanged +23/-11 layout/book_index_template.html 3: unchanged +7/-7 layout/chapter_template.html 4: unchanged +3/-3 script/pdf.rb 5: unchanged +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown Update>> 1-4 staged unstaged path * 1: unchanged +4/-0 assets/stylesheets/style.css * 2: unchanged +23/-11 layout/book_index_template.html * 3: unchanged +7/-7 layout/chapter_template.html * 4: unchanged +3/-3 script/pdf.rb 5: unchanged +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown Update>> Если нажать клавишу ввода, я буду возвращен к главному меню, где можно видеть, как изменился признак файла: What now> status staged unstaged path 1: +4/-0 nothing assets/stylesheets/style.css 2: +23/-11 nothing layout/book_index_template.html 3: +7/-7 nothing layout/chapter_template.html 4: +3/-3 nothing script/pdf.rb 5: unchanged +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown Теперь мы можем видеть, что первые четыре файла подготовлены к индексации, а последний - ещё нет. Это, в основном, сжатый метод отображения той же информации, которую мы видели, когда запускали 'git status' в командной строке: $ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # modified: assets/stylesheets/style.css # modified: layout/book_index_template.html # modified: layout/chapter_template.html # modified: script/pdf.rb # # Changed but not updated: # (use "git add ..." to update what will be committed) # # modified: text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown # Есть множество полезных вещей, которые мы можем делать, включая откат сделанных (но незакоммиченных) изменений файлов (3: revert), добавление неотслеживаемых файлов (4: add untracked), и просмотр изменений (6: diff). Это все довольно просто. Однако, есть одна команда, которая просто замечательна, это та что устанавливает патч (5: patch). Если вы наберете '5' или 'p' в меню, git покажет вам различия, сделанные вами, патч за патчем (или кусочек за кусочком) и спросит, хотите ли вы зафиксировать его. Таким образом, вы можете фактически зафиксировать коммиты правок частей файла. Если вы отредактировали файл и вы хотите зафиксировать только его часть, а не незаконченную часть, или зафиксировать комментарии или правку пробелов относительно основных изменений, вы можете использовать 'git add -i' делать так очень легко. Здесь я фиксирую некоторые изменения в файле book_index_template.html, но не все из них: зафиксированы незафиксированным патчем 1: +4/-0 nothing assets/stylesheets/style.css 2: +20/-7 +3/-4 layout/book_index_template.html 3: +7/-7 nothing layout/chapter_template.html 4: +3/-3 nothing script/pdf.rb 5: unchanged +121/-0 text/14_Interactive_Rebasing/0_ Interactive_Rebasing.markdown 6: unchanged +85/-0 text/15_Interactive_Adding/0_ Interactive_Adding.markdown Когда вы закончили внесение изменений в свой индекс с помощью 'git add -i', вы просто выходите (7:quit) и затем запускаете 'git commit' для фиксации подготовленных изменений. **Не** запускайте 'git commit -a', это удалит все изменения, внесённые вами, и просто зафиксирует все изменения подряд. [gitcast:c3_add_interactive]("GitCast #3: Interactive Adding") ## Утаивание ## Во время, когда вы в самой сердцевине какой либо сложной работы, и вы нашли неважные, но очевидные и тривиальные ошибки. Вы можете исправить их до того как продолжить. Вы можете использовать linkgit:git-stash[1] для сохранения текущего состояния вашей работы, и после исправления ошибки (или, опционально после произведения этого на другой ветке и вернувшись назад), нескрывая прогресс рабочих изменений. $ git stash "work in progress for foo feature" Эта команда будет сохранять ваши изменения в `stash`, и сбросит ваше рабочее дерево и индекс в соответствии с состоянием вашей текущей ветки. Затем вы можете сделать свои исправления как обычно. ... редактирование и тестирование ... $ git commit -a -m "blorpl: typofix" После этого, вы можете вернуться к тому, с чем вы работали, с помощью `git stash apply`: $ git stash apply ### Очередь сокрытия ### Вы можете также складывать скрываемые изменения в очередь. Если вы запустите 'git stash list' вы можете увидеть какие сокрытия вы сохранили: $>git stash list stash@{0}: WIP on book: 51bea1d... исправление изображений stash@{1}: WIP on master: 9705ae6... изменение кода браузера к официальному репозиторию Затем вы можете применить их отдельно с помощью 'git stash apply stash@{1}'. Вы можете очистить список с помощью 'git stash clear'. ## Git Ветвление ## Имеется несколько способов указания на конкретный коммит или дерево, отличное от указания полного 40-символьного хеша. В Git, они называются 'treeish'. ### Частичное указание Sha ### Если хеш вашего коммита '980e3ccdaac54a0d4de358f3fe5d718027d96aae' , то git будет воспринимать любой из перечисленных идентификаторов одинаково: 980e3ccdaac54a0d4de358f3fe5d718027d96aae 980e3ccdaac54a0d4 980e3cc До тех пор пока часть хеша является уникальной - она не может быть спутана с другим хешом (что маловероятно, если вы используете не менее 5 знаков), git расширит часть хеша для вас. ###Имя ветки, удаленного репозитория или тега### Вы всегда можете использовать имя ветки, удаленного репозитория или тега вместо хеша, так как они так или иначе являются простыми указателями. Если ваша ветка master находится на коммите с хешом 980e3 и вы залили его в родительский ствол и обозначили его как 'v1.0', следующие идентификаторы эквивалентны: 980e3ccdaac54a0d4de358f3fe5d718027d96aae origin/master refs/remotes/origin/master master refs/heads/master v1.0 refs/tags/v1.0 Это означает, что следующие команды дадут вам идентичный результат: $ git log master $ git log refs/tags/v1.0 ### Спецификация по Дате ### Ref Log который сохраняет git позволит вам делать некоторые связанные со временем вещи локально, такие как: master@{yesterday} master@{1 month ago} Что есть сокращение для 'where the master branch head was yesterday' и подобное. Учитывайте что этот формат может результировать в различных хешах на различных компьютерах, даже если ветка master в настоящее время указывает на тоже место. ### Спецификация по Порядку ### Такой формат даст вам N-предыдущее значение конкретной ссылки. Например: master@{5} даст вам пятое предшествующее значение головной master ссылки ### Carrot Parent ### Следующее даст вам N-ого родителя указанной фиксации. Этот формат полезен лишь на фиксациях слияний - объектах фиксаций которые имеют более чем одного родителя master^2 ### Tilde Спецификация ### Tilde Spec даст вам N-ого предродителя объекта фиксации. Для примера, master~2 даст вам указатель на первого родителя первого родителя фиксации в master Это эквивалентно: master^^ Вы можете продолжить делать подобное. Следующие спецификации укажут на одну и ту же фиксацию: master^^^^^^ master~3^~2 master~6 ###Указатель Дерева### Это определяет фиксацию из ветки в которой он указан. Если вы хотите получить sha на который указывает фиксация, вы можете добавить '^{tree}' в конец определения. master^{tree} ### Бинарная Спецификация ### Если вы хотите бинарный хеш, вы можете добавить бинарный патч в конец treeish, наподобие этого: master:/path/to/file ### Диапазон коммитов ### Наконец, вы можете задать диапазон фиксаций. Это даст вам фиксации между 7b593b5 и 51bea1 (здесь 51bea1 последнее), включительно: 7b593b5..51bea1 Этот диапазон содержит каждую фиксацию *начиная с* 7b593b: 7b593b.. ## Отслеживание Ветвей ## 'tracking branch' в Git это локальная ветка которая соединена с отдаленной веткой. Когда вы отправляете и забираете на этой ветке, то автоматически отправляется и и забирается на связанной с ней ветке. Используйте это если вы всегда забираете из той же верхней ветки в новую ветвь, и вы не хотите использовать "git pull " в явном виде. Команда 'git clone' автоматически устанавливает ветвь 'master' которая есть отслеживаемой ветвью для 'origin/master' - главной (master) ветвью клонированной с репозитория. Вы можете создать отслеживаемую ветвь вручную, добавив опцию '--track' к команде Git 'branch' git branch --track experimental origin/experimental После этого вы запускаете: $ git pull experimental Эта команда автоматически загрузит ветку 'origin/experimental' из 'origin' и сольет ее с вашей локальной веткой 'experimental'. Аналогичным образом, когда вы отправляете изменения в 'origin', она отправит то, на что указывает ваш 'experimental', в 'experimental', принадлежащий 'origin', без необходимости указания на него. ## Поиск с Git Grep ## Поиск файлов со словами или фразами в Git в действительности прост при помощи команды linkgit:git-grep[1]. Возможен поиск с обычной unix-командой 'grep' , но с 'git grep' вы можете так же искать сквозь предыдущие версии проекта. Для примера, если я хочу обнаружить каждое место которое использует вызов 'xmmap' в моем git.git репозитории, Я могу запустить следующее: $ git grep xmmap config.c: contents = xmmap(NULL, contents_sz, PROT_READ, diff.c: s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0); git-compat-util.h:extern void *xmmap(void *start, size_t length, int prot, int fla read-cache.c: mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, refs.c: log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0); sha1_file.c: map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0); sha1_file.c: idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0); sha1_file.c: win->base = xmmap(NULL, win->len, sha1_file.c: map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, f sha1_file.c: buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); wrapper.c:void *xmmap(void *start, size_t length, Если я хочу увидеть номер строки для каждого совпадения, я могу добавить опцию '-n' : $>git grep -n xmmap config.c:1016: contents = xmmap(NULL, contents_sz, PROT_READ, diff.c:1833: s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, git-compat-util.h:291:extern void *xmmap(void *start, size_t length, int prot, int read-cache.c:1178: mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_ refs.c:1345: log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0); sha1_file.c:377: map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0); sha1_file.c:479: idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd sha1_file.c:780: win->base = xmmap(NULL, win->len, sha1_file.c:1076: map = xmmap(NULL, *size, PROT_READ, MAP_PR sha1_file.c:2393: buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd wrapper.c:89:void *xmmap(void *start, size_t length, Если вас интересует только имя файла, вы можете передать опцию '--name-only' : $>git grep --name-only xmmap config.c diff.c git-compat-util.h read-cache.c refs.c sha1_file.c wrapper.c Если вы хотите увидеть, какое количество строк соответствует образцу в каждом файле, добавьте опцию '-c' : $>git grep -c xmmap config.c:1 diff.c:1 git-compat-util.h:1 read-cache.c:1 refs.c:1 sha1_file.c:5 wrapper.c:1 И наконец, если я хотел увидеть, что было использовано в конкретной версии git, я мог добавить тег ссылку в конец, подобно следующему: $ git grep xmmap v1.5.0 v1.5.0:config.c: contents = xmmap(NULL, st.st_size, PROT_READ, v1.5.0:diff.c: s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, v1.5.0:git-compat-util.h:static inline void *xmmap(void *start, size_t length, v1.5.0:read-cache.c: cache_mmap = xmmap(NULL, cache_mmap_size, v1.5.0:refs.c: log_mapped = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd v1.5.0:sha1_file.c: map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, v1.5.0:sha1_file.c: idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd v1.5.0:sha1_file.c: win->base = xmmap(NULL, win->len, v1.5.0:sha1_file.c: map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, v1.5.0:sha1_file.c: buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd Вы можете видеть, что есть некоторые различия между текущими строками и строками версии 1.5.0, одно из которых в том, что xmmap теперь используется в wrapper.c, чего еще не было в версии v1.5.0. Мы также можем комбинировать условия поиска в grep. Скажем, что мы хотели найти, где находится в нашем репозитории определение SORT_DIRENT: $ git grep -e '#define' --and -e SORT_DIRENT builtin-fsck.c:#define SORT_DIRENT 0 builtin-fsck.c:#define SORT_DIRENT 1 Мы можем также искать во всех файлах, которые содержат *оба* искомых слова, но при этом показать в этих файлах каждую строку, содержащую *любое* искомое слово: $ git grep --all-match -e '#define' -e SORT_DIRENT builtin-fsck.c:#define REACHABLE 0x0001 builtin-fsck.c:#define SEEN 0x0002 builtin-fsck.c:#define ERROR_OBJECT 01 builtin-fsck.c:#define ERROR_REACHABLE 02 builtin-fsck.c:#define SORT_DIRENT 0 builtin-fsck.c:#define DIRENT_SORT_HINT(de) 0 builtin-fsck.c:#define SORT_DIRENT 1 builtin-fsck.c:#define DIRENT_SORT_HINT(de) ((de)->d_ino) builtin-fsck.c:#define MAX_SHA1_ENTRIES (1024) builtin-fsck.c: if (SORT_DIRENT) Мы можем также искать строки, которые содержат первое слово и любое из двух других, для примера, если вы хотите увидеть, где мы определили константу, имя которой содержит PATH или MAX: $ git grep -e '#define' --and \( -e PATH -e MAX \) abspath.c:#define MAXDEPTH 5 builtin-blame.c:#define MORE_THAN_ONE_PATH (1u<<13) builtin-blame.c:#define MAXSG 16 builtin-describe.c:#define MAX_TAGS (FLAG_BITS - 1) builtin-fetch-pack.c:#define MAX_IN_VAIN 256 builtin-fsck.c:#define MAX_SHA1_ENTRIES (1024) ... ## Отмена в Git - Reset, Checkout и Revert ## Git предоставляет различные методы исправления ошибок во время вашей разработки. Выбор подходящего метода зависит от того, была или нет закоммичена ошибка, и если была, используется ли ошибочный код кем либо еще. ### Исправление незакоммиченных ошибок ### Если вы напутали в вашем рабочем дереве, но не успели еще сделать фиксацию вашей ошибки, вы можете вернуть свое рабочее дерево к последнему зафиксированному состоянию при помощи команды $ git reset --hard HEAD Это отбросит любые изменения, возможно добавленные вами к индексу Git и также любые неподтвержденные изменения, которые вы внесли в ваше рабочее дерево. Другими словами, это приводит к тому, что результаты "git diff" и "git diff --cached" будут пусты. Если вы просто хотите откатить только один файл, скажем, ваш hello.rb, используйте linkgit:git-checkout[1] вместо $ git checkout -- hello.rb $ git checkout HEAD hello.rb Первая команда возвращает файл hello.rb к версии, внесенной в индекс, так что "git diff hello.rb" не выведет различий. Вторая команда вернет файл hello.rb к версии, зафиксированной в ревизии HEAD, так что и "git diff hello.rb", и "git diff --cached hello.rb" не выведут различий. ### Исправление зафиксированных ошибок ### Если вы сделали фиксацию, которую позднее хотите убрать, есть два фундаментально различных способа решить эту проблему: 1. Вы можете создать новую фиксацию, которая отменяет то, что было сделано старой фиксацией. Это правильный путь, если ваша ошибка уже была опубликована. 2. Вы можете вернуться назад и модифицировать старую фиксацию. Вам не стоит этого делать, если эта фиксация уже была опубликована; git обычно ожидает, что "история" проекта не меняется, и не может корректно выполнять повторные слияния с веткой, чья история была изменена. #### Исправление ошибок новой фиксацией #### Создать новую фиксацию, которая отменяет сделанные ранее изменения, очень легко; просто передайте команде linkgit:git-revert[1] ссылку на плохую фиксацию; для примера, чтобы вернуться к последней фиксации, наберите: $ git revert HEAD Будет создана новая фиксация, которая отменит изменения в HEAD. Вам будет предоставлена возможность отредактировать комментарий для новой фиксации. Вы также можете вернуть более ранние изменения, например, предпоследние: $ git revert HEAD^ В этом случае Git попытается отменить старые изменения, оставив нетронутыми любые изменения, сделанные с тех пор. Если более поздние изменения перемежаются с откатываемыми изменениями, вам будет предложено разрешить конфликты вручную, как и в случае возникновения конфликтов при слиянии. #### Исправление ошибки модификацией фиксации #### Если вы только что зафиксировали свои изменения, но поняли, что вам нужно исправить эту фиксацию, последние версии linkgit:git-commit[1] поддерживают флаг **--amend**, который указывает git заменить HEAD фиксацию новой, основанной на текущем содержимом индекса. Это дает вам возможность добавить файлы, которые вы забыли добавить или исправить опечатки в комментарии к фиксации, перед вынесением сделанного изменения на всеобщее обозрение. Если вы нашли ошибку в старой, но еще не опубликованной, фиксации, вы можете использовать linkgit:git-rebase[1] в интерактивном режиме, с "git rebase -i", помечая изменения, которые нуждаются в исправлении, меткой **edit**. Это позволит вам исправить ошибочную фиксацию в процессе перебазирования. ## Поддержка Git ## ### Обеспечение хорошей производительности ### В больших проектах git'у требуется сжатие, чтобы информация об по истории, которая занимают слишком много места на диске или в памяти. Такое сжатие не выполняется автоматически. Поэтому вам следует периодически запускать linkgit:git-gc[1]: $ git gc для пережатия архива. Это может занимать много времени, итак вам наверно лучше запускать git-gc когда вы не делаете другой работы. ### Обеспечение надежности ### Команда linkgit:git-fsck[1] производит несколько тестов на самосогласованность репозитория, и сообщает о любых выявленных проблемах. Это может занять некоторое время. Большинство общих предупреждений сообщают об "висящих"(dangling) объектах: $ git fsck dangling commit 7281251ddd2a61e38657c827739c57015671a6b3 dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63 dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5 dangling blob 218761f9d90712d37a9c5e36f406f92202db07eb dangling commit bf093535a34a4d35731aa2bd90fe6b176302f14f dangling commit 8e4bec7f2ddaa268bef999853c25755452100f8e dangling tree d50bb86186bf27b681d25af89d3b5b68382e4085 dangling tree b24c2473f1fd3d91352a624795be026d64c8841f ... Прерванные объекты не проблема. В худшем случае они могут занять немного лишнего дискового пространства. Иногда они могут помочь в последнем способе по восстановлению утерянной работы. ## Создание Приватного Репозитория ## Если вам нужен личный, приватный репозиторий и вы хотите сделать его локально, а не на хостинге, есть некоторое количество опций для этого. ### Доступ в Репозиторий через SSH ### В общем, самый первый способ это использовать git посредством SSH. Если пользователи уже имеют аккаунты на машине, вы можете расположить репозиторий git в любом месте где они имеют доступ и дать им доступ через нормальный логин ssh. Для примера, скажем вы имеете репозиторий вы хотите разместить хост. Вы можете экспортировать его в качестве пустого репозитория и затем посредством scp отправить его на ваш сервер примерно так: $ git clone --bare /home/user/myrepo/.git /tmp/myrepo.git $ scp -r /tmp/myrepo.git myserver.com:/opt/git/myrepo.git Затем любой кто имеет аккаунт на myserver.com может клонировать его через: $ git clone myserver.com:/opt/git/myrepo.git Что потребует ввода пароля на их ssh аккаунт или публичного ключа, таким образом они пройдут процедуру авторизации. ### Многопользовательский доступ с помощью Gitosis ### Если вы не хотите настраивать отдельные учетные записи для каждого пользователя, можно воспользоваться инструментом под названием Gitosis. В Gitosis имеется файл authorized_keys, который содержит публичные ключи всех пользователей, имеющих доступ в репозиторий, а скачивание и выкладывание кода в репозиторий производится из под учетной записи пользователя 'git'. [Установка и настройка Gitosis](http://www.urbanpuddle.com/articles/2008/07/11/installing-git-on-a-server-ubuntu-or-debian) ## Создание Новых Пустых Веток ## Иногда,Вы можете сохранить филиалы в вашем хранилище, не имеют общих предков с нормальным кодом. Примером может служить сгенерированная документация или что-либо подобное. Если вы хотите создать новую головную ветвь, которая не использует вашу текущую кодовую базу в качестве родителя, вы можете создать пустую ветвь так: git symbolic-ref HEAD refs/heads/newbranch rm .git/index git clean -fdx git add ваши файлы git commit -m 'Initial commit' [gitcast:c9-empty-branch]("GitCast #7: Создание Пустых Веток") # Продвинутый Git # ## Изменение вашей Истории ## Интерактивное перебазирование -- это хороший способ для изменения отдельных фиксаций. linkgit:git-filter-branch[1] -- это хороший способ редактировать фиксации в массовом порядке. ## Продвинутое Ветвление и Слияние ## ### Получение помощи в разрешении конфликтов в слиянии ### Все изменения, которые git способен объединить автоматически, уже добавлены в индексный файл, так что linkgit:git-diff[1] показывает только конфликты. Для этого используется необычный синтаксис: $ git diff diff --cc file.txt index 802992c,2b60207..0000000 --- a/file.txt +++ b/file.txt @@@ -1,1 -1,1 +1,5 @@@ ++<<<<<<< HEAD:file.txt +Hello world ++======= + Goodbye ++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.tx Напомним, что фиксация, которая будет сделана после разрешения этого конфликта, будет иметь двух родителей вместо одного: один родитель будет HEAD, вершина текущей ветви; другой будет вершиной другой ветви, который сохранен временно в MERGE_HEAD. В течение слияния, индекс содержит три версии каждого файла. Каждое из этих трех "состояний файла" представляет различные версии файла: $ git show :1:file.txt # версия файла из общего предка сливаемых веток $ git show :2:file.txt # версия из HEAD. $ git show :3:file.txt # версия из MERGE_HEAD. Когда вы просите linkgit:git-diff[1] показать конфликты, он запускает трехстороннее сравнение между конфликтующими соедененными результатами в рабочем дереве с стадией 2 и 3 показавая только куски, содержимое которых приходят с обеих сторон, смешанное (другими словами, когда смесь частей исходит только от стадии 2, ) та часть которая не конфликтует и не показывается. То же и со стадией 3). Просмотр показывает различия между рабочим деревом версии file.txt и версии стадии 2 и версии стадии 3. Так вместо предварения каждой строки одиночным "+" или "-", теперь используется два столбца: первый столбец используется для показа различий между первым родителем и рабочей копией директории, второй для различиями второго родителя и копией рабочей директории. (Смотри главу "COMBINED DIFF FORMAT секцию" linkgit:git-diff-files[1] для пояснений формата.) После разрешения конфликтов очевидным образом (но до обновления индекса), diff будет выглядет подобно: $ git diff diff --cc file.txt index 802992c,2b60207..0000000 --- a/file.txt +++ b/file.txt @@@ -1,1 -1,1 +1,1 @@@ - Hello world -Goodbye ++Goodbye world Это показывает что наша версия удалила "Hello world" из первого родителя, удалила "Goodbye" из второго родителя, и добавила "Goodbye world", что ранее отсутсвовало в обоих. Некоторые специальные опции diff позволяют различать рабочие директории относительно любых этих стадий: $ git diff -1 file.txt # различие относительно стадии 1 $ git diff --base file.txt # то же самое $ git diff -2 file.txt # различия относительно стадии 2 $ git diff --ours file.txt # то же самое $ git diff -3 file.txt # различия относительно стадии 3 $ git diff --theirs file.txt # то же самое. Команды linkgit:git-log[1] и linkgit:gitk[1] также оказывают особую помощь для слияний: $ git log --merge $ gitk --merge Это показывает все фиксации которые существуют только в HEAD или в MERGE_HEAD, и что указывает на неприсоединенный файл. Вы можете так же использовать linkgit:git-mergetool[1], что позволит вам соединять неприсоединенный файлы используя внешние инструменты, такие как emacs или kdiff3. Каждый раз когда вы разрешаете конфликты в файле и обновляете индекс: $ git add file.txt различные стадии того файла будут "collapsed", после которого git-diff не будет (по умолчанию) показывать различия для этого файла. ### Многосторонние Слияния ### Вы можете соеденить несколько заголовков за один раз просто перечислив из в той же команде linkgit:git-merge[1]. Для примера, $ git merge scott/master rick/master tom/master что есть эквивалентно: $ git merge scott/master $ git merge rick/master $ git merge tom/master ### Поддерево ### Есть ситуации когда вы хотите включить некое содержание в ваш проект из независимого проекта разработки. Вы можете просто вытянуть из другого проекта сколько возможно не конфликтующих патчей. Проблемы происходят когда есть конфликтующие файлы. Потенциальные кандидаты для этого Makefiles и другого рода файлы имеющие стандартные имена. Вы могли бы соеденить эти файлы но возможно вы не хотите этого делать. Лучший совет для подобного случая может быть в соеденении проектов в их собственных поддиректориях. Это не поддерживается стратегией рекурсивного слияния, и вытягивание не будет работать. То что вы хотите, называется стратегией слияния поддеревьев, которая поможет вам в такой ситуации. В этом примере, предположим вы имеете репозиторий в /path/to/B (но это может быт и URL, если захотите). Вы хотите соединить главную ветку этого репозитория с dir-B поддиректорией в вашей текущей ветке. Вот последовательность команд, которая вам нужна: $ git remote add -f Bproject /path/to/B (1) $ git merge -s ours --no-commit Bproject/master (2) $ git read-tree --prefix=dir-B/ -u Bproject/master (3) $ git commit -m "Merge B project as our subdirectory" (4) $ git pull -s subtree Bproject master (5) Приемущество в использовании слияниия поддеревьев что оно требует преодоления меньших административных барьеров для пользователей вашего репозитория. Оно работает со старыми (до Git v1.5.2 ) клиентами и вы получите хороший код после клонирования. Однако если вы используете подмодули тогда вы можете решить не переносить субмодульные объекты. Это может быть проблемой в слиянии поддеревьев. Кроме того, в случае внесения изменений в другие проекты, проще представить изменения, если вы просто используете подмодули. (из [Using Subtree Merge](http://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html)) ## Поиск Проблем - Git Bisect ## Предположим версия 2.6.18 вашего проекта работает, но версия "master" сломана. Иногда лучший путь найти причину такой регрессии это устроить грубо-энергичный поиск во всей истории проекта что бы найти ту фиксацию которая является причиной проблемы. Команда linkgit:git-bisect[1] может помочь вам сделать так: $ git bisect start $ git bisect good v2.6.18 $ git bisect bad master Bisecting: 3537 revisions left to test after this [65934a9a028b88e83e2b0f8b36618fe503349f8e] BLOCK: Make USB storage depend on SCSI rather than selecting it [try #6] Если вы запустите "git branch" в этой точке, вы увидите что git временно передвинул вас на новую ветку названную "bisect". Эта ветвь указана фиксацией (с id 65934...) который достижим из "master" но не из v2.6.18. Скомпилируйте и проверте его и вы увидите рабочий ли он. Предположим, что он не рабочий. Тогда: $ git bisect bad Bisecting: 1769 пройдено проверочных тестов после этого [7eff82c8b1511017ae605f0c99ac275a7e21b867] i2c-core: Оставте бесполезные bitmaskings проверки старой версии. Продолжайте подобно этому, вызывая git в каждой стадии, и проверяя дает ли версия вам хороший результат или плохой, замечая тот номер версии прошедшей тест разрывающийся примерно в половине каждый раз. После около 13 тестов (в этом случае), оно выведет индетификатор виновной фиксации. Вы можете просмотреть фиксацию с , linkgit:git-show[1], найдя кто написал его, и выслать им ваше сообщение об ошибке дав номер фиксации. Наконец, запустите $ git bisect reset для возвращения к ветви, где вы были ранее и удалите временную "bisect" ветвь. Учтите, что версия которую проверяет git-bisect для вас в каждой точке есть просто предположение, и вы свободны в опробировании другой версии если вы думаете что это хорошая идея. Для примера, иногда вы можете перейти на фиксацию которая сломана кем то неизвестным; запустите $ git bisect visualize который запустит gitk и пометит фиксацию выбранным маркером который обозначает "bisect". Выберите безопасную фиксацию вблизи, примите к сведению индетификационный номер фиксации, и проверте его с: $ git reset --hard fb47ddb2db... после теста, запустите "bisect good" или "bisect bad" в зависимости от результата, и продолжайте. ## Поиск Проблем - Git Blame ## linkgit:git-blame[1] эта команда очень полезна для определения кто изменил какие секции файла. Если вы просто запустите git blame [имя_файла]' вы получите вывод всего файла с последним хешем фиксации, датой и автором для каждой строки файла. $ git blame sha1_file.c ... 0fcfd-0700 8) */160 (Linus Torvalds 2005-04-18 13:04:43 0fcfd160 (Linus Torvalds 2005-04-18 13:04:43 -0700 9) #include "cache.h" 1f688557 (Junio C Hamano 2005-06-27 03:35:33 -0700 10) #include "delta.h" a733cb60 (Linus Torvalds 2005-06-28 14:21:02 -0700 11) #include "pack.h" 8e440259 (Peter Eriksen 2006-04-02 14:44:09 +0200 12) #include "blob.h" 8e440259 (Peter Eriksen 2006-04-02 14:44:09 +0200 13) #include "commit.h" 8e440259 (Peter Eriksen 2006-04-02 14:44:09 +0200 14) #include "tag.h" 8e440259 (Peter Eriksen 2006-04-02 14:44:09 +0200 15) #include "tree.h" f35a6d3b (Linus Torvalds 2007-04-09 21:20:29 -0700 16) #include "refs.h" 70f5d5d3 (Nicolas Pitre 2008-02-28 00:25:19 -0500 17) #include "pack-revindex.h"628522ec (Junio C Hamano 2007-12-29 02:05:47 -0800 18) #include "sha1-lookup.h" ... Это часто удобно если файл имел перевернутую строку или ошибку которая не позволяет сборку, чтобы помочь вам узнать, кто изменил, какие последние строки. Вы можете также определить начало и конец виновной строки: $>git blame -L 160,+10 sha1_file.c ace1534d (Junio C Hamano 2005-05-07 00:38:04 -0700 160)} ace1534d (Junio C Hamano 2005-05-07 00:38:04 -0700 161) 0fcfd160 (Linus Torvalds 2005-04-18 13:04:43 -0700 162)/* 0fcfd160 (Linus Torvalds 2005-04-18 13:04:43 -0700 163) * * NOTE! This returns a statically allocate 790296fd (Jim Meyering 2008-01-03 15:18:07 +0100 164) * careful about using it. Do an "xstrdup() 0fcfd160 (Linus Torvalds 2005-04-18 13:04:43 -0700 165) * filename. ace1534d (Junio C Hamano 2005-05-07 00:38:04 -0700 166) * ace1534d (Junio C Hamano 2005-05-07 00:38:04 -0700 167) * Also note that this returns the location ace1534d (Junio C Hamano 2005-05-07 00:38:04 -0700 168) * SHA1 file can happen from any alternate d19938ab (Junio C Hamano 2005-05-09 17:57:56 -0700 169) * DB_ENVIRONMENT environment variable if i ## Git and Email ## ### Отправка патчей для проекта ### Если вы просто имеете несколько изменений, простой путь отправить их это возможно просто послать их по электронной почте: Во первых, используйте формат linkgit:git-format-patch[1]; для примера: $ git format-patch origin произведет пронумерованные серии файлов в текущей директории, по одному для каждого патча в текущей ветви но не в origin/HEAD. Вы можете импортировать их в ваш клиент электронной почты и послать их вручную. Одноко, если вы хотите послать много всего, вам наверно лучше использовать linkgit:git-send-email[1] скрипт для автоматизации процесса. Проконсультируйтесь, для начала, в списке рассылки вашего проекта для определения как собутыльники предпочитают получать такие патчи. ### Импорт патчей в проект ### Git предоставляет так же инструмент называемый linkgit:git-am[1] (сокращение к "apply mailbox" ), для импорта присланной серии патчей. Просто сохраните содержащие патчи письма, по порядку, в одном файле, скажем названном "patches.mbox", затем запустите $ git am -3 patches.mbox Git применит каждый патч по порядку, если будут найдены конфликты, произойдет остановка, и вы сможете вручную разрешить конфликты и принять решение по объединению. (опция "-3" сообщает git предложить слияние; если вы предпочтете просто прервать и покинуть ваше дерево и индексировать нетронутое, вам следует опустить этот параметр.) После того как индекс обновляется с результатом конфликта вместо открытия нового коммита, просто запустите $ git am --resolved и git откроет коммит для вас и продолжит применять оставшиеся патчи из файла. Финалом будет серия фиксаций, по одной на каждый патч в оригинальном mailbox, с авторством и логом сообщения для каждой фиксации взятом из сообщения содержавшего патч. ## Настройка Git ## linkgit:git-config[1] ###Изменение вашего Редактора ### $ git config --global core.editor emacs ### Добавление Псевдонимов ### $ git config --global alias.last 'cat-file commit HEAD' $ git last tree c85fbd1996b8e7e5eda1288b56042c0cdb91836b parent cdc9a0a28173b6ba4aca00eb34f5aabb39980735 author Scott Chacon 1220473867 -0700 committer Scott Chacon 1220473867 -0700 fixed a weird formatting problem $ git cat-file commit HEAD tree c85fbd1996b8e7e5eda1288b56042c0cdb91836b parent cdc9a0a28173b6ba4aca00eb34f5aabb39980735 author Scott Chacon 1220473867 -0700 committer Scott Chacon 1220473867 -0700 fixed a weird formatting problem ### Добавление Цвета ### Смотри по данным настройкам в документации linkgit:git-config[1] $ git config color.branch auto $ git config color.diff auto $ git config color.interactive auto $ git config color.status auto Или, вы можете установить их в опции color.ui: $ git config color.ui true ### Commit Template ### $ git config commit.template '/etc/git-commit-template' ## Формат Лога ### $ git config format.pretty oneline ### Другие Опции Настройки ### Есть ряд интересных опций для пакетирования, объединения, gc-ing, управления, ветвления, передачи в http, diffs, paging, и многом другом. Если вы хотите настроить их, смотрите документацию linkgit:git-config[1] ## Git Обработчики ## Обработчики маленьких скриптов вы можете поместить в директорию $GIT_DIR/hooks для переключения действия к определенной точке. Когда запускается git-init, несколько примеров обработчиков копируются в директорию обработчиков нового репозитория, но по умолчанию они все отключены. Чтобы активировать обработчик, переименуйте его удалив суффикс .sample ### applypatch-msg ### GIT_DIR/hooks/applypatch-msg Этот обработчик вызывается git-am скриптом. Он принимает одиночный параметр, имя файла который содержит предлогаемое сообщение лога коммита. Выход с ненулевым статусом означает что git-am закончил работу до применения патча. Обработчику позволено редактировать сообщение файла на месте, и он может использоваться для форматирования сообщения в соответствии с неким стандартом проекта (если проект его имеет). Он может использоваться для удаления фиксации после проверки файла сообщения. Когда дефолтный обработчик applypatch-msg активирован, запускает commit-msg обработчик, если последний активирован. ### pre-applypatch ### GIT_DIR/hooks/pre-applypatch Этот обработчик вызвается по git-am. Он не принимает параметров, и вызывается после применения патча, но до производства фиксации. Если он заканчивает работу с ненулевым статусом, то рабочая директория не может быть зафиксирована после применения патча. Он может использоваться для проверки текущего рабочего дерева и удаления сделанной фиксации если та не прошла определенный тест. Дефолтный pre-applypatch обработчик, когда активирован, запускает обработчик pre-commit если последний активирован. ### post-applypatch ### GIT_DIR/hooks/post-applypatch Обработчик вызывается 'git-am'. Он не принимает параметров, и вызывается после применения патча и сделанной фиксации. Этот обработчик предназначен прежде всего для уведомлений, и не может влиять на исход git-am'. ### pre-commit ### GIT_DIR/hooks/pre-commit Этот обработчик вызывается 'git-commit', и может быть игнорирован опцией `\--no-verify`. Он не принимает параметров, и вызывается до предполагаемого совершения сообщения лога фиксации и производства самой фиксации. Окончание работы с ненулевым статусом этого скрипта приведет к неудаче 'git-commit'. Когда дефолтный обработчик 'pre-commit' работает, ищутся введенные строки оканчивающиеся пробелами и фиксация прерывается когда такие строки найдены. Все обработчики 'git-commit' вызываются с переменной окружения `GIT_EDITOR=:` если команда не выполняется редактором для модификации сообщения фиксации. Здесь примеры скрипта на Ruby который запускает тест RSpec до принятия коммита. ruby html_path = "spec_results.html" `spec -f h:#{html_path} -f p spec` # run the spec. send progress to screen. save html results to html_path # find out how many errors were found html = open(html_path).read examples = html.match(/(\d+) examples/)[0].to_i rescue 0 failures = html.match(/(\d+) failures/)[0].to_i rescue 0 pending = html.match(/(\d+) pending/)[0].to_i rescue 0 if failures.zero? puts "0 failures! #{examples} run, #{pending} pending" else puts "\aDID NOT COMMIT YOUR FILES!" puts "View spec results at #{File.expand_path(html_path)}" puts puts "#{failures} failures! #{examples} run, #{pending} pending" exit 1 end ### prepare-commit-msg ### GIT_DIR/hooks/prepare-commit-msg Этот обработчик вызывается удачным 'git-commit' после подготовки дефолтного лога сообщения, и до старта редактора. Он принимает один из трех аргументов. Первый имя файла лога сообщения фиксации. Второй источник фиксации сообщения, и может быть: `message` (если ключи -m или -F были даны); `template` (если передан ключ `-t`или установлена настроечная опция `commit.template`); `merge` (если коммит слияния или `.git/MERGE_MSG` файл существует); `squash` (если существует файл `.git/SQUASH_MSG` ); или за `commit`, следует коммит SHA1 (если ключи `-c`, `-C` или `\--amend`переданы). Если выход происходит с ненулевым статусом 'git-commit' будет прерван. Цель обработчика это открытие файла сообщения в месте и это не запрещено ключом `\--no-verify` . Ненулевой выход означает ошибку обработчика и прерывание фиксации. Он не должен быть использован как замена pre-commit обработчику. Простой prepare-commit-msg` обработчик который происходит с git коммитами выводит `Conflicts:` часть сообщения коммита слияния ### commit-msg ### GIT_DIR/hooks/commit-msg Этот обработчик вызывается 'git-commit', и может быть подавлен ключом `\--no-verify`. Он принимает одиночный параметр, имя файла который содержит предлагаемый лог сообщения фиксации. Завершение работы с ненулевым статусом прерывает 'git-commit' аборт, да Этот обработчик допускает изменение файла сообщений на месте, и может использоваться для форматирования сообщения в стандарте проекта (если таковой есть). Он также используется для удаления коммита после проверки файла сообщения. По умолчанию обработчик 'commit-msg', когда активирован, определяет двойные "Signed-off-by" строки, и прерывает фиксацию, если таковые найдены. ### post-commit ### GIT_DIR/hooks/post-commit Этот коммит вызывается 'git-commit'. Он не принимает параметров, и вызывается после выполнения фиксации. Этот обработчик предназначен прежде всего для уведомлений, и не может повлиять на результаты 'git-commit'. ### pre-rebase ### GIT_DIR/hooks/pre-rebase Этот обработчик вызывается 'git-rebase' и может быть использован для предупреждения ветви от перебазирования. ### post-checkout ### GIT_DIR/hooks/post-checkout Этот обработчик вызывается git-checkout после обновления рабочего дерева. У него есть три параметра: сноска предидущей HEAD, сноска новой HEAD, (которая может или нет быть поменяна), и флаг (flag) указывающий была ли проверка ветвью проверки (смена ветки, flag=1) или файл проверен (извлечение файла из индекса, flag=0). Этот обработчик не повлияет на результат 'git-checkout'. Этот обработчик может быть использован для представления проверки достоверности данных репозитория, автоматического отображения различий от предидущего HEAD если таковое есть, или установки свойств метаданных рабочей директории. ### post-merge ### GIT_DIR/hooks/post-merge Этот обработчик вызывается 'git-merge', что случается когда 'git-pull' сделан в локальном репозитарии. Обработчик принимает одиночный параметр, флаг статуса определяющий было ли или нет сделано слияние типа squash. Этот обработчик не повлияет на результат git-merge' и не извлекается если слияние было прервано в результате конфликтов. Этот обработчик может быть использован в сочетании с передающим pre-commit обработчиком для сохранения и возвращения любой формы метаданных связанных с рабочим деревом (например: права доступа/владение, ACLS, и тому подобное). Смотрите contrib/hooks/setgitperms.perl для изучения примеров как это делать. ### pre-receive ### GIT_DIR/hooks/pre-receive Этот обработчик вызывается git-receive-pack' на отдаленный репозиторий, что случается когда проходит 'git-push' на локальном репозитории. Сразу после начала обновления сносок на удаленном репозитории, вызывается обработчик pre-receive. Его статус выхода определяет удачу или неудачу обновления. Этот обработчик извлекается только для принятой операции. Он не принимает аргументов, но для каждой обновленной сноски он принимает на стандартный вход строку формата: SP SP LF Где `` старое имя объекта сохраненное в сноске, `` новое имя объекта которое сохранится в сноске и `` полное имя сноски. Когда открывается новая сноска, `` есть 40 -`0`. Если обработчик выходит из работы с ненулевым статусом, значение NONE сноски будет обновлен. Если обработчик возвращает ноль, обновление отдельной сноски может еще быть предотвращено обработчиком <>. Оба и стандартный вывод и стандартный вывод ошибок направляются к 'git-send-pack' на другой конец, так что вы можете просто использовать `echo`. для пользователя. Если вы пишете на Ruby, вы могли бы получить аргументы этим способом: ruby rev_old, rev_new, ref = STDIN.read.split(" ") Или в скрипте шелла, что то подобное будет работать: #!/bin/sh # # update a blame tree while read oldrev newrev ref do echo "STARTING [$oldrev $newrev $ref]" for path in `git diff-tree -r $oldrev..$newrev | awk '{print $6}'` do echo "git update-ref refs/blametree/$ref/$path $newrev" `git update-ref refs/blametree/$ref/$path $newrev` done done ### Обновление ### GIT_DIR/hooks/update Этот обработчик вызвается 'git-receive-pack' на удаленном репозитории, после 'git-push' на локальном репозитарии. Прямо перед обновлением сноски на удаленном репозитории, обработчик обновления вызывается. Статус его выхода определяет удачу или нет обновления сноски. Обработчик извлекается на каждую обновляемую сноску, и принимает три параметра: - название обновляемой сноски - старое название объекта хранимого в сноске и новое название объекта хранимого в сноске. Нулевой статус выхода обработчика позволяет сноске обгновиться. Выход с ненулевым статусом предотвращает 'git-receive-pack' от обновления этой сноски. Этот обработчик может использоваться для предотвращения "форсированного" обновления на определенной сноске по той причине что имя объекта есть объект фиксации который потомок фиксированного объекта названный старым именем объекта. Таким образом, обеспечивается политика "быстрой перемотки только вперед". Он так же может использоваться для пометки старого ... новым статусом. Однако, он не знает всей совокупности ветвей, и таким образом может в конечном итоге наивно использовать один e-mail на сноску. <> обработчик более подходит для этого. Другие предлагают использовать этот обработчик в списке рассылки для осуществления контроля доступа, который более филигранный чем тот что основан на группах допуска файловой системы. Как стандартный вывод, так и стандартный вывод ошибок направляется к 'git-send-pack' на другом конце, так вы можете просто использовать 'echo' при сообщениях для пользователя. По умолчанию обработчик 'update', активирован и с hooks.allowunannotated` настроечной опцией предотвращает загрузку неуказанных тегов. ### Пост-получение ### GIT_DIR/hooks/post-receive Этот обработчик вызывается 'git-receive-pack' на удаленном репозитории, это случается когда происходит 'git-push' на локальном репозитории. Его извлечение на удаленном репозитории происходит тотчас после того как все сноски. будут обновлены Этот обработчик выполняется один раз во время приема. Он не принимает аргументов, но получает ту же информацию как и <> обработчик на свой стандартный вход. Этот обработчик не влияет на работу git-receive-pack', так как он вызвается после действительной работы. Это отличает от <> обработчика в том что он получает оба старые и новые значения всех сносок в дополнение к их именам. Стандартные потоки выхода и ошибок направлены к 'git-send-pack' с другой стороны, так что вы можете просто использовать 'echo' сообщения пользователю. По умолчанию 'post-receive' обработчик пуст, но есть простой сценарий `post-receive-email` предлагаемый в `contrib/hooks` git репозитория, который реализует отправку фиксаций по почте. ### после обновления ### GIT_DIR/hooks/post-update Этот обработчик вызывается при помощи 'git-receive-pack' на удаленном репозитории, это происходит после завершения 'git-push' на локальном репозитории. Этот обработчик выполняется на удаленном репозитории тот час после обновления всех сносок. ... Он принимает различное число параметров, каждый из которых имя сноски которая была в действительности обновлена. Этот обработчик предназначен прежде всего для уведомлений, и не имеет влияния на git-receive-pack'. Обработчик post-update может сообщить какие заголовки были расположены, но он не знает, их оригинальных и обновленных значений, таким образом это просто способ чтоб обновить лог. В <> попадают и оригинальные, и обновленные значения ссылок. Вы могли бы использовать их вместо этого, если вам это нужно. Когда возможно, обработчик 'post-update', используемый по-умолчанию, выполняет 'git-update-server-info', чтобы своевременно сохранить информацию, используемую транспортным пакетом (например HTTP). Если вы публикуете git репозиторий, который доступен через HTTP, вам следует, возможно, разрешить этот обработчик Оба стандартных потока вывода и ошибок, перенаправляются в 'git-send-pack' , и вы можете просто повторить это сообщение для пользователя ### pre-auto-gc ### GIT_DIR/hooks/pre-auto-gc Этот обработчик вызывается 'git-gc --auto'. Он не имеет параметров, и отдает не нулевой статус для скрипта, заставляя 'git-gc --auto' прерваться. ### Ссылки ### [Git Hooks](http://www.kernel.org/pub/software/scm/git/docs/githooks.html) * http://probablycorey.wordpress.com/2008/03/07/git-hooks-make-me-giddy/ ## Восстановление поврежденных объектов ## [Recovering Lost Commits Blog Post](http://programblings.com/2008/06/07/the-illustrated-guide-to-recovering-lost-commits-with-git) [Recovering Corrupted Blobs by Linus](http://www.kernel.org/pub/software/scm/git/docs/howto/recover-corrupted-blob-object.txt) ## Подмодули ## Большие проекты обычно состоят из небольших, самодостаточных модулей. На пример, дерево исходных кодов дистрибутивов Linux для встраеваемых систем может включать кусочки программного обеспечения с какими-либо изменениями; видео-проигрыватель может нуждаться в пересборке специфичной, версии библиотеки декомпресии, которая проверенно работает; несколько независимых программ могут совместно использовать некоторые сборочные скрипты В централизованных системах контроля версий это часто достигалось включением каждого модуля в один единственный репозиторий. Разработчики могут выгрузить все модули, или только те, что нужны им для работы. Они могут даже изменять файлы в нескольких модулях одним commit, когда переставляют что-то или обновляют API и переводы. Git не позволяет осуществлять частичные выгрузки, так что дублирование это подход в Git, который подталкивает разработчиков хранить локально модули, в изменениях которых они не заинтересованы. Commit в огромной выгрузке будет медленнее чем, вы ожидали так, как Git должен просканировать все директории на изменения. Если у модуля есть множество локальных изменений, то клонирование займет вечность. С положительной стороны, распределенные системы контроля ревизий могут намного лучше интегрироваться с внешними источниками. В централизованной модели, один, произвольный срез стороннего проекта экспортируется из его системы контроля ревизий, а затем импортируется в локальную в ветвь разработки. Все изменения скрыты. С распределенными системами контроля ревизий вы можете клонировать всю внешнюю историю и более того легко следить за разработкой и совмещать с локальными изменениями. Поддержка подмодулей в Git позволяет репозиторию содержать как поддиректории, так и выгрузки сторонних проектов. Подмодули сохраняют свою собственную идентичность; В поддержке подмодулей все еще сохраняется расположение подмодуля репозитория и фиксируется по ID, хотя другие разработчики, клонирующие включающий проект ("суперпроект") могут легко склонировать все подмодули в той же ревизии. Частичные выгрузки в суперпроекте возможны: вы можете сказать Git частично либо полностью не клонировать подмодули. Команда linkgit:git-submodule[1] доступна начиная с Git 1.5.3 Пользователи использующие версию 1.5.2 могут найти подмодуль фиксаций в репозиторий и все подтвердить вручную; более ранние версии полностью не поддерживают подмодули . Чтобы увидеть поддержку подмодулей в действии, создайте, например, 4 тестовых репозитория, в которых могли бы использоваться в качестве подмодулей: $ mkdir ~/git $ cd ~/git $ for i in a b c d do mkdir $i cd $i git init echo "module $i" > $i.txt git add $i.txt git commit -m "Initial commit, submodule $i" cd .. done Теперь создадим суперпроект и добавим все подмодули: $ mkdir super $ cd super $ git init $ for i in a b c d do git submodule add ~/git/$i $i done Внимание: Не используйте здесь локальные URL если вы планируете опубликовать ваш суперпроект. Посмотрим какие файлы были созданы после `git-submodule`: $ ls -a . .. .git .gitmodules a b c d Команда `git-submodule add` делает следующее: - Она клонирует подмодули под текущую директорию и по умолчанию закрепляет их за главной веткой проекта. - Она добавляет linkgit:gitmodules[5] файл к пути клонированных подмодулей и добавляет этот файл к индексу, готового для фиксации. - Она добавляет текущий ID фиксации подмодуля к индексу, готового для фиксации. Для фиксации главного проекта необходимо ввести следующее: $ git commit -m "Add submodules a, b, c and d." Теперь сделаем клон главного проекта: $ cd .. $ git clone super cloned $ cd cloned Здесь находятся директории подмодулей, но они пусты: $ ls -a a . .. $ git submodule status -d266b9873ad50488163457f025db7cdd9683d88b a -e81d457da15309b4fef4249aba9b50187999670d b -c1536a972b9affea0f16e0680ba87332dc059146 c -d96249ff5d57de5de093e6baff9e0aafa5276a74 d Внимание: Наименование объектов фиксации, отображаемых сверху, будут отличается от ваших, но они должны совпадать в ГОЛОВНЫМ наименованиями объектов фиксации вашего репозитория. Вы можете проверить это запустив `git ls-remote ../git/a`. Выталкивание подмодулей - это двух-шаговый процесс. Первоначально запускаем `git submodule init`для добавления URL подмодуля репозитория в `.git/config`: $ git submodule init Теперь используем `git-submodule update` для клонирования репозитория и закрепления указанной фиксации в главном проекте: $ git submodule update $ cd a $ ls -a . .. .git a.txt Одно из главных отличий между `git-submodule update` и `git-submodule add` это то, что `git-submodule update` закрепляет указанную фиксацию, отличную от вершины ветки. Это как закрепление метки: голова отделяется и таким образом вы не работаете с веткой. $ git branch * (no branch) master Если вы хотите внести изменения в подмодуль и уже отделили голову, тогда вам следует создать или закрепить ветвь, совершить свои изменения, внести их в подмодуль, и затем обновить главный проект для ссылки на новую фиксацию (коммит): $ git checkout master или $ git checkout -b fix-up затем $ echo "снова добавляю строку" >> a.txt $ git commit -a -m "Обновил подмодуль из главного проекта." $ git push $ cd .. $ git diff diff --git a/a b/a index d266b98..261dfac 160000 --- a/a +++ b/a @@ -1 +1 @@ -Subproject commit d266b9873ad50488163457f025db7cdd9683d88b +Subproject commit 261dfac35cb99d380eb966e102c1197139f7fa24 $ git add a $ git commit -m "Обновил подмодуль a." $ git push Вы должны запустить `git submodule update` после `git pull` если вы также хотите обновить подмодули. ### Подводные камни с подмодулями Всегда публикуйте изменения в подмодулях перед тем как, опубликовать изменения главного проекта, ссылающегося на них. Если вы забыли опубликовать изменения в подмодулях, другие не смогут выполнить клонирование репозитория. $ cd ~/git/super/a $ echo я добавил ещё строку в этот файл >> a.txt $ git commit -a -m "делаю это неверно на этот раз" $ cd .. $ git add a $ git commit -m "Обновил подмодуль a снова." $ git push $ cd ~/git/cloned $ git pull $ git submodule update error: pathspec '261dfac35cb99d380eb966e102c1197139f7fa24' did not match any file(s) known to git. А вы не забыли о 'git add'? Невозможно закрепить '261dfac35cb99d380eb966e102c1197139f7fa' в пути подмодуля 'a' Если вы предоставляете обновленный подмодуль для фиксации самостоятельно, будьте осторожны и не добавьте обратный слеш при указании пути. При добавленном слеше Git предположит, что вы удаляете подмодуль и копируете содержимое этой директории в репозиторий, содержащий эту директорию. $ cd ~/git/super/a $ echo я добавил ещё одну строку в этот файл >> a.txt $ git commit -a -m "делаю это неверно на этот раз" $ cd .. $ git add a/ $ git status # В ветке master # Изменения, предоставленные для фиксации: # (используйте "git reset HEAD ..." для удаления) # # удален: a # новый файл: a/a.txt # # Модифицированные подмодули: # # * a aa5c351...0000000 (1): # < Исходный коммит, подмодуль a # Чтобы исправить индекс после выполнения этой операции, сбросьте изменения и затем добавьте подмодуль без замыкающего слэша. $ git reset HEAD A $ git add a $ git status #В ветке master #Изменения, предоставленные для фиксации: #(используйте "git reset HEAD ..." для отката) # # модифицированы: a # # Модифицированные подмодули: # # * a aa5c351...8d3ba36 (1): # > делаю это неверно на этот раз # Вам также не следует возвращаться к веткам подмодуля после фиксаций, записанных в каком-либо суперпроекте. Выполнение команды `git submodule update` является небезопасным, если вы внесли изменения и зафиксировали их в пределах подмодуля не скопировав ветку перед этим. Они будут перезаписаны по умолчанию. $ cat a.txt module a $ echo line added from private2 >> a.txt $ git commit -a -m "line added inside private2" $ cd .. $ git submodule update Submodule path 'a': checked out 'd266b9873ad50488163457f025db7cdd9683d88b' $ cd a $ cat a.txt module a Замечание: Изменения все ещё обозримы в журнале ссылок (reflog) подмодуля. Это не относится к случаю, если вы не зафиксировали свои изменения. [gitcast:c11-git-submodules]("GitCast #11: Git Submodules") ## Git в Windows ## (mSysGit) [gitcast:c10-windows-git]("GitCast #10: Git on Windows") #Работа с Git ## Развертывание с Git ## ### Capistrano и Git ### [Руководство GitHub по развертыванию с помощью Cap](http://github.com/guides/deploying-with-capistrano) [Скринкаст Git и Capistrano](http://www.vimeo.com/369095) ## Интеграция с Subversion ## Миграция из СУВ (Систем Управления Версиями) ## Итак, вы решили перейти с вашей текущей системы и перевести весь ваш проект на Git. Как сделать это без особого труда? ### Импортирование из Subversion ### Git поставляется со скриптом git-svn, который имеет команду clone. Она импортирует subversion-репозиторий в новый git-репозиторий. Существует также бесплатный инструмент, предоставляемый в качестве услуги на GitHub, который сделает это за вас. $ git-svn clone http://my-project.googlecode.com/svn/trunk new-project Вы получите новый Git репозиторий со всей историей исходного репозитория Subversion. Это занимает, как правило, довольно много времени, поскольку, начиная с первой версии, закрепляет и фиксирует локально каждую ревизию по одной. ### Импортирование из Perforce ### В contrib/fast-import вы найдете python-скрипт git-p4, который импортирует из репозитория Perforce за вас. $ ~/git.git/contrib/fast-import/git-p4 clone //depot/project/main@all myproject ### Импортирование из других ### Это другие СУВ, которые находятся в начале списка Git Survey. Необходимо найти документацию по импорту для них. !!TODO!! * CVS * Mercurial (hg) * Bazaar-NG * Darcs * ClearCase ## Графический Git ## Git имеет несколько достаточно популярных GUI, с помощью которых можно читать и/или манипулировать Git репозиториями. ### Включенные GUIs ### Git поставляется с двумя основными программами GUI, написанными на Tcl/Tk. Gitk — инструмент визуализации обозревателя репозиториев и истории фиксаций. [gitk](http://www.kernel.org/pub/software/scm/git/docs/gitk.html) linkgit:git-gui[1] является инструментом, который позволяет визуализировать индексные операции, такие, как добавить, удалить и зафиксировать. Он не будет делать все, что можно сделать в командной строке, но для большинства основных операций его вполне хватает. [git gui](http://www.kernel.org/pub/software/scm/git/docs/git-gui.html) ### Сторонние проекты ### Для пользователей Mac есть [GitX](http://gitx.frim.nl/) и [GitNub](http://github.com/Caged/gitnub/wikis) Для пользователей Linux и остальных пользователей Qt имеется [QGit](http://digilander.libero.it/mcostalba/) ## Хостинг Git ## github repoorcz ## Другие способы использования ## ContentDistribution TicGit ## Написание скриптов и Git ## ### Ruby и Git ### grit jgit + jruby ### PHP и Git ### ### Python и Git ### pygit ### Perl и Git ### perlgit ## Git и текстовые редакторы ## textmate eclipse netbeans ## Каким образом Git хранит объекты ## Эта глава детально объясняет как Git физически хранит объекты. Все объекты хранятся в виде sha-хеш значений их сжатого содержимого. Они содержат тип объекта, размер и содержимое в формате gzip. Существует 2 формата, в котором Git сохраняет объекты - неупакованные и упакованные объекты. ### Неупакованные Объекты ### Неупакованные объекты это простой формат. Это просто сжатый файл, хранящийся в одном файле на диске. Каждый объект записан в отдельный файл. Если sha-хеш вашего объекта соответствует ab04d884140f7b0cf8bbf86d6883869f16a46f65, в таком случае этот файл будет сохранен в директории: GIT_DIR/objects/ab/04d884140f7b0cf8bbf86d6883869f16a46f65 Два первых символа используются как подкаталог, таким образом, в одном каталоге никогда не бывает слишком много объектов. Фактическое имя файла составляют остальные 38 символов. Самый легкий способ описать, как именно данные объекта хранятся это следующая реализация хранения объекта на Ruby: ruby def put_raw_object(content, type) size = content.length.to_s header = "#{type} #{size}\\0" # type(space)size(null byte) store = header + content sha1 = Digest::SHA1.hexdigest(store) path = @git_dir + '/' + sha1[0...2] + '/' + sha1[2..40] if !File.exists?(path) content = Zlib::Deflate.deflate(store) FileUtils.mkdir_p(@directory+'/'+sha1[0...2]) File.open(path, 'w') do |f| f.write content end end return sha1 end ### Упакованные Объекты ### Другим форматом для хранения объектов является файл-упаковка. Т.к. Git хранит каждую версию любого файла как отдельный объект, это может быть довольно неэффективно. Представьте себе, что имеется файл в несколько тысяч строк и изменили одну строку. Git сохранит второй файл полностью, что займет довольно много места. Для того, чтобы сохранить это пространство, Git использует файл-упаковку. Это формат, в котором Git сохранить только часть того, что изменилось во втором файле, с указателем на схожий файл. Когда объекты записываются на диск, это часто происходит в неупакованном формате, т.к. этот формат является менее дорогостоящим для доступа. Тем не менее, в конце концов вы захотите сохранить пространство, упаковав объекты - это осуществимо с помощью команды linkgit:git-gc[1]. Она будет использовать довольно сложную эвристику для определения файлов, которые наиболее близки, и их разницы, основываясь на данном анализе. Может быть несколько файлов-упаковок, в случае необходимости они могут быть созданы заново (linkgit:git-repack[1]) или относительно легко распакованы (linkgit:git-unpack-objects[1]). Git также составляет файл индексации для каждого файла-упаковки, который значительно меньше по размеру и содержит смещение в файле-упаковке, чтобы быстрее найти конкретные объекты по sha-хэшу. Фактические детали реализации файла-упаковки (packfile) находятся далее в главе Файл-упаковка. # Internals and Plumbing # ## Просмотр Git Объектов ## Мы можем спросить git о конкретных объектах с помощью команды cat-file. Обратите внимание, что вы можете сократить sha-хэши лишь на несколько символов, чтобы не печатать все 40 цифр hex: $ git-cat-file -t 54196cc2 commit $ git-cat-file commit 54196cc2 tree 92b8b694ffb1675e5975148e1121810081dbdffe author J. Bruce Fields 1143414668 -0500 committer J. Bruce Fields 1143414668 -0500 initial commit Дерево может ссылаться на один или более объектов типа "blob", каждый из которых соответствует файлу. Кроме того, дерево может также ссылаться на другие объекты типа дерево, таким образом, создавая иерархии каталогов. Вы можете просмотреть содержимое любого дерева с помощью ls-tree (помните, что достаточно длинный начальный участок SHA1 также будет работать): $ git ls-tree 92b8b694 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad file.txt Таким образом, мы видим, что это дерево содержит один файл. SHA1-хэш является ссылкой на данные файла: $ git cat-file -t 3b18e512 blob Объект типа "blob" - это просто файл данных, который мы также можем просмотреть с помощью команды cat-file: $ git cat-file blob 3b18e512 hello world Заметьте, что это старый файл данных; таким образом объект, названный git в ответ на первоначальное дерево, был деревом со статическим срезом состояния каталога, в котором он был записан при первой фиксации. Все эти объекты находятся под их sha1-именами в git каталоге: $ find .git/objects/ .git/objects/ .git/objects/pack .git/objects/info .git/objects/3b .git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad .git/objects/92 .git/objects/92/b8b694ffb1675e5975148e1121810081dbdffe .git/objects/54 .git/objects/54/196cc2703dc165cbd373a65a4dcf22d50ae7f7 .git/objects/a0 .git/objects/a0/423896973644771497bdc03eb99d5281615b51: .git/objects/d0 .git/objects/d0/492b368b66bdabf2ac1fd8c92b39d3db916e59 .git/objects/c4 .git/objects/c4/d59f390b9cfd4318117afde11d601c1085f241 и содержимое этих файлов является только сжатыми данными плюс заголовок, определяющий их длину и тип. Тип может быть "blob", дерево (tree), фиксация (commit), или тэг (tag). Простейшая фиксация - это HEAD, которую мы можем найти начиная с .git/HEAD: $ cat .git/HEAD ref: refs/heads/master Как вы можете видеть, это говорит нам, на какой ветке мы находимся в данный момент, и делает это названием файла в .git каталоге, который сам по себе содержит SHA1 имя, относящееся к commit-объекту, который мы можем исследовать с помощью cat-file: $ cat .git/refs/heads/master c4d59f390b9cfd4318117afde11d601c1085f241 $ git cat-file -t c4d59f39 commit $ git cat-file commit c4d59f39 tree d0492b368b66bdabf2ac1fd8c92b39d3db916e59 parent 54196cc2703dc165cbd373a65a4dcf22d50ae7f7 author J. Bruce Fields 1143418702 -0500 committer J. Bruce Fields 1143418702 -0500 add emphasis Объект типа "tree" в данном случае ссылается на новое состояние дерева: $ git ls-tree d0492b36 100644 blob a0423896973644771497bdc03eb99d5281615b51 file.txt $ git cat-file blob a0423896 hello world! и родительский объект ссылается на предыдущую фиксацию: $ git-cat-file commit 54196cc2 tree 92b8b694ffb1675e5975148e1121810081dbdffe author J. Bruce Fields 1143414668 -0500 committer J. Bruce Fields 1143414668 -0500 ## Ссылки Git ## Branches, remote-tracking branches, and tags are all references to Все ссылки имеют названия, которые состоят из слэш-разделенного пути к файлу и начинаются с "refs"; имена, которыми мы пользовались до сих пор, на самом деле условные обозначения: - Ветка "test" является аббревиатурой "refs/heads/test". - Тэг "v2.6.18" является короткой версией "refs/tags/v2.6.18". - "origin/master" укороченное "refs/remotes/origin/master". Полное название иногда полезно, если, например, существует тэг и ветка с таким же названием. (Вновь созданные ссылки на самом деле хранятся в каталоге .git/refs, путь ссылок задан их именем. Тем не менее, в пользу эффективности они могут быть упакованы в один файл; см. linkgit:git-pack-refs[1]). Еще один полезный ярлык, на "HEAD" репозитория можно ссылаться по имени этого репозитория. Так, например, "origin" обычно является ярлыком ветки HEAD репозитория "origin". Для полного списка путей, проверяемых на ссылки, и информации о порядке, используемом для решения выбора из нескольких ссылок с одинаковым ярлыком, см. раздел linkgit:git-rev-parse[1] в "Уточнение ревизий". ### Просмотр уникальных фиксаций ветки ### Предположим вы хотите просмотреть все фиксации доступные из головы ветки "master", не учитывая фиксаций в других головах репозитория. Перечислить все головы репозитория можно с помощью linkgit:git-show-ref[1]: $ git show-ref --heads bf62196b5e363d73353a9dcf094c59595f3153b7 refs/heads/core-tutorial db768d5504c1bb46f63ee9d6e1772bd047e05bf9 refs/heads/maint a07157ac624b2524a059a3414e99f6f44bebc1e7 refs/heads/master 24dbc180ea14dc1aebe09f14c8ecf32010690627 refs/heads/tutorial-2 1e87486ae06626c2f31eaa63d26fc0fd646c8af2 refs/heads/tutorial-fixes Мы можем получить только названия ветвей-голов, не учитывая "master", с помощью стандартных утилит cut и grep: $ git show-ref --heads | cut -d' ' -f2 | grep -v '^refs/heads/master' refs/heads/core-tutorial refs/heads/maint refs/heads/tutorial-2 refs/heads/tutorial-fixes А затем мы можем осуществить запрос списка всех фиксаций доступных из главной ветки (master), но недоступных из других веток: $ gitk master --not $( git show-ref --heads | cut -d' ' -f2 | grep -v '^refs/heads/master' ) Очевидно, что возможно бесконечное число вариаций; например, чтобы просмотреть список фиксаций доступных из какой-нибудь головы, но не из какой-либо метки репозитория: $ gitk $( git show-ref --heads ) --not $( git show-ref --tags ) (См. linkgit:git-rev-parse[1] для пояснений синтаксиса отбора фиксаций, такого как `--not`.) (!!update-ref!!) ## Индекс Git ## Индекс - бинарный файл (обычно находится в .git/index), содержащий отсортированный список путей с соответствующими правами доступа и SHA1 объекта типа blob; linkgit:git-ls-files[1] может показать содержимое индекса: $ git ls-files --stage 100644 63c918c667fa005ff12ad89437f2fdc80926e21c 0 .gitignore 100644 5529b198e8d14decbe4ad99db3f7fb632de0439d 0 .mailmap 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0 COPYING 100644 a37b2152bd26be2c2289e1f57a292534a51a93c7 0 Documentation/.gitignore 100644 fbefe9a45b00a54b58d94d06eca48b03d40a50e0 0 Documentation/Makefile ... 100644 2511aef8d89ab52be5ec6a5e46236b4b6bcd07ea 0 xdiff/xtypes.h 100644 2ade97b2574a9f77e7ae4002a4e07a6a38e46d07 0 xdiff/xutils.c 100644 d5de8292e05e7c36c4b68857c1cf9855e3d2f70a 0 xdiff/xutils.h Обратите внимание, что в старой документации индекс может быть назван "кэш текущей директории" (current directory cache) или просто "кэш" (cache). Он имеет 3 важных свойства: 1. Индекс содержит всю информацию, необходимую для создания единственного (уникально определенного) tree-объекта. Например, запуск linkgit:git-commit[1] создает такой объект типа tree из индекса, сохраняет его в базе данных объектов и использует его как tree-объект, связанный с новым коммитом. 2. Индекс делает возможным быстрое сравнение установленных объектов типа tree с рабочим деревом. Он делает это благодаря сохранению некоторой дополнительной информации для каждой записи (такой как последнее время изменения). Эти данные не отображаются выше и не сохраняются в созданном tree-объекте, но могут быть использованы для быстрого определения отличий между файлами текущей директории и тем, что сохранено в индексе, и таким образом избавляет git от чтения всех данных таких файлов в поисках изменений. 3. Он может эффективно представить информацию о конфликтах слияний между различными объектами типа дерево, что позволяет каждому пути быть ассоциированным с достаточной информацией о деревьях, используемых для создания тройного слияния. Во время слияния, индекс может содержать несколько версий одного файла (называется "stages"). Третья колонка выхода команды linkgit:git-ls-files[1], приведенного ранее, - номер представления (stage), может принять значение отличное от нуля для файлов, имеющих конфликты слияния. Таким образом, индекс в каком-то роде временная область представления, наполненная деревьями, с которыми работают на данный момент. ## Файл-Упаковка (Packfile) ## Эта глава подробно объясняет, как форматируются файл-упаковка и его индексные файлы. ### Индекс Упакованного Файла ### Для начала, у нас есть индекс упакованного файла, который в основном только последовательность закладок в файле-упаковке. Существует две версии индекса для упакованных файлов - версия 1, которая используется по умолчанию в ранних версиях Git, до 1.6, и версия 2, которая используется по умолчанию с версии 1.6, но может быть понятна версиям git начиная с 1.5.2 и опускаясь вниз до версии 1.4.4.5, если вы все ещё пользуетесь версиями 1.4. Версия 2 также включает в себя контрольную сумму CRC каждого объекта, таким образом сжатые данные могут быть скопированы при переупаковке непосредственно из одной упаковки в другую не имея незамеченных повреждений данных. Индексы версии 2 также могут обрабатывать файлы-упаковки превышающие по размеру 4 Гб. [fig:packfile-index] В обоих форматах, таблица разветвлений это просто способ быстрее найти смещение определенного sha-хэша в файле-индекса. Таблицы смещения/sha1[] сортируются по значению sha1[] (это позволяет применить бинарный поиск в этой таблице), а таблица смещений специфически указывает на таблицу смещения/sha1[] (так, что часть последней таблицы, которая содержит все хэши, начинающиеся с заданного байта, может быть найдена, избежав 8 итераций бинарного поиска). В версии 1, смещения и хэши находятся в том же пространстве, где и в версии 2; существуют отдельные таблицы для sha-хэшей, контрольных сумм CRC и смещений. В конце обоих файлов находятся sha-хэши контрольных сумм для индексного и упакованного файлов, на которые она (таблица) ссылается. Важно отметить, что индексы упакованных файлов *не* нужны при при распаковке объектов, они просто используются для *быстрого* извлечения отдельных объектов из упаковки. Упакованный формат используется в программах загрузки и получения пакетов (протоколы продвижения и доставки) для передачи объектов, в таком случае индекс не используется - он может быть создан после сканирования упакованного файла. ### Формат Файла-Упаковки ### Файл-упаковка сам по себе имеет очень простой формат. Он имеет заголовок, последовательность упакованных объектов (каждый со своим собственным заголовком и телом) а затем завершается контрольной суммой. Первые 4 байта это строка 'PACK', которая используется, чтобы убедиться, что вы имеете правильное начало упакованного файла. За ними следуют четыре байта для версии фала-упаковки и затем 4-ех байтовое число для количества записей в этом файле. На Ruby вы можете прочесть данные заголовка следующим образом: ruby def read_pack_header sig = @session.recv(4) ver = @session.recv(4).unpack("N")[0] entries = @session.recv(4).unpack("N")[0] [sig, ver, entries] end После этого вы получаете ряд упакованных объектов, в порядке их SHA-хэша, где каждый объект имеет заголовок и содержимое. В конце файла-упаковки находится 20-ти байтовая сумма SHA1-хэшей (отсортированных) этого файла. [fig:packfile-format] Заголовок объекта - это последовательность из 1 или более кусков по 1 байту (8 бит), которая указывает тип объекта текущих данных и их размер при распаковке. Каждый байт на самом деле это 7 бит данных, а первый бит говорит, является ли этот кусок последним или находится до начала данных. Если первый бит - 1, вы прочтете еще один байт, иначе - данные начинаются за ним. Первые 3 бита в первом байте определяют тип данных в соответствии с приведенной ниже таблицей. (В настоящее время из 8 значений, которые могут быть выражены 3-мя битами (0-7), 0 (000) является 'неопределенным' и 5 (101) ещё не использовано.) Здесь мы видим пример заголовка в 2 байта, где первый указывает, что следующие данные являются фиксацией, и остаток первых и последних 7 бит второго говорит, что данные займут 144 байт при распаковке. [fig:packfile-logic] Важно отметить, что размер, указанный в заголовке данных не является размером следующих данных на самом деле, но является таковым *при распаковке*. Именно по этой причине смещения в индексе файла-упаковки являются столь полезными, иначе вам пришлось бы распаковывать каждый объект, только для того, чтобы знать, когда начинается следующий заголовок. Часть данных является лишь zlib потоком для не-разностных (non-delta) типов объектов; для двух разностных представлений объекта, часть данных содержит то, что определяет, от каких базовых объектов зависит это представление разности, и разность, которую необходимо применить к объекту для его восстановления. ref-delta использует 20-байтовый хеш базового объекта в начале данных, тогда как ofs-delta хранит смещение в пределах одного файла-упаковки для определения базового объекта. В любом случае, два важных ограничения, которых должен придерживаться исполнитель, таковы: * представление разности (delta) должно быть также основано на других объектах в том же файле-упаковке; * Базовый объект должен быть одного и того же базового типа ("blob", дерево, коммит или тэг); ## Низкоуровневый Git ## Здесь мы рассмотрим, как манипулировать git на более низком уровне, на случай если вы захотите написать инструмент, который создает новые объекты типа "blob", деревья или фиксации более изощренным способом. Если вы хотите написать скрипт, который использует [git plumbing] для новых задач на более низком уровне, вот некоторые из инструментов, которые вам понадобятся. ### Создание объектов типа blob ### Создание blob-объекта в репозитории Git и получить его sha-хэш довольно легко. Команда linkgit:git-hash-object[1] - это все, что вам нужно. Чтобы создать blob-объект из существующего файла, просто запустите ее с опцией '-w' (которая говорит записать blob-объект, а не только вычислить sha-хэш). $ git hash-object -w myfile.txt 6ff87c4664981e4397625791c8ea3bbb5f2279a3 $ git hash-object -w myfile2.txt 3bb0e8592a41ae3185ee32266c860714980dbed7 Вывод STDOUT команды будет SHA-хэшем blob-объекта, который был создан. ### Создание деревьев ### Предположим, вы хотите создать дерево из ваших новых объектов. Команда linkgit:git-mktree[1] делает это с легкостью, создавая новые объекты типа дерево из форматированного выхода linkgit:git-ls-tree[1]. Например, если вы записываете следующий файл с именем '/tmp/tree.txt' : 100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3 file1 100644 blob 3bb0e8592a41ae3185ee32266c860714980dbed7 file2 а затем направили через 'pipe' команде linkgit:git-mktree[1], Git запишет новое дерево в базу данных объектов и вернет новый sha-хэш этого дерева. $ cat /tmp/tree.txt | git mk-tree f66a66ab6a7bfe86d52a66516ace212efa00fe1f Тогда мы можем использовать его и сделать подкаталогом еще одного дерева, и т.д. Если бы мы хотели создать новое дерево и использовать его в качестве поддерева, мы бы просто создали новый файл (/tmp/newtree.txt) с нашим новым sha-хэшем как деревом внутри: 100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3 file1-copy 040000 tree f66a66ab6a7bfe86d52a66516ace212efa00fe1f our_files а затем использовали linkgit:git-mk-tree[1] снова: $ cat /tmp/newtree.txt | git mk-tree 5bac6559179bd543a024d6d187692343e2d8ae83 Теперь мы имеем искусственную структуру каталога Git, который выглядит следующим образом: . |-- file1-copy `-- our_files |-- file1 `-- file2 1 directory, 3 files без существования этой структуры на диске. Плюс, у нас есть sha-хэш (5bac6559), который указывает на дерево. ### Реорганизация деревьев ### Мы также можем манипулировать деревьями, объединяя их в новые структуры, используя файл индексов. В качестве простого примера, давайте возьмем дерево, которое мы только что создали, и создадим новое, которое будет состоять из двух копий нашего 5bac6559-дерева, используя временный файл индексов. (Вы можете сделать это путем сброса переменной окружения GIT_INDEX_FILE или из командной строки) Сначала мы прочтем дерево в файл индексов под новым префиксом при помощи команды linkgit:git-read-tree[1], а затем запишем индекс содержания как дерево, используя команду linkgit:git-write-tree[1]: $ export GIT_INDEX_FILE=/tmp/index $ git read-tree --prefix=copy1/ 5bac6559 $ git read-tree --prefix=copy2/ 5bac6559 $ git write-tree bb2fa6de7625322322382215d9ea78cfe76508c1 $>git ls-tree bb2fa 040000 tree 5bac6559179bd543a024d6d187692343e2d8ae83 copy1 040000 tree 5bac6559179bd543a024d6d187692343e2d8ae83 copy2 Теперь, как видим, мы создали новое дерево только манипулируя индексами. Вы также можете осуществлять интересные операции слияния и прочее, используя таким образом временные индексы - см. документацию linkgit:git-read-tree[1] для дополнительной информации. ### Создание фиксаций ### Теперь, когда у нас есть sha-хэш дерева, мы можем создать коммит-объект, который будет ссылаться на него. Мы можем сделать это, используя команду linkgit:git-commit-tree[1]. Большинство данных, которые входят в фиксацию, должны быть установлены в качестве переменных окружения, так что вы захотите установить следующее: GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE Тогда вам следует записать сообщение коммита в файл или используя 'pipe' направить его в команду с помощью STDIN. После этого, вы сможете создать коммит своего объекта, основываясь на sha-хэше имеющегося дерева. $ git commit-tree bb2fa < /tmp/message a5f85ba5875917319471dfd98dfc636c1dc65650 Если вы хотите задать один или несколько родительских фиксаций, просто добавьте sha-хэши в командную строку указав опцию '-p' перед каждым. SHA-хэш новой фиксации объекта будет возвращен в STDOUT. ### Обновление ссылок ветки ### Теперь, когда у нас есть новый SHA-хэш коммит-объекта, мы можем обновить ветку, если хотим, чтобы она указывала на эту фиксацию. Допустим, мы хотим обновить нашу ветвь 'master' так, чтобы она указывала на только что созданную фиксацию - мы будем использовать команду linkgit:git-update-ref[1]: $ git update-ref refs/heads/master a5f85ba5875917319471dfd98dfc636c1dc65650 ## Протоколы передачи ## Здесь мы рассмотрим как клиенты и серверы общаются друг с другом для обмена данными Git. ### Извлечение данных по HTTP ### Извлечение данных по URL http/s Git использует слегка тупой протокол. В этом случае вся логика полностью выполняется на стороне клиента. Сервер не требует специальной установки - любой статический веб-сервер подойдет, если git каталог, данные которого извлекаются, имеет путь доступный для веб-сервера. Для того, чтобы это работало, вам необходимо запускать одну команду на сервере репозитория каждый раз когда что-нибудь обновится, несмотря на linkgit:git-update-server-info[0], которая обновляет файлы объектов/информации/пакетов и информации/ссылок для перечисления доступных ссылок и файлов-упаковок, т.к. вы не можете этого сделать по http. Когда вы запустите эту команду, файл объектов/информации/упаковок выглядит следующим образом: P pack-ce2bd34abc3d8ebc5922dc81b2e1f30bf17c10cc.pack P pack-7ad5f5d05f5e20025898c95296fe4b9c861246d8. Так что, если при извлечении невозможно найти распакованный файл, он может попробовать извлечь эти упакованные файлы. Тогда файл информации/ссылок будет выглядеть так: 184063c9b594f8968d61a686b2f6052779551613 refs/heads/development 32aae7aef7a412d62192f710f2130302997ec883 refs/heads/master Затем, при извлечении данных из этого репозитория, он начнет с этих ссылок и будет обходить все коммит-объекты пока клиент не получит все необходимые объекты. Например, если вы запросите данные ветки 'master', он увидит, что 'master' указывает на 32aae7ae и что ваш 'master' указывает на ab04d88, поэтому вам нужен 32aae7ae. Вы извлекаете этот объект: CONNECT http://myserver.com GET /git/myproject.git/objects/32/aae7aef7a412d62192f710f2130302997ec883 - 200 и это выглядит так: tree aa176fb83a47d00386be237b450fb9dfb5be251a parent bd71cad2d597d0f1827d4a3f67bb96a646f02889 author Scott Chacon 1220463037 -0700 committer Scott Chacon 1220463037 -0700 added chapters on private repo setup, scm migration, raw git Таким образом, сейчас запрашивается дерево aa176fb8: GET /git/myproject.git/objects/aa/176fb83a47d00386be237b450fb9dfb5be251a - 200 что выглядит как: 100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3 COPYING 100644 blob 97b51a6d3685b093cfb345c9e79516e5099a13fb README 100644 blob 9d1b23b8660817e4a74006f15fae86e2a508c573 Rakefile Далее он запрашивает следующие объекты: GET /git/myproject.git/objects/6f/f87c4664981e4397625791c8ea3bbb5f2279a3 - 200 GET /git/myproject.git/objects/97/b51a6d3685b093cfb345c9e79516e5099a13fb - 200 GET /git/myproject.git/objects/9d/1b23b8660817e4a74006f15fae86e2a508c573 - 200 Он делает это при помощи Curl, и может открыть несколько параллельных потоков, что может ускорить процесс. Когда рекурсивный обход дерева, на которое указывает фиксация, окончен, он извлекает следующего родителя. GET /git/myproject.git/objects/bd/71cad2d597d0f1827d4a3f67bb96a646f02889 - 200 В этом случае фиксация, которая возвращается, выглядит следующим образом: tree b4cc00cf8546edd4fcf29defc3aec14de53e6cf8 parent ab04d884140f7b0cf8bbf86d6883869f16a46f65 author Scott Chacon 1220421161 -0700 committer Scott Chacon 1220421161 -0700 added chapters on the packfile and how git stores objects и мы можем видеть, что родитель ab04d88 находится там, куда указывает наша 'master'-ветка в данный момент. Таким образом, мы рекурсивно извлечем это дерево, а затем остановимся, так как мы знаем, что имеем все до этого места. Вы можете заставить Git перепроверить, что мы имеем все с помощью опции '--recover'. См. linkgit:git-http-fetch[1] для информации. Если один из неупакованных объектов извлечь не удается, Git скачает индексы упакованных файлов в поисках необходимого sha-хэша, затем скачает этот файл-упаковку. Если вы содержите git сервер, обслуживающий репозиторий, важно обеспечить запуск команды 'git update-server-info' каждый раз по получении запроса, иначе не избежать путаницы. ### Извлечение данных с помощью Upload Pack ### При использовании более умных протоколов, извлечение объектов значительно эффективнее. Используется открытый сокет по ssh или через порт 9418 (в случае протокола git://), и команда linkgit:git-fetch-pack[1] на стороне клиента начинает общение с ответвленным процессом linkgit:git-upload-pack[1] на стороне сервера. Затем сервер говорит клиенту, какие SHA-хэши он имеет на каждую ссылку, и клиент, выясняет, что ему надо и отсылает в ответ список SHA-хэшей, которые ему нужны и которыми уже обладает. На этой стадии, сервер сгенерирует упакованный файл всех объектов, которые нужны клиенты начнет отправку клиенту. Давайте рассмотрим пример. Клиент подключается, и посылает заголовок запроса. Команда клонирования $ git clone git://myserver.com/project.git создает следующий запрос: 0032git-upload-pack /project.git\\000host=myserver.com\\000 Первые четыре байта содержат шестнадцатеричную длину строки (в том числе 4 байта длины строки и, если присутствует, завершающий символ новой строки). За ними следует команда и аргументы. Затем следует нулевой байт, а затем информация о хосте. Запрос завершается нулевым байтом. Запрос обрабатывается и превращается в вызов git-upload-pack: $ git-upload-pack /path/to/repos/project.git Это сразу же возвращает информацию о репозитории: 007c74730d410fcb6603ace96f1dc55ea6196122532d HEAD\\000multi_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug 003d5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/dist 003e7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 refs/heads/local 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master 0000 Каждая строчка начинается с четырех байт определения длины строки в шестнадцатеричном виде. Секция заканчивается вводом длины 0000. Это отправляется клиенту обратно в дословном виде. Клиент отвечает другим запросом: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack side-band-64k ofs-delta 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a 0032want 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 0032want 74730d410fcb6603ace96f1dc55ea6196122532d 00000009done Это отправляется в открытый процесс git-upload-pack, который затем отправляет финальный ответ: "0008NAK\n" "0023\\002Counting objects: 2797, done.\n" "002b\\002Compressing objects: 0% (1/1177) \r" "002c\\002Compressing objects: 1% (12/1177) \r" "002c\\002Compressing objects: 2% (24/1177) \r" "002c\\002Compressing objects: 3% (36/1177) \r" "002c\\002Compressing objects: 4% (48/1177) \r" "002c\\002Compressing objects: 5% (59/1177) \r" "002c\\002Compressing objects: 6% (71/1177) \r" "0053\\002Compressing objects: 7% (83/1177) \rCompressing objects: 8% (95/1177) \r" ... "005b\\002Compressing objects: 100% (1177/1177) \rCompressing objects: 100% (1177/1177), done.\n" "2004\\001PACK\\000\\000\\000\\002\\000\\000\n\\355\\225\\017x\\234\\235\\216K\n\\302"... "2005\\001\\360\\204{\\225\\376\\330\\345]z\226\273"... ... "0037\\002Total 2797 (delta 1799), reused 2360 (delta 1529)\n" ... "<\\276\\255L\\273s\\005\\001w0006\\001[0000" См. главу Файл-упаковка (Packfile) для фактического формата упакованных данных ответа. ### Отправка данных ### Отправка данных через git и ssh протокол аналогична, но проще. В основном, происходит следующее: клиент запрашивает единицу получаемого пакета, который принимается, если клиент имеет право доступа, тогда сервер возвращает все имеющиеся sha-хэши ссылок на заголовки и клиент создает упакованный файл из всего, что необходимо серверу (в основном, только в том случае, если сервер является прямым предком передаваемой информации), и передает его серверу, который сохраняет его на диске и создает для него файл индекса, или распаковывает (если в упаковке не много объектов). Весь этот процесс осуществляется при использовании команды linkgit:git-send-pack[1] на стороне клиента, которая вызывается командой linkgit:git-push[1], и команды linkgit:git-receive-pack[1] на стороне сервера, которая инициализируется во время процесса соединения по ssh или демоном git (если это открытый сервер для передачи). ## Словарь ## Здесь мы имеем значения некоторых терминов, используемых в контексте Git. Эти термины были полностью скопированы из [Словаря Git (англ.)](http://www.kernel.org/pub/software/scm/git/docs/gitglossary.html). _alternate object database_ >Используя механизм дублирования, репозиторий может унаследовать часть объектов её базы данных через другую базу данных объектов, которая называется "alternate" (дублер). _bare repository_ > Bare (чистый) репозиторием обычно называют каталог с суффиксом `.git`, не имеющий под контролем версий ни одной локально извлеченной копии какого-либо файла. Т.е. все административные файлы и файлы управления `git`, которые обычно присутствуют в скрытом подкаталоге `.git`, непосредственно присутствуют в каталоге `repository.git`, и любые другие файлы отсутствуют и не извлекаются. Обычно создатели публичных репозиториев делают их доступными. _blob object_ Объект без типа, т.е. содержимое файла. _branch_ > "Branch" (ветка) - это активная линия разработки. На последнюю фиксацию ветки ссылаются как на конец этой ветки. На конец ветки ссылается голова ветки, которая растет по мере дальнейших работ на ветке. Один git репозиторий может отслеживать любое число веток, но ваше рабочее дерево будет ассоциировано с одной из них (с "current" (текущая) или "checked out" (извлеченная) веткой), и голова будет указвает на эту ветвь. _cache_ > Устаревшее для: index (индекс). _chain_ Список объектов, каждый объект в котором содержит ссылку на его наследника (для примера, наследник коммита может быть одним из его родителей). _changeset_ BitKeeper/cvsps умеет передавать коммиты. Поскольку git хранит не изменения, а все данные, он совсем не нуждается в использовании термина "изменение данных". _checkout_ Действия по обновлению всех частей рабочего дерева, с объектом дерева , или части от объекта базы данных, и обновления индекса и HEAD если целое рабочее дерево отмечено в новой ветке. _cherry-picking_ На SCM-сленге, "cherry pick" означает выбрать подмножество изменений из серии изменений (типичных коммитов) и записать их как новые серии изменений в top другого репозитория. В GIT, это выполненный посредством "git cherry-pick" команды, для извлечения введенных изменений через существующий коммит и запись их, размещая в верхушку существующей ветки, как новый коммит. _clean_ Рабочее дерево считается чистым, если соответствует ревизии, текущей по главной ветке. Аналогично можно представить грязное. _commit_ > Как существительное: Одна точка в истории git; вся история проекта представляется в виде множества взаимосвязанных фиксаций. Слово "commit" (коммит) часто используется git в местах, где в других системах контроля версий пользуются словами "revision" (ревизия) или "version" (версия). Также используется как краткое имя для объекта типа коммит. > Как глагол: Действие сохранения нового мгновенного снимка состояния проекта в истории git путем создания новой фиксации, представляющей нынешнее состояние индекса, и создание указателя на новую фиксацию для HEAD. _commit object_ > Объект, который содержит информацию о конкретной ревизии, такую как родителей, коммиттера, автора, дату и объект типа дерево, который соответствует корневому каталогу хранящейся ревизии. _core git_ > Фундаментальные структуры данных и утилиты git. Предоставляет лишь ограниченный набор инструментов для управления исходным кодом. _DAG_ > Направленный ациклический граф. Коммит-объекты образуют направленный ациклический граф, потому что они имеют родителей (направленных), и ациклический граф из коммит-объектов (нет цепи, которая начинается и оканчивается тем же объектом). _dangling object_ > Недостижимый объект, который не достижим даже для других недостижимых объектов; оборванный объект не имеет на себя ссылок или ссылающихся объектов в репозитории. _detached HEAD_ > Обычно HEAD хранит имя ветки. Тем не менее, git также позволяет получить произвольную фиксацию, которая не обязательно является концом определенной ветки. В этом случае HEAD считается "detached" (отделенной). _dircache_ > Вы *отстааааали*. См. индекс. _directory_ > Список, который вы получаете командой "ls" :-) _dirty_ > Рабочее дерево считается грязным ("dirty"), если оно содержит изменения, которые не были зафиксированы в текущей ветке. _ent_ > Любимый синоним деревянному для полных гиков. См. `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` для подробного объяснения. Избегайте этого термина, чтобы не путать людей. _evil merge_ > Evil merge (злобное слияние) - слияние, которое вносит изменения не появляющиеся ни в одном родителе. _fast forward_ > "Fast-forward" представляет собой особый тип слияния, при котором у вас имеется ревизия и вы создаете слияние изменений другой ветки, которая является потомком того, что вы имеете. В таких случаях вы не создадите фиксации нового слияния, но вместо этого просто обновите до этой ревизии. Это будет часто происходить на отслеживаемой ветке удаленного репозитория. _fetch_ > Извлечение ветки означает получение ссылки на голову ветки из удаленного репозитория, получение информации об объектах отсутствующих в локальной базе данных, а также их получение. См. также linkgit:git-fetch[1]. _file system_ > Линус Торвальдс изначально создал git в качестве файловой системы пользовательского пространства, т.е. как инфраструктуру для содержания файлов и каталогов. Это обеспечило git эффективность и скорость. _git archive_ > Синоним репозитория (). _grafts_ > Позволяет объединить две различных линии разработки путем записи поддельной родословной информации для фиксации. Таким образом вы позволите git представиться множеством родителей фиксации, что будет отличаться от записанного при создании фиксации. Настраивался через файл `.git/info/grafts`. _hash_ > В контексте git, синонимом имени объекта. _head_ > Именованная ссылка на фиксацию в конце ветки. Храняться `$GIT_DIR/refs/heads/`, кроме случаев использования упакованных ссылок. (См. linkgit:git-pack-refs[1].) _HEAD_ > Текущая ветка. Более подробно: ваше рабочее дерево, как правило, получается из состояния дерева, на которое ссылается HEAD. HEAD - это ссылка на одну из голов в вашем репозитории, кроме случаев использования отделенной HEAD, когда она может ссылаться на произвольный коммит. _head ref_ > Синоним "head" (головы). _hook_ > Во время нормального выполнения некоторых git команд, выполняются вызовы необязательных скриптов, которые позволяют разработчикам добавлять функциональность или проверку. Как правило, hook'и позволяют предварительно проверить и, возможно, прервать команду, а также послать уведомление после её выполнения. Эти скрипты находятся в каталоге `$GIT_DIR/hooks/` и включаются просто убрав суффикс `.sample` из названия файла. В более ранних версиях git вам необходимо сделать их исполняемыми. _index_ Набор файлов статистики, чьё содержание хранится как объект. Индекс - это хранящаяся версия вашего рабочего дерева. Правду говоря, он также может содержать вторую и даже третью версию рабочего дерева, которые используются при слиянии. _index entry_ > Информация о конкретном файле, хранящемся в индексе. Индекс может быть получен в процессе обратном слиянию, если слияние было начато, но ещё не окончено (т.е. если индекс имеет несколько версий этого файла). _master_ > Ветка разработки по умолчанию. Всякий раз, когда вы создаете репозиторий git, также создается ветка с именем "master" и становится активной веткой. В большинстве случаев она содержит локальную разработку, хотя это чисто условно и не является необходимым. _merge_ > Как глагол: внести содержание одной ветки (возможно из внешнего репозитория) в текущую ветку. В случае, когда вносимая ветка из другого репозитория, это осуществимо путем получения удаленной ветки и затем слиянием результата с текущей веткой. Эта комбинация операции получения и слияния называется "pull". Слияние осуществляется как автоматический процесс, который определяет изменения, внесенные после того, как ветви разошлись, и затем применяет все эти изменения сразу. В случаях, когда изменения имеют конфликт, может потребоваться ручное вмешательство для завершения слияния. > Как существительное: если он не является "fast forward" (быстрая перемотка вперед), успешное слияние приводит к созданию нового коммита, представляющего собой результат слияния, и имеющего в качестве родителей концы объединённых ветвей. Этот коммит называется "merge commit" (фиксация слияния), а иногда просто "merge" (слияние). _object_ > Единица хранения git. Однозначно задается содержанием SHA1-хэша. Следовательно, объект не может быть изменен. _object database_ Хранит множество объектов, а также является отдельным объектом идентифицируемым по его объектному имени. Объекты обычно находятся в `$GIT_DIR/objects/`. _object identifier_ > Синоним имени объекта. _object name_ > Уникальный идентификатор объекта. Хэш содержимого объекта, который получен используя Secure Hash Algorithm 1 и обычно представляется как 40 шестнадцатеричных символов хэша объекта. _object type_ > Один из идентификаторов "commit", "tree", "tag" или "blob", описывающий тип объекта. _octopus_ Для объединения более двух ветвей. Кроме того, значит умный хищник. _origin_ Основной репозиторий по умолчанию. Большинство проектов имеют по крайней мере один такой отслеживаемый репозиторий. По умолчанию, для этой цели используется 'origin'. Новые основные обновления будут получены следящими удаленными ветками, назваными origin/имя-основной-ветки; их можно увидеть используя "`git branch -r`". _pack_ > Множество объектов, которые сжаты в один файл (для экономии места или эффективной их передачи). _pack index_ Список идентификаторов и другой информации объекта в упакованном виде для помощи в эффективном доступе к содержимому упаковки. _parent_ > Коммит-объект содержит (возможно, пустой) список логических предков по линии разработки, т.е. его родителей. _pickaxe_ > Термин "pickaxe" относится к опции процедуры diffcore, помогающей выбрать изменения, добавлять или удалять заданную строку текста. С опцией `--pickaxe-all` она (процедура) может быть использована для просмотра полного списка изменений, при которых внесли или удалили, скажем, определенную строку текста. См. linkgit:git-diff[1]. _plumbing_ > Милое название основ git. _porcelain_ Милое название программ и программных пакетов, зависящих от основы git и представляющих высокий уровень доступа к ней. Они раскрывают больше интерфейса SCM, чем "plumbing". _pull_ > Значит получение копии и выполнение её слияния. См. также linkgit:git-pull[1]. _push_ > Значит получить ссылку на голову ветки из удаленного репозитория, узнать, является ли она прямым предком ссылки на голову локальной ветки, и в таком случае поместить все объекты, доступные по ссылке этой локальной головы, и отсутствующие в удаленном репозитории, в удаленную объектную базу данных, и обновить ссылку удаленной головы. Если удаленная голова не является предком локальной головы, операция не удается. _reachable_ > Всех предков данной фиксации называют "reachable" (доступными) из этого коммита. В общем смысле, один объект доступен из другого, если мы можем достигнуть один из другого по цепочке, которая следует от тега к тегу, от фиксации к её родителю или деревьям, и от дерева к дереву или объекту типа "blob", который они содержат. _rebase_ > Чтобы повторно применить ряд изменений к ветке на другой основе, и в результате осуществить сброс головы этой ветки. _ref_ 40-байтовое шестнадцатеричное представление sha1-хэша или имя, обозначающее конкретный объект. Могут хранится в $GIT_DIR/refs/`. _reflog_ Показывает локальную историю для ref. Другими словами, может сказать вам, что последняя третья ревизия произошла в репозитории _this_ , и каким было текущее состояние в этом репозитории, вчера в 9:14 вечера. См. linkgit:git-reflog[1] для подробностей. _refspec_ > "refspec" используется командами fetch и push для описания соответствий удаленной и локальной ссылок (ref). Они соединены двоеточием в формате : с предшествующим необязательным знаком сложения +. Например: `git fetch $URL refs/heads/master:refs/heads/origin` значит "взять голову ветки 'master' из $URL и сохранить её как голову моей оригинальной ветки". И `git push $URL refs/heads/master:refs/heads/to-upstream` значит "объявить голову моей ветки 'master' как голову к основной ветке по $URL". См. также linkgit:git-push[1]. _repository_ Собрание вместе ссылок и всех объектов объектной БД, которые доступны по ссылкам; возможно сопровождается мета-данными из одного или нескольких "porcelain". Репозитории могут разделять базы данных объектов с другими репозиториями через механизм замещения. _resolve_ > Действие исправления вручную того, что осталось после неудачного автоматического слияния. _revision_ > Определенное состояние файлов и каталогов, которые хранятся в объектной базе данных. На него ссылается коммит-объект. _rewind_ Забросить часть разработки, т.е. назначить головой более раннюю версию. _SCM_ > Управление исходным кодом (инструмент). _SHA1_ > Синоним имени обьекта. _shallow repository_ Такой репозиторий может иметь неполную историю, т.к. его фиксации утеряли предков (другими словами, git сказано делать вид, что эти фиксации не имеют родителей, даже если они записаны в коммит-объекте). Иногда это полезно, когда вы заинтересованы только в недавней истории проекта, даже если настоящая текущая история намного больших размеров. Такой репозиторий создается задав опцию `--depth` команде linkgit:git-clone[1] , его история может быть расширена командой linkgit:git-fetch[1]. _symref_ > Символическая ссылка: вместо идентификатора sha1-хэша, она имеет формат 'ref: refs/some/thing' и когда на неё ссылаются, она рекурсивно убирает с неё ссылку. 'HEAD' яркий пример symref. С символическими ссылками работает команда linkgit:git-symbolic-ref[1]. _tag_ Ссылка (ref), указывающая на тэг или коммит-объект. В отличие от головы, тег не изменяется фиксацией. Тэги (не тэговые объекты) хранятся в `$GIT_DIR/refs/tags/`. Git-тэг не имеет ничего общего с Lisp-тэгом (который бы назывался типом объекта в контексте git). Тэг чаще всего используется для обозначения определенной точки в родословной цепочке фиксации. _tag object_ > Объект, содержащий ссылку, указывающую на другой объект, который может содержать кроме сообщения так же как и коммит-объект. Объект также может содержать (PGP) подпись, в таком случае он называется подписанным теговым объектом ("signed tag object"). _topic branch_ > Обычная ветвь git, которая используется разработчиком для определения концептуального направления разработки. Т.к. создание ветвей является очень легким и недорогим, часто желательно иметь несколько мелких ветвей, где каждая содержит очень четко определенные понятия или небольшие последовательные связанные изменения. _tracking branch_ > Обычная ветка git, которая используется для отслеживания изменений другого репозитория. Такая ветка не должна иметь прямых модификаций или принимать локальные фиксации. Ветка отслеживания может быть идентифицирована как правая ссылка ("right-hand-side ref") в пуле: refspec. _tree_ > Либо рабочее дерево, либо объект типа дерево вместе с зависимыми объектами типа "blob" и дерево (т.е. хранимое представление рабочего дерева). _tree object_ > Объект, содержащий список имен файлов и состояний совместно со ссылками (refs) на ассоциированный объект типа "blob" и/или дерево. Дерево эквивалентно директории. _tree-ish_ > Ссылка указывающая либо на коммит-объект, объект типа дерево, либо тэговый объект, указывающий на объект типа тэг, коммит или дерево. _unmerged index_ Индекс, который содержит записи непрошедшие слияние. _unreachable object_ > Объект, который не доступен с ветки, тэга, или любой другой ссылки. _working tree_ > Дерево фактически проверенных файлов. Рабочее дерево обычно равно сумме HEAD и локальных изменений, которые возможно были совершены, но ещё не зафиксированы. ## Установка публичного репозитория ## Предположим, ваш личный репозиторий в папке ~/proj. Мы сначала создаём новую копию репозитория и говорим git-daemon, что она должна быть публичной: $ git clone --bare ~/proj proj.git $ touch proj.git/git-daemon-export-ok Итоговая папка proj.git сожержит "чистый" git репозиторий -- это просто содержание папки ".git" без каких-либо файлов, проверяемых вокруг неё. Далее, скопируйте proj.git на сервер, где вы планируете разместить публичный репозиторий. Вы можете использовать scp, rsync или что-то ещё более удобное. ### Экспорт git репозитория через протокол git ### Это предпочтительный метод. Если кто-либо другой администрирует сервер, он должен сказать вам, в какую папку сохранять репозиторий и по какой git:// URL он будет доступен. Иначе, вам нужно выполнить linkgit:git-daemon[1]; он будет ожидать соединения на 9418 порту. По-умолчанию, он позволяет получать доступ к любой папке, которая выглядит как папка git и содержит файл git-daemon-export-ok. Передавая какой-либо путь к папке в качестве git-daemon аргумента будет в будущем ограничивать экспорт этим путём. Вы также можете запустить git-daemon в качестве inetd сервиса; смотрите linkgit:git-daemon[1] справочную страницу для подробностей. (Особенно обратите внимание на раздел с примерами.) ###Экспорт репозиториев git через http### Протокол git обеспечивает лучшую производительность и надёжность, но на web-сервере http экспорт может быть легче для установки. Всё, что вам нужно сделать — разместить чистый репозиторий git в директории, которая экспортируется web-сервером, и произвести некоторые настройки, чтобы дать web-клиентам дополнительную информацию, которая им нужна: $ mv proj.git /home/you/public_html/proj.git $ cd proj.git $ git --bare update-server-info $ chmod a+x hooks/post-update (Для объяснения последних двух строчек, смотрите linkgit:git-update-server-info[1] и linkgit:githooks[5].) Распространите URL вашего proj.git. Теперь любой может клонировать или обновить с этого URL, например как в следующей комманде: $ git clone http://yourserver.com/~you/proj.git ------------------------------------------------------------------------------- http://translated.by/you/git-community-book/into-ru/trans/ Original (English): Git Community Book (http://book.git-scm.com/) Translation: © zeffyre, saturn721, ivan.borzenkov, Виктор, aiva, Валерий, Дмитрий Исайкин, shergin.com, lexich121, AgurcoV, blog.andrey-chernyshev.com, iBog, k_s, Kroxy, ciiccii, goriy, AmorphDev, aavezel, wolkodaw, intnzy, plishas, anatolik, Даниил, ostinru, Владимир Коршунов, mmv-ru, BlackLacost, Bubik, pyromaniac, chemtech, enchantner, and_rew, BrainDamaged, msilen, stogerc. License: GPL translated.by crowd