2.2 Основы Git — Запись изменений в репозиторий
Итак, у вас имеется настоящий Git-репозиторий и рабочая копия файлов для некоторого проекта. Вам нужно делать некоторые изменения и фиксировать «снимки» состояния (snapshots) этих изменений в вашем репозитории каждый раз, когда проект достигает состояния, которое вам хотелось бы сохранить.
Запомните, каждый файл в вашем рабочем каталоге может находиться в одном из двух состояний: под версионным контролем (отслеживаемые) и нет (неотслеживаемые). Отслеживаемые файлы — это те файлы, которые были в последнем снимке состояния проекта; они могут быть неизменёнными, изменёнными или подготовленными к коммиту. Если кратко, то отслеживаемые файлы — это те файлы, о которых знает Git.
Неотслеживаемые файлы — это всё остальное, любые файлы в вашем рабочем каталоге, которые не входили в ваш последний снимок состояния и не подготовлены к коммиту. Когда вы впервые клонируете репозиторий, все файлы будут отслеживаемыми и неизменёнными, потому что Git только что их извлек и вы ничего пока не редактировали.
Как только вы отредактируете файлы, Git будет рассматривать их как изменённые, так как вы изменили их с момента последнего коммита. Вы индексируете эти изменения, затем фиксируете все проиндексированные изменения, а затем цикл повторяется.
Рисунок 8. Жизненный цикл состояний файлов
Определение состояния файлов
Основной инструмент, используемый для определения, какие файлы в каком состоянии находятся — это команда git status . Если вы выполните эту команду сразу после клонирования, вы увидите что-то вроде этого:
$ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working tree clean
Это означает, что у вас чистый рабочий каталог, другими словами — в нём нет отслеживаемых изменённых файлов. Git также не обнаружил неотслеживаемых файлов, в противном случае они бы были перечислены здесь. Наконец, команда сообщает вам на какой ветке вы находитесь и сообщает вам, что она не расходится с веткой на сервере. Пока что это всегда ветка master , ветка по умолчанию; в этой главе это не важно. В главе Ветвление в Git будут рассмотрены ветки и ссылки более детально.
Примечание
В 2020 году GitHub изменил имя ветки по умолчанию с master на main , другие же git-хостинг платформы последовали этому примеру. Поэтому, вы можете обнаружить, что ветка по умолчанию для новых репозиториев — main , а не master . Более того, имя ветки по умолчанию можно изменить (как вы видели в Настройка ветки по умолчанию), поэтому вам может встретиться и другое имя. При этом Git продолжает использовать имя master , поэтому далее в книге мы используем именно его.
Предположим, вы добавили в свой проект новый файл, простой файл README . Если этого файла раньше не было, и вы выполните git status , вы увидите свой неотслеживаемый файл вот так:
$ echo 'My Project' > README $ git status On branch master Your branch is up-to-date with 'origin/master'. Untracked files: (use "git add . " to include in what will be committed) README nothing added to commit but untracked files present (use "git add" to track)
Понять, что новый файл README неотслеживаемый можно по тому, что он находится в секции «Untracked files» в выводе команды status . Статус Untracked означает, что Git видит файл, которого не было в предыдущем снимке состояния (коммите); Git не станет добавлять его в ваши коммиты, пока вы его явно об этом не попросите. Это предохранит вас от случайного добавления в репозиторий сгенерированных бинарных файлов или каких-либо других, которые вы и не думали добавлять. Мы хотели добавить README, так давайте сделаем это.
Отслеживание новых файлов
Для того чтобы начать отслеживать (добавить под версионный контроль) новый файл, используется команда git add . Чтобы начать отслеживание файла README , вы можете выполнить следующее:
$ git add README
Если вы снова выполните команду status , то увидите, что файл README теперь отслеживаемый и добавлен в индекс:
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git restore --staged . " to unstage) new file: README
Вы можете видеть, что файл проиндексирован, так как он находится в секции «Changes to be committed». Если вы выполните коммит в этот момент, то версия файла, существовавшая на момент выполнения вами команды git add , будет добавлена в историю снимков состояния. Как вы помните, когда вы ранее выполнили git init , затем вы выполнили git add (файлы) — это было сделано для того, чтобы добавить файлы в вашем каталоге под версионный контроль. Команда git add принимает параметром путь к файлу или каталогу, если это каталог, команда рекурсивно добавляет все файлы из указанного каталога в индекс.
Индексация изменённых файлов
Давайте модифицируем файл, уже находящийся под версионным контролем. Если вы измените отслеживаемый файл CONTRIBUTING.md и после этого снова выполните команду git status , то результат будет примерно следующим:
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD . " to unstage) new file: README Changes not staged for commit: (use "git add . " to update what will be committed) (use "git checkout -- . " to discard changes in working directory) modified: CONTRIBUTING.md
Файл CONTRIBUTING.md находится в секции «Changes not staged for commit» — это означает, что отслеживаемый файл был изменён в рабочем каталоге, но пока не проиндексирован. Чтобы проиндексировать его, необходимо выполнить команду git add . Это многофункциональная команда, она используется для добавления под версионный контроль новых файлов, для индексации изменений, а также для других целей, например для указания файлов с исправленным конфликтом слияния. Вам может быть понятнее, если вы будете думать об этом как «добавить этот контент в следующий коммит», а не как «добавить этот файл в проект». Выполним git add , чтобы проиндексировать CONTRIBUTING.md , а затем снова выполним git status :
$ git add CONTRIBUTING.md $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD . " to unstage) new file: README modified: CONTRIBUTING.md
Теперь оба файла проиндексированы и войдут в следующий коммит. В этот момент вы, предположим, вспомнили одно небольшое изменение, которое вы хотите сделать в CONTRIBUTING.md до коммита. Вы открываете файл, вносите и сохраняете необходимые изменения и вроде бы готовы к коммиту. Но давайте-ка ещё раз выполним git status :
$ vim CONTRIBUTING.md $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD . " to unstage) new file: README modified: CONTRIBUTING.md Changes not staged for commit: (use "git add . " to update what will be committed) (use "git checkout -- . " to discard changes in working directory) modified: CONTRIBUTING.md
Что за чёрт? Теперь CONTRIBUTING.md отображается как проиндексированный и непроиндексированный одновременно. Как такое возможно? Такая ситуация наглядно демонстрирует, что Git индексирует файл в точности в том состоянии, в котором он находился, когда вы выполнили команду git add . Если вы выполните коммит сейчас, то файл CONTRIBUTING.md попадёт в коммит в том состоянии, в котором он находился, когда вы последний раз выполняли команду git add , а не в том, в котором он находится в вашем рабочем каталоге в момент выполнения git commit . Если вы изменили файл после выполнения git add , вам придётся снова выполнить git add , чтобы проиндексировать последнюю версию файла:
$ git add CONTRIBUTING.md $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD . " to unstage) new file: README modified: CONTRIBUTING.md
Сокращённый вывод статуса
Вывод команды git status довольно всеобъемлющий и многословный. Git также имеет флаг вывода сокращённого статуса, так что вы можете увидеть изменения в более компактном виде. Если вы выполните git status -s или git status —short вы получите гораздо более упрощённый вывод:
$ git status -s M README MM Rakefile A lib/git.rb M lib/simplegit.rb ?? LICENSE.txt
Новые неотслеживаемые файлы помечены ?? слева от них, файлы добавленные в отслеживаемые помечены A , отредактированные файлы помечены M и так далее. В выводе содержится два столбца — в левом указывается статус файла, а в правом модифицирован ли он после этого. К примеру в нашем выводе, файл README модифицирован в рабочем каталоге, но не проиндексирован, а файл lib/simplegit.rb модифицирован и проиндексирован. Файл Rakefile модифицирован, проиндексирован и ещё раз модифицирован, таким образом на данный момент у него есть те изменения, которые попадут в коммит, и те, которые не попадут.
Игнорирование файлов
Зачастую, у вас имеется группа файлов, которые вы не только не хотите автоматически добавлять в репозиторий, но и видеть в списках неотслеживаемых. К таким файлам обычно относятся автоматически генерируемые файлы (различные логи, результаты сборки программ и т. п.). В таком случае, вы можете создать файл .gitignore . с перечислением шаблонов соответствующих таким файлам. Вот пример файла .gitignore :
$ cat .gitignore *.[oa] *~
Первая строка предписывает Git игнорировать любые файлы заканчивающиеся на «.o» или «.a» — объектные и архивные файлы, которые могут появиться во время сборки кода. Вторая строка предписывает игнорировать все файлы заканчивающиеся на тильду ( ~ ), которая используется во многих текстовых редакторах, например Emacs, для обозначения временных файлов. Вы можете также включить каталоги log, tmp или pid; автоматически создаваемую документацию; и т. д. и т. п. Хорошая практика заключается в настройке файла .gitignore до того, как начать серьёзно работать, это защитит вас от случайного добавления в репозиторий файлов, которых вы там видеть не хотите.
К шаблонам в файле .gitignore применяются следующие правила:
- Пустые строки, а также строки, начинающиеся с # , игнорируются.
- Стандартные шаблоны являются глобальными и применяются рекурсивно для всего дерева каталогов.
- Чтобы избежать рекурсии используйте символ слеш (/) в начале шаблона.
- Чтобы исключить каталог добавьте слеш (/) в конец шаблона.
- Можно инвертировать шаблон, использовав восклицательный знак (!) в качестве первого символа.
Glob-шаблоны представляют собой упрощённые регулярные выражения, используемые командными интерпретаторами. Символ ( * ) соответствует 0 или более символам; последовательность [abc] — любому символу из указанных в скобках (в данном примере a, b или c); знак вопроса ( ? ) соответствует одному символу; и квадратные скобки, в которые заключены символы, разделённые дефисом ( [0-9] ), соответствуют любому символу из интервала (в данном случае от 0 до 9). Вы также можете использовать две звёздочки, чтобы указать на вложенные каталоги: a/**/z соответствует a/z , a/b/z , a/b/c/z , и так далее.
Вот ещё один пример файла .gitignore :
# Исключить все файлы с расширением .a *.a # Но отслеживать файл lib.a даже если он подпадает под исключение выше !lib.a # Исключить файл TODO в корневом каталоге, но не файл в subdir/TODO /TODO # Игнорировать все файлы в каталоге build/ build/ # Игнорировать файл doc/notes.txt, но не файл doc/server/arch.txt doc/*.txt # Игнорировать все .txt файлы в каталоге doc/ doc/**/*.txt
GitHub поддерживает довольно полный список примеров .gitignore файлов для множества проектов и языков https://github.com/github/gitignore это может стать отправной точкой для .gitignore в вашем проекте.
Примечание
В простейшем случае репозиторий будет иметь один файл .gitignore в корневом каталоге, правила из которого будут рекурсивно применяться ко всем подкаталогам. Так же возможно использовать .gitignore файлы в подкаталогах. Правила из этих файлов будут применяться только к каталогам, в которых они находятся. Например, репозиторий исходного кода ядра Linux содержит 206 файлов .gitignore .
Детальное рассмотрение использования нескольких .gitignore файлов выходит за пределы этой книги; детали доступны в справке man gitignore .
Просмотр индексированных и неиндексированных изменений
Если результат работы команды git status недостаточно информативен для вас — вам хочется знать, что конкретно поменялось, а не только какие файлы были изменены — вы можете использовать команду git diff . Позже мы рассмотрим команду git diff подробнее; вы, скорее всего, будете использовать эту команду для получения ответов на два вопроса: что вы изменили, но ещё не проиндексировали, и что вы проиндексировали и собираетесь включить в коммит. Если git status отвечает на эти вопросы в самом общем виде, перечисляя имена файлов, git diff показывает вам непосредственно добавленные и удалённые строки — патч как он есть.
Допустим, вы снова изменили и проиндексировали файл README , а затем изменили файл CONTRIBUTING.md без индексирования. Если вы выполните команду git status , вы опять увидите что-то вроде:
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD . " to unstage) modified: README Changes not staged for commit: (use "git add . " to update what will be committed) (use "git checkout -- . " to discard changes in working directory) modified: CONTRIBUTING.md
Чтобы увидеть, что же вы изменили, но пока не проиндексировали, наберите git diff без аргументов:
$ git diff diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ebb991..643e24f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,8 @@ branch directly, things can get messy. Please include a nice description of your changes when you submit your PR; if we have to read the whole diff to figure out why you're contributing in the first place, you're less likely to get feedback and have your change -merged in. +merged in. Also, split your changes into comprehensive chunks if you patch is +longer than a dozen lines. If you are starting to work on a particular area, feel free to submit a PR that highlights your work in progress (and note in the PR title that it's
Эта команда сравнивает содержимое вашего рабочего каталога с содержимым индекса. Результат показывает ещё не проиндексированные изменения.
Если вы хотите посмотреть, что вы проиндексировали и что войдёт в следующий коммит, вы можете выполнить git diff —staged . Эта команда сравнивает ваши проиндексированные изменения с последним коммитом:
$ git diff --staged diff --git a/README b/README new file mode 100644 index 0000000..03902a1 --- /dev/null +++ b/README @@ -0,0 +1 @@ +My Project
Важно отметить, что git diff сама по себе не показывает все изменения сделанные с последнего коммита — только те, что ещё не проиндексированы. Такое поведение может сбивать с толку, так как если вы проиндексируете все свои изменения, то git diff ничего не вернёт.
Другой пример: вы проиндексировали файл CONTRIBUTING.md и затем изменили его, вы можете использовать git diff для просмотра как проиндексированных изменений в этом файле, так и тех, что пока не проиндексированы. Если наше окружение выглядит вот так:
$ git add CONTRIBUTING.md $ echo '# test line' >> CONTRIBUTING.md $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD . " to unstage) modified: CONTRIBUTING.md Changes not staged for commit: (use "git add . " to update what will be committed) (use "git checkout -- . " to discard changes in working directory) modified: CONTRIBUTING.md
Используйте git diff для просмотра непроиндексированных изменений
$ git diff diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 643e24f..87f08c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,3 +119,4 @@ at the ## Starter Projects See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md). +# test line
а так же git diff —cached для просмотра проиндексированных изменений ( —staged и —cached синонимы):
$ git diff --cached diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ebb991..643e24f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,8 @@ branch directly, things can get messy. Please include a nice description of your changes when you submit your PR; if we have to read the whole diff to figure out why you're contributing in the first place, you're less likely to get feedback and have your change -merged in. +merged in. Also, split your changes into comprehensive chunks if you patch is +longer than a dozen lines. If you are starting to work on a particular area, feel free to submit a PR that highlights your work in progress (and note in the PR title that it's
Примечание
Git Diff во внешних инструментах
Мы будем продолжать использовать команду git diff различными способами на протяжении всей книги. Существует ещё один способ просматривать эти изменения, если вы предпочитаете графический просмотр или внешнюю программу просмотра различий, вместо консоли. Выполнив команду git difftool вместо git diff , вы сможете просмотреть изменения в файле с помощью таких программ как emerge, vimdiff и других (включая коммерческие продукты). Выполните git difftool —tool-help чтобы увидеть какие из них уже установлены в вашей системе.
Коммит изменений
Теперь, когда ваш индекс находится в таком состоянии, как вам и хотелось, вы можете зафиксировать свои изменения. Запомните, всё, что до сих пор не проиндексировано — любые файлы, созданные или изменённые вами, и для которых вы не выполнили git add после редактирования — не войдут в этот коммит. Они останутся изменёнными файлами на вашем диске. В нашем случае, когда вы в последний раз выполняли git status , вы видели что всё проиндексировано, и вот, вы готовы к коммиту. Простейший способ зафиксировать изменения — это набрать git commit :
$ git commit
Эта команда откроет выбранный вами текстовый редактор.
Примечание
Редактор устанавливается переменной окружения EDITOR — обычно это vim или emacs, хотя вы можете установить любой другой с помощью команды git config —global core.editor , как было показано в главе Введение).
В редакторе будет отображён следующий текст (это пример окна Vim):
# Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Your branch is up-to-date with 'origin/master'. # # Changes to be committed: # new file: README # modified: CONTRIBUTING.md # ~ ~ ~ ".git/COMMIT_EDITMSG" 9L, 283C
Вы можете видеть, что комментарий по умолчанию для коммита содержит закомментированный результат работы команды git status и ещё одну пустую строку сверху. Вы можете удалить эти комментарии и набрать своё сообщение или же оставить их для напоминания о том, что вы фиксируете.
Примечание
Для ещё более подробного напоминания, что же именно вы поменяли, можете передать аргумент -v в команду git commit . Это приведёт к тому, что в комментарий будет также помещена дельта/diff изменений, таким образом вы сможете точно увидеть все изменения которые вы совершили.
Когда вы выходите из редактора, Git создаёт для вас коммит с этим сообщением, удаляя комментарии и вывод команды diff .
Есть и другой способ — вы можете набрать свой комментарий к коммиту в командной строке вместе с командой commit указав его после параметра -m , как в следующем примере:
$ git commit -m "Story 182: fix benchmarks for speed" [master 463dc4f] Story 182: fix benchmarks for speed 2 files changed, 2 insertions(+) create mode 100644 README
Итак, вы создали свой первый коммит! Вы можете видеть, что коммит вывел вам немного информации о себе: на какую ветку вы выполнили коммит ( master ), какая контрольная сумма SHA-1 у этого коммита ( 463dc4f ), сколько файлов было изменено, а также статистику по добавленным/удалённым строкам в этом коммите.
Запомните, что коммит сохраняет снимок состояния вашего индекса. Всё, что вы не проиндексировали, так и висит в рабочем каталоге как изменённое; вы можете сделать ещё один коммит, чтобы добавить эти изменения в репозиторий. Каждый раз, когда вы делаете коммит, вы сохраняете снимок состояния вашего проекта, который позже вы можете восстановить или с которым можно сравнить текущее состояние.
Игнорирование индексации
Несмотря на то, что индекс может быть удивительно полезным для создания коммитов именно такими, как вам и хотелось, он временами несколько сложнее, чем вам нужно в процессе работы. Если у вас есть желание пропустить этап индексирования, Git предоставляет простой способ. Добавление параметра -a в команду git commit заставляет Git автоматически индексировать каждый уже отслеживаемый на момент коммита файл, позволяя вам обойтись без git add :
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add . " to update what will be committed) (use "git checkout -- . " to discard changes in working directory) modified: CONTRIBUTING.md no changes added to commit (use "git add" and/or "git commit -a") $ git commit -a -m 'Add new benchmarks' [master 83e38c7] Add new benchmarks 1 file changed, 5 insertions(+), 0 deletions(-)
Обратите внимание, что в данном случае перед коммитом вам не нужно выполнять git add для файла CONTRIBUTING.md , потому что флаг -a включает все файлы. Это удобно, но будьте осторожны: флаг -a может включить в коммит нежелательные изменения.
Удаление файлов
Для того чтобы удалить файл из Git, вам необходимо удалить его из отслеживаемых файлов (точнее, удалить его из вашего индекса) а затем выполнить коммит. Это позволяет сделать команда git rm , которая также удаляет файл из вашего рабочего каталога, так что в следующий раз вы не увидите его как «неотслеживаемый».
Если вы просто удалите файл из своего рабочего каталога, он будет показан в секции «Changes not staged for commit» (изменённые, но не проиндексированные) вывода команды git status :
$ rm PROJECTS.md $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add/rm . " to update what will be committed) (use "git checkout -- . " to discard changes in working directory) deleted: PROJECTS.md no changes added to commit (use "git add" and/or "git commit -a")
Затем, если вы выполните команду git rm , удаление файла попадёт в индекс:
$ git rm PROJECTS.md rm 'PROJECTS.md' $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD . " to unstage) deleted: PROJECTS.md
После следующего коммита файл исчезнет и больше не будет отслеживаться. Если вы изменили файл и уже проиндексировали его, вы должны использовать принудительное удаление с помощью параметра -f . Это сделано для повышения безопасности, чтобы предотвратить ошибочное удаление данных, которые ещё не были записаны в снимок состояния и которые нельзя восстановить из Git.
Другая полезная штука, которую вы можете захотеть сделать — это удалить файл из индекса, оставив его при этом в рабочем каталоге. Другими словами, вы можете захотеть оставить файл на жёстком диске, но перестать отслеживать изменения в нём. Это особенно полезно, если вы забыли добавить что-то в файл .gitignore и по ошибке проиндексировали, например, большой файл с логами, или кучу промежуточных файлов компиляции. Чтобы сделать это, используйте опцию —cached :
$ git rm --cached README
В команду git rm можно передавать файлы, каталоги или шаблоны. Это означает, что вы можете сделать что-то вроде:
$ git rm log/\*.log
Обратите внимание на обратный слеш ( \ ) перед * . Он необходим из-за того, что Git использует свой собственный обработчик имён файлов вдобавок к обработчику вашего командного интерпретатора. Эта команда удаляет все файлы, имеющие расширение .log и находящиеся в каталоге log/ . Или же вы можете сделать вот так:
$ git rm \*~
Эта команда удаляет все файлы, имена которых заканчиваются на ~ .
Перемещение файлов
В отличие от многих других систем контроля версий, Git не отслеживает перемещение файлов явно. Когда вы переименовываете файл в Git, в нём не сохраняется никаких метаданных, говорящих о том, что файл был переименован. Однако, Git довольно умён в плане обнаружения перемещений постфактум — мы рассмотрим обнаружение перемещения файлов чуть позже.
Таким образом, наличие в Git команды mv выглядит несколько странным. Если вам хочется переименовать файл в Git, вы можете сделать что-то вроде:
$ git mv file_from file_to
и это отлично сработает. На самом деле, если вы выполните что-то вроде этого и посмотрите на статус, вы увидите, что Git считает, что произошло переименование файла:
$ git mv README.md README $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD . " to unstage) renamed: README.md -> README
Однако, это эквивалентно выполнению следующих команд:
$ mv README.md README $ git rm README.md $ git add README
Git неявно определяет, что произошло переименование, поэтому неважно, переименуете вы файл так или используя команду mv . Единственное отличие состоит лишь в том, что mv — одна команда вместо трёх — это функция для удобства. Важнее другое — вы можете использовать любой удобный способ для переименования файла, а затем воспользоваться командами add или rm перед коммитом.
Анализ сделанных изменений — Введение в Git
Во время разработки программистам часто приходится останавливаться и анализировать изменения, которые они сделали с последнего коммита.
Потребность смотреть изменения становится очевидной, если представить себе, что такое работа над реальным проектом. Как правило, это тысячи строк кода, сотни файлов, десятки часов работы над одной задачей.
Потратив даже несколько часов в таком проекте, очень сложно вспомнить, что и где менялось, а что еще осталось поменять. Чтобы было проще, нужно анализировать изменения, о чем мы поговорим в этом уроке.
Как анализировать изменения
Анализировать изменения важно даже в небольших проектах. Прямо сейчас во время разработки этого курса изменилось несколько файлов и git status выглядит так:
(use "git restore --staged . " to unstage) modified: 300-working-directory/README.md Changes not staged for commit: (use "git add . " to update what will be committed) (use "git restore . " to discard changes in working directory) modified: 100-intro/README.md modified: 250-github/README.md modified: 300-working-directory/README.md modified: 300-working-directory/spec.yml modified: 350-changes/README.md
Попробуем воспроизвести подобную ситуацию в нашем проекте. Выполним следующий код в репозитории hexlet-git:
echo 'new line' >> INFO.md echo 'Hello, Hexlet! How are you?' > README.md git status Changes not staged for commit: (use "git add . " to update what will be committed) (use "git restore . " to discard changes in working directory) modified: INFO.md modified: README.md no changes added to commit (use "git add" and/or "git commit -a")
Изменились оба файла. В один файл мы добавили строчку, в другом — заменили. Как теперь посмотреть эти изменения?
Для этого в Git есть команда git diff , которая показывает разницу между тем, что было и что стало:
--git a/INFO.md b/INFO.md index d5225f8..40f51f1 100644 --- a/INFO.md +++ b/INFO.md @@ -1 +1,2 @@ git is awesome! +new line diff --git a/README.md b/README.md index ffe7ece..00fd294 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -Hello, Hexlet! +Hello, Hexlet! How are you?
Вывод команды поначалу может смутить. Здесь довольно много служебных данных, за которыми уже идут изменения.
Вывод git diff содержит не файлы целиком, а именно те строки, которые изменились — и иногда строки вокруг измененных для удобства анализа.
Слева от строк ставится:
- Знак — , если строка была удалена
- Знак + , если строка была добавлена
Сама команда не просто выводит на экран разницу между файлами, но и запускает пейджер — специальную программу, которая позволяет перемещаться по выводу и искать внутри него нужные данные. Для этого используются клавиши:
- f — перемещение вниз
- b или u — перемещение вверх
- q — или выход из режима просмотра
По умолчанию команда git diff показывает изменения только для тех модифицированных файлов, которые еще не были добавлены в индекс. Подразумевается, что добавленные в индекс файлы смотреть не нужно, ведь мы их уже подготовили к коммиту. Но лучше еще раз увидеть эти изменения и все проверить. Для этого нужно запустить команду git diff с флагом —staged :
# Выведет все изменения, сделанные в рабочей директории # которые были добавлены в индекс git diff --staged
В целом, git diff — команда, которую нужно обязательно запускать перед каждым коммитом. С ее помощью можно проанализировать добавляемые изменения и исправить возможные ошибки, ведь иногда программисты случайно добавляют в коммит то, что туда не должно попасть.
Самостоятельная работа
- Выполните все шаги из урока
- Сделайте коммит с сообщением add new content
- Залейте изменения на GitHub
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Git для начинающих. Урок 3.
git status и git diff
Краткое содержание урока, основные инструкции для командной строки, полезные ссылки и советы.
Создаем тестовый сайт и инициализируем репозиторий
С этого момента мы будем проходить уроки на примере тестового сайта site-git. Проект содержит три файла: index.html, css/main.css и .gitignore. По мере прохождения уроков будем понемногу расширять сайт, вносить изменения и добавлять новые файлы.
Гнаться за красотой не будем, сайт очень простой. Главное, сосредоточиться на сути, поэтому верстки и стилей будет минимум. Даже если вы не знакомы с html и css, то легко разбересь в нашем простом примере.
Создайте на github новый приватный репозиторий site-git. Скачайте архив с файлами сайта и распакуйте содержимое в папку site-git
Обратите внимание на .gitignore
Кроме index.html и main.css в архиве лежит еще скрытый файл .gitignore. Это файл, в котором прописаны папки и файлы, которые git не отслеживает. Подробнее о .gitignore мы поговорим во второй части курса
После этого нужно инициализировать репозиторий, как мы делали в прошлом уроке. Не забудьте поправить имя Webdevkin на свое.
$ cd site-git $ git init $ git add . $ git commit -m "first commit" $ git remote add origin git@github.com:Webdevkin/site-git.git $ git push -u origin master
Теперь все приготовления сделаны, приступим к изменениям в проекте.
Вводная
Когда мы работаем над проектом, то делаем много изменений в разных файлах. Эти изменения легко потерять или запутаться в них.
Git позволяет легко отслеживать все изменения, видеть добавленные и удаленные файлы в проекте, а также изменения в самих файлах. Если мы изменим хоть один символ в коде, git заметит это изменение и уже не потеряет. Команды git status и git diff (от слова difference — разница) позволяют отслеживать все изменения в проекте.
git status
Эта команда показывает список измененных файлов
$ git status
Также git status выводит некоторую дополнительную информацию, которую мы изучим позже.
В PhpStorm — окно Version Control (Вызов окна — Alt+9)
git diff
Это команда для просмотра изменений в файлах. В терминале мы видим добавленные и удаленные строки. То есть если в строке изменился хоть один символ, то git diff покажет 2 строки: одну удаленную, другую — добавленную.
Просмотр всех изменений
$ git diff
Просмотр изменений в отдельном файле
$ git diff index.html
Изменения в PhpStorm
В онке Version Control мы сразу видим список измененных файлов. Чтобы посмотреть изменения в конкретном файле, нужно кликнуть на него и нажать Crtl+D (diff). В окне difference видны не только добавленные и удаленные строки, но и изменения в отдельных строках.
Плюсы PhpStorm
- список измененных файлов можно держать открытым, не обязательно набирать git status
- измененные строки видны сразу же в редакторе кода
- просмотр изменений визуально нагляднее, чем в терминале (показываются даже изменения в отдельных строках)
- проще откатить отдельные изменения
PhpStorm отлично интегрируется с git. Конечно, не он один. Некоторые продвинутые редакторы кода тоже могут работать с git. Вы можете посмотреть, как реализована поддержка git, например, в sublime, но это далеко не так удобно, как в IDE от JetBrains. Это не реклама, а просто совет использовать удобные инструменты.
PhpStorm платная программа (9 $/месяц на январь 2020 года), но это затраты, которые окупятся. Рекомендую.
Поддержка git в Sublime Text 3
В Sublime Text 3 ситуация несколько изменилась — поддержка git улучшилась. Подробнее рассмотрим работу с git в sublime во второй части курса
На этом все. В конспекте информации мало, только основные команды, поэтому смотрите видеоурок. Там все нагляднее и понятнее.
В следующем уроке мы познакомимся с коммитами и научимся с ними работать.
Спасибо за внимание и до встречи!
Все уроки курса
- Вводный урок
- 1. Установка и базовая настройка git
- 2. Создание и клонирование репозитория git
- 3. Делаем первые изменения, git status и git diff
- 4. Коммиты и история коммитов, git commit, git log и git show
- 5. Подробнее об истории коммитов. Путешествие по истории
- 6. Работа с сервером, git push и git pull
- 7. Ветки — главная фишка git, git branch и git checkout
- 8. Работа с ветками на сервере, git fetch
- 9. Слияния или мерджи веток, git merge
- 10. Конфликты и их разрешение
- Платная часть курса. Презентация
- * 11. Работа с gitignore и git exclude
- * 12. Буфер обмена git, git stash
- * 13. Копирование коммитов, git cherry-pick
- * 14. Отмена и редактирование последнего коммита
- * 15. Отмена произвольного коммита, git revert
- 16. Склеивание коммитов, git rebase —interactive и git reflog
- * 17. Зачем склеивать коммиты. Плюсы и минусы сквоша
- * 18. Работа с git rebase. Отличия от merge
- * 19. Что такое git push —force и как с ним работать
- * 20. Ищем баги с помощью git, git bisect
- * 21. Как и зачем работать с тегами git
- * 22. Процессы: github flow и git flow
- * 23. Псевдонимы в git
- 24. Мердж-реквесты
- * 25. Форки
A3.3 Приложение C: Команды Git — Основные команды
Всего несколько команд нужно для базового варианта использования Git для ведения истории изменений.
git add
Команда git add добавляет содержимое рабочего каталога в индекс (staging area) для последующего коммита. По умолчанию git commit использует лишь этот индекс, так что вы можете использовать git add для сборки слепка вашего следующего коммита.
Это одна из ключевых команд Git, мы упоминали о ней десятки раз на страницах книги. Ниже перечислены наиболее интересные варианты использования этой команды.
Знакомство с этой командой происходит в разделе Отслеживание новых файлов главы 2.
О том как использовать git add для разрешения конфликтов слияния написано в разделе Основные конфликты слияния главы 3.
В разделе Интерактивное индексирование главы 7 показано как использовать git add для добавления в индекс лишь отдельных частей изменённого файла.
В разделе Деревья показано как эта команда работает на низком уровне, чтобы вы понимали, что происходит за кулисами.
git status
Команда git status показывает состояния файлов в рабочем каталоге и индексе: какие файлы изменены, но не добавлены в индекс; какие ожидают коммита в индексе. Вдобавок к этому выводятся подсказки о том, как изменить состояние файлов.
Мы познакомили вас с этой командой в разделе Определение состояния файлов главы 2, разобрали стандартный и упрощённый формат вывода. И хотя мы использовали git status повсеместно в этой книге, практически все варианты использования покрыты в указанной главе.
git diff
Команда git diff используется для вычисления разницы между любыми двумя Git деревьями. Это может быть разница между вашей рабочей копией и индексом (собственно git diff ), разница между индексом и последним коммитом ( git diff —staged ), или между любыми двумя коммитами ( git diff master branchB ).
Мы познакомили вас с основами этой команды в разделе Просмотр индексированных и неиндексированных изменений главы 2, где показали как посмотреть какие изменения уже добавлены в индекс, а какие — ещё нет.
О том как использовать эту команду для проверки на проблемы с пробелами с помощью аргумента —check можно почитать в разделе Правила создания коммитов главы 5.
Мы показали вам как эффективно сравнивать ветки используя синтаксис git diff A…B в разделе Определение применяемых изменений главы 5.
В разделе Продвинутое слияние главы 7 показано использование опции -w для скрытия различий в пробельных символах, а также рассказано как сравнивать конфликтующие изменения с опциями —theirs , —ours и —base .
Использование этой команды с опцией —submodule для сравнения изменений в подмодулях показано в разделе Начало работы с подмодулями главы 7.
git difftool
Команда git difftool просто запускает внешнюю утилиту сравнения для показа различий в двух деревьях, на случай если вы хотите использовать что-либо отличное от встроенного просмотрщика git diff .
Мы лишь вкратце упомянули о ней в разделе Просмотр индексированных и неиндексированных изменений главы 2.
git commit
Команда git commit берёт все данные, добавленные в индекс с помощью git add , и сохраняет их слепок во внутренней базе данных, а затем сдвигает указатель текущей ветки на этот слепок.
Вы познакомились с основами модели коммитов в разделе Коммит изменений главы 2. Там же мы продемонстрировали использование опций -a для добавления всех изменений в индекс без использования git add , что может быть удобным в повседневном использовании, и -m для передачи сообщения коммита без запуска полноценного редактора.
В разделе Операции отмены главы 2 мы рассказали об опции —amend , используемой для изменения последнего совершённого коммита.
В разделе О ветвлении в двух словах главы 3 мы более подробно познакомились с тем, что делает команда git commit и почему она делает это именно так.
Мы показали вам как подписывать ваши коммиты, используя опцию -S в разделе Подпись коммитов главы 7.
И наконец мы заглянули внутрь команды git commit в разделе Объекты коммитов главы 10 и узнали что она делает за кулисами.
git reset
Команда git reset , как можно догадаться из названия, используется в основном для отмены изменений. Она изменяет указатель HEAD и, опционально, состояние индекса. Также эта команда может изменить файлы в рабочем каталоге при использовании параметра —hard , что может привести к потере наработок при неправильном использовании, так что убедитесь в серьёзности своих намерений прежде чем использовать его.
Мы рассказали об основах использования git reset в разделе Отмена индексации файла главы 2, где эта команда использовалась для удаления файла из индекса, добавленного туда с помощью git add .
В разделе Раскрытие тайн reset, полностью посвящённой этой команде, мы разобрались в деталях её использования.
Мы использовали git reset —hard чтобы отменить слияние в разделе Прерывание слияния главы 7, там же было продемонстрировано использование команды git merge —abort для этих целей, которая работает как обёртка над git reset .
git rm
Команда git rm используется в Git для удаления файлов из индекса и рабочей копии. Она похожа на git add с тем лишь исключением, что она удаляет, а не добавляет файлы для следующего коммита.
Мы немного разобрались с этой командой в разделе Удаление файлов главы 2, показали как удалять файлы из рабочего каталога и индекса и только из индекса, используя флаг —cached .
Ещё один вариант использования git rm приведён в разделе Удаление объектов главы 10, где мы вкратце объяснили как использовать опцию —ignore-unmatch при выполнении git filter-branch , которая подавляет ошибки удаления несуществующих файлов. Это может быть полезно для автоматически выполняемых скриптов.
git mv
Команда git mv — это всего лишь удобный способ переместить файл, а затем выполнить git add для нового файла и git rm для старого.
Мы лишь вкратце упомянули эту команду в разделе Перемещение файлов главы 2.
git clean
Команда git clean используется для удаления мусора из рабочего каталога. Это могут быть результаты сборки проекта или файлы конфликтов слияний.
Мы рассмотрели множество опций и сценариев использования этой команды в разделе Очистка рабочего каталога главы 7.