Изучаем Git по командам

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

Когда я учил Git мне говорили, что в работе мне понадобятся всего 3 команды:

  • add

  • commit

  • push

Правда, по сути мне немного слукавили, нужен был ещё как минимум:

  • pull

  • clone

  • status

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

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

Основная терминология

  • отслеживаемые файлы - когда файл добавляется в отслеживание Git'а создается специальный кэш, благодаря которому можно будет откатываться к предыдущим версиям файла

  • неотслеживаемые файлы - когда файл не добавляется в отслеживание Git'а его кэш не хранится в Git'е, а значит откат на предыдущие версии не работает для таких файлов

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

  • откат - переход на предыдущий коммит и отмена изменений с текущего коммита до того, на который мы хотим вернуться

  • пуш - отправка изменений на удаленный репозиторий (в Github, Gitlab, BitBucket)

  • фетч - скачивание изменений из репозитория

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

  • мердж - слитие веток в одну

Работа с Git

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

init

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

git init

clone

Данная команда нужна для того, чтобы клонировать репозиторий из облака. Можно клонировать репозитории из Github, Gitlab, BitBucket и других сервисов. Для того чтобы склонировать репозиторий нужно ввести:

git clone ссылка-на-репозиторий

add

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

  1. Вы изменяете файлы, можете изменять достаточно много файлов, задачи изменения которых вообще никак не связаны

  2. Вы решаете "закоммитить" ваши файлы (сделать сохранение версии, для того чтобы Git запомнил все ваши изменения в файлах и как-то назвал их, для этого есть отдельная команда git commit)

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

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

git add . # Занесение всех файлов для отслеживания
git commit -m "init" # Создание версии

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

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

git add ./file # Добавление файла для отслеживания
git add ./dir # Добавление всех файлов из директории и дочерних директорий для отслеживания

Так, хорошо, но что если нам нужно занести весь проект в систему отслеживания, однако есть пару директорий, которые мы не хотим заносить (например, node_modules или out)? Для этого мы можем создать файл .gitignore, в котором будут прописаны пути до файлов и директорий, которые Git должен игнорировать:

node_modules
out
one_file.tsx

В данном случае Git будет игнорировать директорию node_modules, out и файл one_file.tsx

Также мы можем сделать так, чтобы Git искал некоторые названия в дочерних директориях и игнорировал их:

./*/**/node_modules

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

Больше о .gitignore можно прочитать здесь

А что если я хочу обновить уже закэшированные файлы, мне что теперь их писать по одному? Конечно же нет. Для этого существует специальный флаг -u (сокращение от --update), который позволяет закэшировать только те файлы, которые уже отслеживаются:

git add -u

rm

Данная команда поможет, когда нам нужно избавиться от файла, она подобно команде rm удаляет файл из файловой системы и кэша Git, позволяя нам быстро избавиться от файла.

git rm file.txt

Данный пример удалит файл file.txt из кэша и файловой системы.

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

git rm --cached file.txt

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

Таким образом мы можем работать с данным файлом и Git не будет знать что именно в нём мы изменяем, а затем просто можем опять добавить его. Файл будет висеть в состоянии "untracked" (не отслеживается) до тех пор, покуда мы его опять не добавим.

commit

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

Обычно разработчики сохраняют версию программы с помощью данной команды:

git commit -am "Что было сделано"

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

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

git commit --amend -am "Название коммита"

show

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

git show

Выведутся изменения следующим образом:

commit 090b467c8b1d1b66c7bd3e732ee581d18eeab597
Author: Daniil Shilo 
Date:   Thu Oct 14 03:43:58 2021 +0300

    Lorem

diff --git a/1 b/1
new file mode 100644
index 0000000..cee7a92
--- /dev/null
+++ b/1
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
diff --git a/git b/git
new file mode 100644
index 0000000..e69de29

status

Если до этого момента вы не знали как проверить какой файл кэшируется, какой не отслеживается, а какие файлы и вовсе только что были удалены из кэша, то спешу вас обрадовать, у вас теперь есть команда git status, которая даёт возможность посмотреть что именно происходит с вашими файлами:

git status

Если вы хотите узнать более подробно о том, что же именно случилось с вашими файлами, то можете добавить ключ -v (сокращение от --verbose - внимательно):

git status -v

Если же вам наоборот хочется увидеть бриф по информации, то добавьте флаг -s (сокращение от --short):

git status -s

log

До этого момента мы могли только посмотреть что творилось в предыдущем коммите, однако с помощью git log мы можем посмотреть историю наших коммитов:

git log

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

git log --graph --pretty=oneline --abbrev-commit

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

diff

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

git diff ID-коммита-1 ID-коммита-2

Так называемые "ID-коммита" можно взять и вышеприведенной git log

Также, можно посмотреть что было изменено с момента последнего коммита (что изменено и попало в кэш) с помощью флага --cached:

