В первой части этой серии статей я познакомил вас с основами использования Git — созданием репозиториев, добавлением файлов, фиксацией файлов и использованием инструментов Git log и diff для просмотра временной шкалы ваших изменений. Эта часть будет продолжена, чтобы охватить некоторые более сложные темы: отмена изменений, создание веток и объединение изменений из одной ветви в другую.
Отмена изменений после того, как вы облажались
Давайте рассмотрим классический пример, включающий непреднамеренно удаленную работу. Вы отредактировали исходный файл, чтобы сделать простое изменение, и вы собираетесь нажать Ctrl
+ S
(при условии, что вы не используете vi), чтобы сохранить свою работу, когда ладонь руки касается сенсорной панели вашего ноутбука, что вызывает большой кусок файла, который будет выбран. Затем вы случайно нажали какую-то другую клавишу, которая заставляет выделение уничтожить вашу работу, и прежде чем вы узнаете, что произошло, Ctrl
+ S
сохраняет вашу работу. Это легко исправить, если вы используете Git.
Возврат файла к ранее зафиксированной версии
В этом сценарии предполагается, что вы еще не зафиксировали поврежденный файл. Используя файл config.php
из первой части, скажем, вы как-то уничтожили большую часть массива конфигурации базы данных, оставив его в таком виде:
<?php $database = array( "database" => "new_project");
Вы можете запустить git diff
чтобы увидеть, что изменилось; значения драйвера, хоста, имени пользователя и пароля исчезли.
- "водитель" => "mysql", - "host" => "locahost", - "username" => "Foo", - "пароль" => "пароль",
Как вы восстанавливаете файл? С помощью git checkout config.php
. Самый простой способ отменить некоторые изменения — восстановить файл в том состоянии, в котором он находился при последнем коммите.
Иногда вы захотите отменить некоторые изменения и восстановить файл так, как это было много коммитов назад. Если вы хотите восстановить только один файл, а не все файлы, которые находятся в коммите, вы можете использовать git checkout
за которым следует хеш коммита, который содержит версию файла, который вы хотите восстановить, и имя файла.
Допустим, вы запустили git log config.php
и оказалось, что commit c1d55debc7be8f50e363df462f84672ad029b703
содержал версию foo.php
вы хотите восстановить. Теперь вы можете запустить:
sean @ beerhaus: ~ / new_project $ git checkout c1d55debc7be8f50e363df462f84672ad029b703 config.php sean @ beerhaus: ~ / new_project $ git commit config.php -m "Возвращено к предыдущей версии." [master b125806] Возвращен к предыдущей версии. 1 изменение файлов, 1 вставка (+), 1 удаление (-)
Ваша рабочая копия config.php
будет перезаписана версией из предыдущего коммита. Будьте внимательны при запуске команды checkout, поскольку она перезаписывает файл и вы не можете восстановить рабочую копию … вы можете восстанавливать только те версии, которые были зафиксированы и отслеживаются Git.
Возврат всего к предыдущему коммиту
Другой способ отменить изменения в Git — это вернуть все к определенной фиксации. Когда вы возвращаетесь к коммиту, вы возвращаете каждый файл в нем, независимо от того, содержит ли коммит один или сто файлов.
Давайте создадим временную шкалу фальшивых изменений, чтобы проиллюстрировать возврат целых коммитов. Создайте файл с именем foo.php
в своем тестовом хранилище.
<?php // this is our original, pristine version
Добавьте новый файл и подтвердите:
sean @ beerhaus: ~ / new_project $ git add foo.php sean @ beerhaus: ~ / new_project $ git commit -am "Это оригинальная версия." [master 4d7add0] Это оригинальная версия. 1 файл изменен, 2 вставки (+), 0 удалений (-) режим создания 100644 foo.php
Сделайте второе изменение в foo.php
которое будет выглядеть так:
<?php // this is our original, pristine version // this is a version that might be useful
Подтвердите изменения снова.
sean @ beerhaus: ~ / new_project $ git commit -am "Добавлен комментарий". [master 9e87750] Добавлен комментарий. 1 изменение файлов, 1 вставка (+), 0 удалений (-)
Сделайте еще одно изменение, которое вы не хотите оставлять:
<?php while (true) { exec("yes"); }
sean @ beerhaus: ~ / new_project $ git commit -am "Добавлена действительно плохая версия файла." [master d4cb9dd] Добавлена действительно плохая версия файла. 1 файл изменен, 3 вставки (+), 2 удаления (-)
После того, как вы внесете эти три изменения, взгляните на журнал. Поскольку здесь вы в основном следите за хешами коммитов, вы можете передать опцию форматирования в git log
чтобы удалить некоторую дополнительную информацию.
sean @ beerhaus: ~ / new_project $ git log --pretty = oneline foo.php d4cb9ddbcb72cccf61e8682ecc2485a9a7a57a29 Добавлена действительно плохая версия файла. 9e877501c2ad74d177a77b88eb66bfa8be7c18bd Добавил комментарий. 4d7add037b4b38faba3392be988e0e9813daa8a7 Это оригинальная версия.
Чтобы отменить последний коммит, вы можете использовать git revert
. Команда ожидает аргумент, ссылающийся на неверный коммит, который необходимо отменить; поскольку вы хотите отменить только самый последний коммит, вы можете использовать HEAD
для аргумента. В этом контексте HEAD
— это просто ссылка на последний записанный коммит.
sean @ beerhaus: ~ / new_project $ git revert HEAD
Откроется редактор по умолчанию, позволяющий ввести сообщение. В моем случае предлагаемое сообщение уже предоставлено.
Вернуть «Добавлена действительно плохая версия файла». Это отменяет фиксацию d4cb9ddbcb72cccf61e8682ecc2485a9a7a57a29. # Пожалуйста, введите сообщение для ваших изменений. Линии запуска # с '#' будет игнорироваться, и пустое сообщение отменяет фиксацию. # На ветке мастер # Изменения должны быть совершены: # (используйте "git reset HEAD"... "в нестандарт" # # изменено: foo.php #
Когда git revert
завершится, взгляните на свой журнал еще раз.
sean @ beerhaus: ~ / new_project $ git log --pretty = oneline foo.php 264208850a690ba1dc6de13c701390d395706830 Отменить "Добавлена действительно плохая версия файла." d4cb9ddbcb72cccf61e8682ecc2485a9a7a57a29 Добавлена действительно плохая версия файла. 9e877501c2ad74d177a77b88eb66bfa8be7c18bd Добавил комментарий. 4d7add037b4b38faba3392be988e0e9813daa8a7 Это оригинальная версия.
Был сделан новый коммит, в котором содержимое foo.php
выглядит так, как оно было до плохого коммита.
Теперь давайте попробуем это снова, только на этот раз вы 9e877501c2ad74d177a77b88eb66bfa8be7c18bd
свою кодовую базу к 9e877501c2ad74d177a77b88eb66bfa8be7c18bd
sean @ beerhaus: ~ / new_project $ git revert 9e877501c2ad74d177a77b88eb66bfa8be7c18bd
Снова открывается редактор, в котором вы можете указать сообщение о коммите. После этого покажите журнал еще раз.
sean @ beerhaus: ~ / new_project $ git log --pretty = oneline foo.php 522ef8c7b70b1dcb9c21ecb51f1f025323b18da2 Отменить "Добавлен комментарий". 264208850a690ba1dc6de13c701390d395706830 Отменить "Добавлена действительно плохая версия файла." d4cb9ddbcb72cccf61e8682ecc2485a9a7a57a29 Добавлена действительно плохая версия файла. 9e877501c2ad74d177a77b88eb66bfa8be7c18bd Добавил комментарий. 4d7add037b4b38faba3392be988e0e9813daa8a7 Это оригинальная версия.
Ветвление и слияние
Ветвление и слияние — это понятия, общие для многих систем контроля версий. Идея заключается в том, что у вас есть основная линия разработки, которую иногда называют магистральной или стабильной . Именно здесь вы хотите сохранить весь исходный код в рабочем, высвобождаемом и стабильном состоянии. Вы не хотите вводить в эту основную строку всевозможный экспериментальный код не потому, что хотите препятствовать новым идеям или экспериментам, а потому, что хотите, чтобы эта строка была чистой и надежной. Там должна быть другая линия для экспериментов, и это то, что ветви.
Использование веток
Ветвь — это копия вашей основной стабильной линии разработки. Вы можете делать все, что захотите, в пределах вашей собственной ветви, и это не повлияет ни на что в основной строке, пока вы не объедините код своей ветви с основной веткой.
В Git вы уже используете ветку с момента первого создания хранилища. Эта ветка называется «мастер», и вы используете ее все время. Не веришь мне? Запустите git branch
и посмотрите его вывод.
sean @ beerhaus: ~ / new_project $ git branch * мастер
Звездочка указывает на ветку, над которой вы сейчас работаете.
В большинстве случаев master служит стабильной или основной линией разработки, о которой я упоминал ранее, но это не обязательно. Вы можете легко создать свою собственную ветку с именем «стабильная» и назначить ее основной веткой. Самый простой способ создать ветку — это с помощью git branch <name>
.
Давайте создадим ветку в хранилище под названием «экспериментальная».
sean @ beerhaus: ~ / new_project $ git ветка экспериментальная sean @ beerhaus: ~ / new_project $ git branch экспериментальный * мастер
Обратите внимание, что звездочка все еще показывает master как активную ветвь. Вы только создали новую ветку; Вы еще не переключились на это. Чтобы переключиться на новую ветку, используйте git checkout
.
sean @ beerhaus: ~ / new_project $ git checkout экспериментальная Перешел на ветку "Эксперимент" sean @ beerhaus: ~ / new_project $ git branch * экспериментальный мастер
В качестве альтернативы вы могли бы использовать git checkout -b experiment
чтобы создать новую ветку и переключить вас на все сразу. Я предпочитаю использовать этот ярлык вместо двух командных процессов сам.
Поскольку при создании экспериментальной ветви вы использовали ветку master, экспериментальная ветвь будет прямой копией master во время создания ветки.
Теперь откройте foo.php
в вашем редакторе и добавьте комментарий, чтобы он выглядел так:
<?php // this is our original, pristine version // comment from the experimental branch
Затем передайте изменения.
sean @ beerhaus: ~ / new_project $ git commit -am "Добавлен комментарий к ветке". [экспериментальный 8632135] Добавлен комментарий ветки. 1 изменение файлов, 1 вставка (+), 0 удалений (-)
Как только вы внесете это изменение, вернитесь в основную ветку.
sean @ beerhaus: ~ / new_project $ git master checkout Перешел на ветку "мастер"
Взгляните на foo.php
и вы заметите, что изменений, которые вы внесли в вашу экспериментальную ветку, нет. Это потому, что две ветви — это две разные рабочие среды. Изменения из одной ветви не появятся в другой ветви, пока они не будут объединены.
Слияние ветвей
Команда git merge
имеет вид git merge <source>
, где <source>
— ветвь, из которой вы объединяете изменения. Ветвь, в которой вы запускаете команду слияния, подразумевается как место назначения.
Итак, давайте перенесем ваши ветки из экспериментальной в основную ветку.
sean @ beerhaus: ~ / new_project $ git merge экспериментальный Обновление 522ef8c..8632135 Перемотка вперед foo.php | 1 + 1 изменение файлов, 1 вставка (+), 0 удалений (-)
Когда вы посмотрите на foo.php
вы увидите изменения, которые были внесены в экспериментальную ветку.
Теперь давайте сделаем это в другом направлении; пока вы все еще в master, внесите изменения в foo.php
который вы foo.php
в экспериментальную ветку.
sean @ beerhaus: ~ / new_project $ git commit -am "Добавлена функция для мастера." [master cbe5cba] Добавлена функция для мастера. 1 файл изменен, 4 вставки (+), 0 удалений (-)
Переключитесь на свою экспериментальную ветку и объедините изменения с мастером.
sean @ beerhaus: ~ / new_project $ git checkout экспериментальная Перешел на ветку "Эксперимент" sean @ beerhaus: ~ / new_project $ git мастер слияния Обновление 8632135..cbe5cba Перемотка вперед foo.php | 4 ++++ 1 файл изменен, 4 вставки (+), 0 удалений (-)
Взгляните на foo.php
и увидите, что функция, которую вы добавили в master, теперь находится в экспериментальной ветке.
Обработка конфликтов слияния
В конце концов вы столкнетесь с конфликтами при слиянии. Конфликт слияния возникает, когда вы объединяете изменения из одной ветви в другую, и Git не может завершить слияние без вмешательства человека. Это происходит, когда вы редактировали строку 80 файла в одной ветви, Фред редактирует строку 80 того же файла в другой ветви, а затем вы пытаетесь объединить изменения Фреда с вашими изменениями. Git знает, что тот же раздел кода был изменен, но мы, люди, должны выяснить, какие изменения следует сохранить.
Вы можете очень легко создать конфликтную ситуацию слияния, чтобы доказать это. Переключитесь на вашу основную ветку с помощью Git и откройте foo.php
в вашем редакторе.
sean @ beerhaus: ~ / new_project $ git master checkout Перешел на ветку "мастер"
Добавьте эту функцию в строке 9:
function master() { print "This is the master branch.n"; }
Сохраните файл и подтвердите изменения.
sean @ beerhaus: ~ / new_project $ git commit -am "Добавлена основная функция." [master 4f97280] Добавлена функция мастера. 1 файл изменен, 4 вставки (+), 0 удалений (-)
Вернитесь в экспериментальную ветку.
sean @ beerhaus: ~ / new_project $ git checkout экспериментальная Перешел на ветку "Эксперимент"
foo.php
откройте foo.php
в вашем редакторе и добавьте эту функцию также в строку 9:
function experimental() { print "This is the experimental branch.n"; }
Зафиксируйте изменения.
sean @ beerhaus: ~ / new_project $ git commit -am "Добавлена экспериментальная функция." [Экспериментальный c5358cc] Добавлена экспериментальная функция. 1 файл изменен, 4 вставки (+), 0 удалений (-)
Теперь, пока вы еще в экспериментальной ветке, объедините изменения с мастером. Вы отредактировали одни и те же строки одного и того же файла в обеих ветках, так что вы можете ожидать какой-то конфликт.
sean @ beerhaus: ~ / new_project $ git мастер слияния Авто-слияние foo.php CONFLICT (content): конфликт слияния в foo.php Автоматическое объединение не выполнено; исправить конфликты, а затем зафиксировать результат.
Теперь вам нужно открыть файл и вручную решить конфликт. В foo.php
вы увидите что-то вроде этого:
<<<<<<< ГОЛОВА функция экспериментальная () { print "Это экспериментальная ветка.n"; ======= function master () { выведите «Это главный филиал.n»; >>>>>>> Мастер }
Первый раздел, начинающийся с <<<<<<< HEAD
и заканчивающийся =======
— это ваш последний коммит ( HEAD
). Раздел от =======
до >>>>>>> master
— это входящие изменения из основной ветки.
Предположим, вы решили, что собираетесь сохранить изменения от мастера и выбросить экспериментальные изменения. Все, что вам нужно сделать, это удалить символы конфликта и строки, полученные из эксперимента. Этот раздел foo.php
теперь должен иметь только функцию master.
<?php // this is our original, pristine version // comment from the experimental branch function bar() { print "This is bar.n"; } function master() { print "This is the master branch.n"; }
Как только это будет сделано, сохраните файл и передайте разрешенный конфликт.
sean @ beerhaus: ~ / new_project $ git commit -am "Исправлен конфликт слияния между мастером и экспериментом." [экспериментальный 3871a1d] Исправлен конфликт слияния от мастера к экспериментальному
Это был простой пример. В реальном мире профессиональной разработки программного обеспечения у вас может быть 20 различных разработчиков, которые объединяют изменения из их отдельных ветвей в мастер. Каждый раз, когда это происходит, вы захотите объединить новейшие изменения от мастера обратно в вашу отдельную ветку. Конфликты будут происходить, и, хотя Git — отличный инструмент, он не может решить все ваши проблемы в такой ситуации. Иногда доброе старомодное человеческое общение — лучший инструмент, чтобы избежать конфликтов такого размера.
Резюме
И вот как это делается. Вы видели, как исправляться после любых ошибок, которые вы делаете на этом пути, как создавать ветки, чтобы вы могли работать параллельно с другими разработчиками в том же проекте или просто опробовать новые идеи, не ставя под угрозу стабильность вашей основной линии, и наконец, как объединить правки других людей в свою ветку. Три темы, затронутые в этой части — устранение ошибок, ветвление и слияние — вместе с первой частью этой серии предоставили вам ноу-хау для начала использования Git для управления вашими проектами. Если вы никогда не использовали программное обеспечение для контроля версий, надеюсь, я убедил вас начать, и если вы все еще используете CVS Subversion, возможно, вы сможете попробовать Git.