Работа с репозиторием Git
Настройка конфигурации
Существует три конфигурации git:
- system - общесистемная конфигурация, распространяющаяся на всех пользователей системы,
- global - конфигурация пользователя, действующая на все репозитории, с которыми работает этот пользователь,
- local - локальная конфигурация репозитория, распространяется только на репозиторий.
Для редактирования конфигурации можно воспользоваться командой с указанием соответствующей опции:
$ git config --global --edit
В глобальной конфигурации полезно указать имя пользователя и адрес его электронной почты. При внесении правок в какой-либо репозиторий эти данные будут добавляться к каждой сделанной фиксации. Прописываются они следующим образом:
[user]
user = Vladimir Stupin
email = vladimir@stupin.su
Доступ к репозиторию по протоколу HTTP (или HTTPS) должен осуществляться через веб-прокси, то его настройки можно прописать в файле конфигурации следующим образом:
[http]
proxy = http://user:password@proxy.domain.tld:port
Где:
- user - имя пользователя прокси-сервера. Если прокси-сервер не требует аутентификации, то имя пользователя и его пароль вместе со знаками двоеточия и собачки нужно опустить. Если пользователь и пароль не указаны, но всё-таки требуются, то они будут запрошены при обращении к веб-ресурсам.
- password - пароль этого пользователя. Если пароль нежелательно сохранять в файле конфигурации, то его можно опустить вместе с двоеточием.
- proxy.domain.tld - доменное имя (или IP-адрес) прокси-сервера.
- port - номер порта прокси-сервера. По умолчанию используется порт 80 (?), при необходимости его можно опустить вместе с двоеточием.
Если для доступа к удалённому репозиторию по протоколу HTTP (или HTTPS) требуется аутентификация, то при каждом обращении к этому репозиторию будет запрашиваться логин и пароль. Чтобы не вводить логин и пароль при каждом обращении к репозиторию, можно настроить кэш или постоянное хранение учётных данных. Сделать это можно при помощи конфигурации следующего вида:
[credential]
helper = cache --timeout 900
helper = store --file ~/.git-credentials
При использовании сохранения учётных данных в кэше можно указать время в секундах, по умоланию используется время 900 секунд (15 минут). При использовании сохранения учётных данных в постоянном файл необходимо указать имя этого файла. Можно использовать хранение учётных данных как в кэше, так и в файле. В таком случае все источники будут перебираться по порядку до тех пор, пока в одном из хранилищ не найдутся нужные учётные данные.
Для аутентификации на удалённом репозитории по протоколу SSH необходимо загрузить на него свой публичный ключ и указать SSH-клиенту использовать приватный ключ для подключения к этому узлу сети.
Сгенерируем пару ключей:
$ ssh-keygen
Файл ~/.ssh/id_rsa.pub
является публичным ключом, который необходимо установить на сервер. Файл ~/.ssh/id_rsa
является приватным ключом, при помощи которого будет происходить аутентификация на сервере.
Для того, чтобы приватный ключ автоматически использовался при попытке подключения к серверу, можно создать файл ~/.ssh/config
со следующим содержимым:
Host server.domain.tld
User user
AddKeysToAgent yes
IdentityFile ~/.ssh/id_rsa
Изменение автора
Иногда бывает так, что репозиторий уже создан или обновлён, а в конфигурации репозитория ещё не была прописана правильная информация об авторе. В таком случае можно обратиться к предыдущему разделу и поправить конфигурацию репозитория, после чего остаётся только исправить автора в недавно добавленных фиксациях. Воспользуемся для этого перебазированием. С помощью команды git log
находим идентификатор фиксации, после которой нужно внести исправления и запускаем команду:
$ git rebase -r 73431438c62c78831a1ad6ef368ad0431f6400ff \
--exec 'git commit --amend --no-edit --reset-author'
Если вы не хотите менять файл конфигурации, но вам нужно исправить автора фиксаций, то автора можно указать и явным образом:
$ git rebase -r 73431438c62c78831a1ad6ef368ad0431f6400ff \
--exec 'git commit --amend --no-edit --author="Vladimir Stupin <vladimir@stupin.su>"'
Если нужно изменить данные всех фиксаций, включая начальную, то вместо указания идентификатора фиксации нужно указать опцию --root
. В таком случае две приведённых выше команды примут следующий вид:
$ git rebase -r --root \
--exec 'git commit --amend --no-edit --reset-author'
$ git rebase -r --root \
--exec 'git commit --amend --no-edit --author="Vladimir Stupin <vladimir@stupin.su>"'
Стоит отметить, что в git имеется возможность указать разных автора изменений и автора фиксации. Автор изменений может не иметь прямого доступа к репозиторию и может, например, отправить свои доработки в виде заплатки по электронной почте. Автор фиксации в таком случае берёт заплатки и, не являясь их автором, помещает в репозиторий git. Обычно автор изменений и автор фиксации совпадают, но при их различии можно поменять обоих или только одного из них:
$ git filter-branch -f --env-filter '
GIT_AUTHOR_NAME="Vladimir Stupin"
GIT_AUTHOR_EMAIL="vladimir@stupin.su"
GIT_COMMITTER_NAME="Vladimir Stupin"
GIT_COMMITTER_EMAIL="vladimir@stupin.su"
'
В опции --env-filter
можно передавать произвольный сценарий оболочки, с помощью которого можно, например, поменять только почтовый ящик определённого автора, не трогая данные других авторов:
$ git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_NAME" = "Vladimir Stupin" ];
then
GIT_AUTHOR_EMAIL="vladimir@stupin.su";
git commit-tree "$@";
else
git commit-tree "$@";
fi'
Добавление новой начальной правки в репозиторий Git
Случилось так, что я решил поместить в репозиторий все свои программы, которые когда-то разрабатывал. С одной из программ приключилась такая история: я поместил в репозиторий найденную программу версии 1.1, внёс большое количество правок, выпустил версию 1.2, а потом нашёл на своём компьютере версию 1.0. Захотелось добавить эту версию программы перед начальной правкой в репоизтории. Мне удалось успешно провернуть этот трюк, после чего я нашёл ещё две предшествующие версии программы - 0.8 и 0.1. На сей раз я не просто провернул трюк, но и решил задокументировать его.
Создаём новый пустой репозиторий:
$ git init
Добавляем файлы в индекс:
$ git add *.cpp *.h
Добавляем комментарий к правке, заблаговременно выставив отметки времени правки:
$ env GIT_AUTHOR_DATE="Thu Mar 13 13:00:00 2003 +0500" GIT_COMMITTER_DATE="Thu Mar 13 13:00:00 2003 +0500" git commit -m "Версия 0.1. Тест OpenGL"
Заглядываем в журнал правок:
$ git log
И видим идентификатор правки, автора, отметку времени и комментарий к правке:
commit d2d5eb4017b88413ec9c1d5f47252d113bc07996
Author: Vladimir Stupin <vladimir@stupin.su>
Date: Fri Mar 21 13:00:00 2003 +0500
Версия 0.8
Заглядываем в каталог .git/objects и запоминаем имена всех каталогов, имя которых состоит из двух шестандцатеричных цифр.
Теперь переходим в каталог того проекта, в который хотим добавить правку перед самой первой правкой и копируем из только что созданного репозитория все объекты в новый:
$ cp -R new-root/.git/objects/{4a,84,95,a7,b9,c6,d0,d2} project/.git/objects/
И добавляем правку из предыдущего репозитория в качестве начальной правки в нужный нам репозиторий:
$ git rebase --onto d2d5eb4017b88413ec9c1d5f47252d113bc07996 --root master
В ходе перебазирования возникнет конфликт правок. Нужно привести конфликтующие файлы к тому виду, в каком они должны находится на момент правки, которая была раньше начальной. Для этого можно заранее скопировать репозиторий в отдельный каталог, перемотать версию файлов в текущем каталоге на начальную правку при помощи команды git checkout и скопировать файлы из прежней начальной правки в правку, перебазируемую в данный момент.
После этого добавляем в индекс исправленные конфликтующие файлы:
$ git add idpo.cpp main.cpp vmdl.cpp vmdl.h
И продолжаем перебазирование:
$ git rebase --continue
В ходе перебазирования все правки изменят свои идентификаторы, однако в начале журнала правок окажется добавленная нами начальная правка. К сожалению, если репозиторий уже был опубликован, то после перебазирования обновить файлы в опубликованном репозитории будет уже не так просто. Поскольку мой репозиторий был опубликован, но никто про него на тот момент не знал, я просто просто удалил опубликованный репозиторий и закачал на его место исправленный. Такое решение вряд ли подойдёт для опубликованного репозитория, с которым кто то уже ведёт работу и рассчитывает впоследствии объединить свои изменения с опубликованным репозиторием.
Использованные материалы:
- How can one change the timestamp of an old commit in Git?
- Insert a commit before the root commit in Git?
Создание и публикация новой ветки
Переходим к правке, от которой будем растить новую ветку:
$ git checkout 01f6ee2c99a4fb15fab3177decc69147b4288c5e
Создаём новую ветку с именем v1.0b:
$ git branch v1.0b
Изменяем файлы, добавляем их в индекс:
$ git add idpo.cpp
Подтверждаем изменения:
$ git commit -m "Версия 1.0b. Просмотр третьей версии IDPO-моделей Quake"
Заливаем ветку в вышестоящий репозиторий:
$ git push --set-upstream origin v1.0b
Возвращаемся к основной ветке:
$ git checkout master
Создание аннотированной метки
Добавление новой аннотированной метки:
$ git tag -m "Версия 1.0. Просмотр шестой версии IDPO-моделей Quake" v1.0 01f6ee2c99a4fb15fab3177decc69147b4288c5e
Просмотр всех меток в локальном репозитории:
$ git tag
Отправляем все метки в вышестоящий репозиторий:
$ git push --tags
Создание и слияние веток
Создаём новую ветку:
$ git branch idpo-v3
Смотрим на список имеющихся веток и видим, что сейчас мы по-прежнему находимся в ветке master:
$ git branch
idpo-v3
* master
Переключаемся на ветку idpo-v3:
$ git checkout idpo-v3
Смотрим на список веток снова и видим, что теперь мы находимся в ветке idpo-v3:
$ git branch
* idpo-v3
master
Меняем файлы, добавляем их в индекс:
$ git add idpo.cpp
Подтверждаем правку:
$ git commit -m "Версия 1.0b. Просмотр третьей версии IDPO-моделей Quake"
Возвращаемся обратно на главную ветку:
$ git checkout master
Просим выполнить слияние веток без внесения наращивания истории главной ветки правками из ветки idpo-v3 и без подтверждения слияния:
$ git merge --no-ff --no-commit idpo-v3
Теперь изменения из двух веток объединены в рабочем каталоге, но они не подтверждены. Можно вновь отредактировать файлы в текущем каталоге, внести изменения в индекс и только затем выполнить подтверждения правки, соответствующей слиянию веток.
Добавляем изменённые файлы в индекс:
$ git add idpo.cpp main.cpp
Подтверждаем правку, объединяющую ветку idpo-v3 с веткой master:
$ git commit -m "Версия 1.1. Добавлена поддержка просмотра третьей версии IDPO-моделей Quake"
Просмотр истории ветвлений и слияний
Посмотреть на историю взаимоотношений веток можно при помощи команды:
$ git log --all --decorate --oneline --graph
Опция --all
заставляет показывать правки во всех ветках, --oneline
- выводить по одной правки в одной строчке, --graph
- отображать взаимоотношения веток, --decorate
- показывать названия, источники и метки веток.
Программы для управления репозиториями
Опробованы:
- gitweb
- cgit
- gitblit
- gogs
- gitea
Не опробован:
- gitolite3
Стоит попробовать настроить gitblit для работы под управлением сервера приложений uwsgi-plugin-jvm-openjdk-8 или uwsgi-plugin-servlet-openjdk-8
Поместить программы в репозитории
Создание нового пустого репозитория с одним лишь файлом REAMDE.md:
$ vim README.md
$ git init
$ git add README.md
$ git commit -m "first commit"
$ git remote add origin https://stupin.su/git/stupin/pup.git
$ git push -u origin master
Добавить репозитории:
- [x] texture
- [x] pup
- [x] postadmin
- [x] postadmin2
- [x] flask-account
- [x] parled12
- [x] usr
- [ ] wadview - на Pascal
- [ ] vview - на Pascal
- [ ] vga - на C и Assembler
- [ ] quake/vmdl, оно же - view3d
- [ ] quake/bsp, оно же - quake/wad
- [ ] redfaction/vbm
- [ ] daikatana/wal
- [ ] calendar
- [ ] gta-vc - перекодирование из adf в mp3 и обратно
- [ ] riffscanner, bmscanner, musscanner, pwadscanner
- [ ] umxscanner
- [ ] scandir - рекурсивный список файлов
- [ ] stz - извлечение картинок из игры S-Tilez
- [ ] text/text-1 -
Варианты развития программ:
- texture - переписать на Си, реализовать систему плагинов, добавить поддержку redfaction/vbm и daikatana/wal
- view3d/v3dm - переписать на Си, реализовать систему плагинов, текстурирование моделей Quake 2, поиск и отделение независимых mesh'ей, их раскраску в разные цвета при просмотре
- pup - отделить плагины от основной программы более явно
- parled12 - переделать на libev, реализовать в виде модуля ядра Linux?
- vview - переписать на Си и SDL, реализовать систему плагинов
- wadview - переписать на Си и SDL
Правила работы с ветками gitflow
Как и в других системах контроля версий, в Git вы можете создавать и сливать ветки. Особенностью Git является возможность легкого выполнения ветвления и слияния. При использовании Git ветвление выполняется по отношению к локальным веткам. Локальные ветки могут быть либо опубликованы, либо не опубликованы на сервере:
- Опубликованные ветки связаны с ветками в удаленном репозитории. Вы можете сливать с опубликованной веткой изменения, полученные из удаленного репозитория (см. Получение изменений с сервера), и публиковать в удаленной ветке свои наборы изменений (см. Публикация изменений на сервере).
- Неопубликованные ветки не связаны с ветками в удаленном репозитории. Вы можете использовать такие ветки для временной работы, а затем удалить их. При необходимости можно опубликовать ветку в удаленном репозитории (см. Публикация изменений на сервере).
Для хранения кода рекомендуется использовать две основные ветки: master и develop. Основными считаются ветки, в которых располагается основная версия кода и которые сохраняются на протяжении всего периода жизни продукта. В ветке master хранится последняя стабильная версия кода на данный момент, в нее сливаются только протестированные изменения из веток релизов. В ветке develop ведется разработка следующих версий продукта. Также для удобства работы с исходным кодом вы можете создавать следующие типы вспомогательных веток:
- Ветки новых возможностей (feature branches). Ветки данного типа отщепляются от основной ветки develop, в них разрабатываются новые возможности (фичи) продукта. По завершении работы с новой возможностью ветка сливается в основную ветку develop. Рекомендуется именовать ветки единообразно. Например, использовать префикс (feature, bugfix, hotfix) в начале имени ветки и прямой слэш в качестве разделителя: feature/specific_name, feature/frontend/add_new_service и т. д.
- Ветки релизов (release branches). Ветки данного типа отщепляются от основной ветки develop и используются для подготовки к выпуску релиза после разработки всех новых возможностей, в них можно исправлять ошибки, найденные при релизном тестировании. По мере исправления ошибок изменения из ветки релизов можно сливать в основную ветку develop. При выпуске релиза изменения из данной ветки сливаются в основную ветку master, а затем изменения из ветки master сливаются в ветку develop.
- Ветки исправлений (hotfix branches). Ветки данного типа отщепляются от основной ветки master и позволяют внести быстрые исправления в релизную версию продукта, не затрагивая разработку следующих версий в основной ветке develop. По завершении работы ветка сливается в обе основные ветки (master и develop).
- Ветки сопровождения проекта (support branches). Ветки данного типа отщепляются от основной ветки master и предназначены для одновременной поддержки нескольких предыдущих версий продукта. При этом в ветке master находится актуальная версия кода. Изменения из веток сопровождения проекта не сливаются в основные ветки.
Или более полное описание.
- Разработка очередной версии компонентов:
- ведется в ветке develop, в которую сливаются результаты работы feature/<ветка> или bugfix/<ветка>;
- ветки bugfix/<ветка> или feature/<ветка> используются в качестве рабочих веток разработчиками;
- результат доработки сливается в develop через pull-request;
- при готовности релиз-кандидата выполняется слияние develop в ветку master через pull-request;
- при выпуске релиза проставляется тег на commit, из которого выпускался релиз.
- При необходимости внесения изменений в последний выпущенный релиз:
- от тега релиза отщепляется ветка hotfix/<ветка>;
- изменения выполняются непосредственно в этой ветке;
- по готовности выполняется слияние hotfix/<ветка> в ветку master через pull-request и проставляется тег на commit, из которого выпускается релиз.
- При необходимости внесения изменений в более старые релизы:
- от тега соответствующего релиза отщепляется ветка support/<версия>;
- для каждой доработки от support/<версия> отщепляются ветки feature/<ветка> или bugfix/<ветка>;
- результат доработки сливается в support/<версия> через pull-request;
- по готовности проставляется тег на commit в support/<версия>, из которого выпускается релиз.
- Для заимствованных компонентов структура веток должна соответствовать Получение исходного кода заимствованного компонента из внешнего репозитория git
- ветка vendor/master используется для синхронизации изменений с репозиторием разработчиков заимствованного компонента;
- ветка vendor/master является родительской для остальных веток, которые содержат изменения, внесённые внутри компании(master, develop, support и всех остальных).
Удаление локальных и удалённых веток
Для удаления локальной ветки используется простая и знакомая многим команда:
$ git branch -d удаляемая-ветка
Для удаления ветки на сервере можно воспользоваться командой следующего вида:
$ git push origin --delete удаляемая-ветка
Просмотр авторов изменений строк файла
Для просмотра построчного авторства файла исходного кода можно переключиться на интересующую ветку/фиксацию и ввести команду следующего вида:
$ git blame файл
Настройка стратегий слияния для отдельных файлов
Создаём в корне проекта файл .gitattributes, добавляем в него строки с названиями файлов (каталогов) и стратегиями, которые будут применяться к ним при слиянии, в следующем виде:
# при слиянии использовать только собственную версию файла
database.xml merge=ours
Клонирование удалённого репозитория
Есть общеупотребимый способ клонирования, когда клонируется ветка master в удалённом репозитории:
$ git clone <внешний репозиторий> <локальный репозиторий>
Другой способ предусматривает клонирование только одной интересующей ветки, отличной от master:
$ git clone --single-branch --branch <релизная ветка> <внешний репозиторий> <локальный репозиторий>
Загрузка ветки в удалённый репозиторий
Для заливки ветки в удалённый репозиторий origin под другим именем можно воспользоваться командой следующего вида:
$ git push origin <ветка>:<удалённая ветка>
Загрзука тега в удалённый репозиторий
Для загрузки тега в удалённый репозиторий origin под другим именем можно воспользоваться такой командой:
$ git push origin <тег>:<удалённый тег>
Посмотреть список тегов определённой ветки
Для просмотра списка тегов из определённой ветки, заданной по имени, тегу или идентификатору фиксации, вплоть до неё, можно воспользоваться такой командой:
$ git tag --merged <ветка>