git diff --cached

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

git diff ID-коммита

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

git diff --stat ID-коммита

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

git diff --staged

branch

В Git есть ветки для разделения версий. Если коммит нужен для того, чтобы сделать snapshot (слепок изменений) файлов, то ветки нужны для того, чтобы эти snapshot'ы разделять. У вас может быть ветка

  • master

  • build

  • beta

  • alpha

  • любое другое название

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

git branch

Для того чтобы создать новую ветку нужно ввести:

git branch имя-ветки

checkout

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

git checkout имя-ветки

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

git checkout -b имя-ветки

merge

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

git merge название-ветки-с-которой-будем-мерджить

push

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

В завершение

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

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

Надеюсь данная статья помогла вам чуть лучше узнать Git. Хорошего дня и продуктивной недели!

Комментарии (22)


  1. hottabxp
    14.10.2021 14:52
    +15

    1. bonta
      15.10.2021 08:27
      +1

      в этом весь Хабр. Рейтингов и кармы хотят не только промо от российских айти компаний, но и обычный пользователи хотят через это стать более полноправными пользователями Хабра.

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


  1. cr0nk
    14.10.2021 15:09

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


    1. skymal4ik
      14.10.2021 15:30
      +1

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

      Хотя по итогу в большинстве случаев все всё равно синхронизируются с мастер веткой на центральном сервере (GitHub, gitlab, etc)


    1. lxsmkv
      14.10.2021 17:51
      +3

      Я думаю самый важный пункт, это система веток в Git. Он помнит всю родословную проекта, в то время как в SVN ветки — практически копирование папок. Создание веток очень «дешевое» в Git, потому что записываются только изменения относительно родителя. Это имеет значение когда у тебя проект на четыре гига и интернет соединение на 8 Mbit/s


      1. ALexhha
        15.10.2021 11:06
        -1

        Это имеет значение когда у тебя проект на четыре гига

        Если у вас проект на 4 гига, то скорее всего вы что то делаете не так, имхо


        1. leonP4
          15.10.2021 14:01

          За МКАД'ом жизнь есть.

          Кроме веб-разработки есть и другие области. К примеру, геймдев давно за 4гб уплыл.

          П. С. Какой обьем сорцов буста на крестах или полного пакета qt?


          1. ALexhha
            15.10.2021 15:35

            Какой обьем сорцов буста на крестах или полного пакета qt?

            boost ~ 75 Мб, qt ~ 1.2 Гб

            $ du -h boost/ | tail -1
            76M	boost/
             
            $ git clone git://code.qt.io/qt/qt.git
            $ du -h qt/ | tail -1
            1.2G	qt/

            К примеру, геймдев давно за 4гб уплыл.

            вообще не показатель, во первых кто то и видео хранит в git и считает это абсолютно нормальным, во вторых - есть git lfs

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


    1. Lyonia
      14.10.2021 19:35

      надеюсь это не тролинг.

      очень много можно говорить но я приведу пару примеров.

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

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

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

      конечно есть и недостатки на больших проэктах с длинной историей


    1. netch80
      14.10.2021 20:40
      +3

      > Единственное удобное отличие в том что возможно коммитить изменения локально перед тем как вызвать push, но это ни ахти какая разница

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

      > чтобы была такая истерия вокруг git`а

      Хм, «истерия» была лет 10 назад. Сейчас я бы сказал, что это единственная массово используемая VCS (по крайней мере в Unix мире и около). Вопрос давно ушёл на другие уровни.


    1. DancingOnWater
      14.10.2021 22:23
      +3

      В svn объект коммита - файл, в git - состояние проекта. Разница всплывает при слияниях.

      1) Если у вас есть несколько команд, которая пилит в основном свой кусок, но немного и общий, то слияние превращается в очень трудоемкий процесс.

      2) Если в ходе мерджа кто-то на сервер выкатил новые изменения, то есть немалый шанс делать слияние по новой.

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


  1. IronHead
    14.10.2021 15:20

    Я тоже не понимаю, почему от git все так прутся.
    Мне лично более симпатичен hg.
    А в связке с tortoise — это вообще бомба


    1. unsignedchar
      14.10.2021 18:17
      +3

      hg это git на минималках ;)

      почему от git все так прутся

      Потому что GitHub, gitlab - фактический стандарт.


      1. IronHead
        15.10.2021 11:19

        А можно более подробно объяснить, в чем заключается "на минималках"?
        Я без сарказма, просто реально не вижу разницы и каких либо сильных сторон gita по сравнению с hg.


        1. unsignedchar
          18.10.2021 13:43

          В git не силён, так что ИМХО:
          Для использовании как в howto пишут (commit-push-pull-update-merge) — никакой разницы.
          В git у команд значительно больше ключей — это же неспроста? ;) За счёт этого mercurial проще. Сравните hg help commit и git help commit.
          В git лучше с бинарными файлами.
          На огромных репозиториях hg может быть хуже. Что-то с расходом памяти и python.


        1. netch80
          01.11.2021 11:28

          > Я без сарказма, просто реально не вижу разницы и каких либо сильных сторон gita по сравнению с hg.

          При некоторых задачах и стилях разработки она может не проявляться, конечно. Но вот пример: я практически только что закончил один злобный рефакторинг (если можно его так назвать).
          Есть некоторое состояние сетевого взаимодействия, которое из-за ошибки проектирования использует общие структуры для двух сторон (и иногда при этом соответственно состояния сторон путаются). Итак, начинаю — на чистой локальной копии делаю разделение (было moo, стало moo_in и moo_out). Падают тесты. Часть тестов юниты, часть более сложные (юнит-тестов не хватает на всё). Код начинает обрастать отладочной печатью (а проходить 100500 раз по одному месту даже отладчиком со скриптами — нереально). Эти печати надо коммитить (хотя бы локально), чтобы помнить историю.
          Идут попытки исправить код этого управляющего уровня (перестроить пути обработки). Несколько проб заканчиваются неудачно, но из них надо сохранить печать как полезную для общего дела.
          Идёт проба добавить просто реализацию слоя с нуля (а вдруг получится — но если нет, будет понятнее логика прошлого писателя). И снова, так как продирание идёт мелкими шагами на ощупь в темноте, каждая правка на десяток строк коммитится. В итоге я справился без этого кода (но не справился бы без попытки его написания).
          По дороге потребовалось ещё зарефакторить пару уровней пониже. Коммиты на этот рефакторинг «пододвигаются» под целевые изменения, делая их в цепочке после.
          Потом произвожу ещё один выворот логики, который надо вдвинуть до основного. Этот уже конфликтует из-за внесённых правок, теперь надо задумчиво сдвигать коммит в цепочке раньше на 1-2 шага каждый раз и фиксить конфликты.
          Это только где-то 1/3 от всей возни… устал описывать.
          Потом есть результат, ура! теперь его очищаем от всего привнесённого мусора (отладка, несработавший альтернативный подход, и прочее), собираем по коммиту на каждый предварительный шаг, основной выворот (увы, изменения просто не читаются, подтверждаются тестами), последующие (у пары функций половина кода стала ненужной и он выкидывается) — 5 коммитов по сути (и ещё ~5 на стиль, doxygenʼацию, и всё такое).
          Итого: результат есть. В процессе его создания рабочая копия больше времени находилась в interactive rebase (git rebase -i с остановками в конкретных точках), чем в своей верхушке. Общее количество входов в такой rebase, наверно, было штук 40-50. И ещё когда делишь коммит на то, что нужно отправлять, и то, что не нужно — git restore -s <id>.

          Даже с возможностями Git, в данном случае interactive rebase для перегруппировки/слияния и «индекса» для набора из 100500 изменений промежуточного коммита — только того, что нужно для конкретной отправки вверх — это было уже тяжело, прошла неделя как закончил, а я ещё морально «выдыхаю». С возможностями Hg это бы затянулось раза в 3, не меньше — мне от мысли, что надо было бы получать дифф и вручную нарезать, что куда идёт — уже страшно.

          (Да, я читал, что в Hg есть примерно похожее. Но именно что «примерно», в разы менее функционально и удобно. Разбить коммиты, переставить, слить — всё это настолько сложно и топорно, что лучше и не пробовать. Аналога git add -e нет в принципе.)


  1. NeoCode
    15.10.2021 08:32

    В оснвоном использую tortoiseGit на винде и git gui на линуксе. Удобно видеть, какие новые файлы добавились в процессе работы, и сразу решать - какие добавить в контроль версий, а какие в gitignore. К консоли как-то не привык, хотя простые команды типа clone/push/push использую.

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


    1. geher
      20.10.2021 07:05

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

      Не очень удтбно, но и особо страшного ничего нет.

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

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


      1. netch80
        01.11.2021 11:30

        Я бы только добавил, что без настройки «merge.conflictstyle = diff3» такое слияние утяжеляется в разы (и для человека, и для автосредств типа kdiff3, vimdiff), и её надо ставить изначально.


        1. ALexhha
          01.11.2021 11:53

          А на macos есть какие то достойные аналоги ?


          1. netch80
            01.11.2021 12:10

            Для чего именно? Настройка пригодна везде. Или речь про визуальные хелперы слияния? Тут я не в курсе, увы. Хотя vimdiff это просто режим vim и он будет доступен.


  1. akabrr
    15.10.2021 10:11

    По моему скромному мнению вот лучший туториал по гиту: https://girliemac.com/blog/2017/12/26/git-purr/