Краткий обзор команды git diff

Итак, продолжаю знакомиться с системой Git и на этот раз вопрос будет касаться команды

1
git diff
. Это краткая заметка, которая ни в коей мере не претендует на полноценный обзор. Скорее всего - философское рассуждение на тему сравнения в Git.

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

Когда изменение обнаружено, тогда можно решать, что с ним делать - оставить, удалить или отредактировать.

Перед использованием команды

1
diff
стоит напомнить о трех состояниях системы Git: Working Area, Staging Area, Repository. Фактически, команда
1
diff
производит сравнение между разными состояниями одного файла.

Поэтому, когда запускается команда

1
diff
, следует принимать во внимание, что и с чем будет сравниваться.

Working Area

Рассмотрим первый случай, когда имеется отслеживаемый файл

1
index.html
, в который вносятся изменения.

Но изменения в этом файле не индексируются (

1
git add
) и не фиксируются (
1
git commit
).

В этом случае, чтобы увидеть изменения, нужно запустить команду:

$ git diff

В этом случае производится сравнение между фиксированной версией файла

1
index.html
(в области Repository) и его измененной версией (в области Working Area).

Вывод будет примерно таким:

$ git diff
diff --git a/index.html b/index.html
index 8fbea1c..fff54a9 100644
--- a/index.html
+++ b/index.html
@@ -5,6 +5,6 @@
   <title>Index</title>
 </head>
 <body>
-
+  <h1>Header 1</h1>
 </body>
 </html>

В этом примере все предельно ясно и понятно. Строка

1
--- a/index.html
- это фиксированная версия файла
1
index.html
. Строка
1
+++ b/index.html
- это измененная версия файла
1
index.html
.

Две строки:

-
+  <h1>Header 1</h1>

… отображают, сколько и каких строк удалено (знак минус); сколько и каких строк добавлено (знак плюс).

Итак, с первым вариантом разобрались. Комадна

1
git diff
выполняет сравнение версии файла из области Repository и этой же версии из области Working Area.

Staging Area

Второй вариант - файл

1
index.html
отслеживается, в него внесено изменение, которое проиндексировано (внесено в область Staging Area).

Команда

1
git diff
ничего не покажет, так как изменения в файле
1
index.html
были перенесены (
1
git add
) из области Working Area в область Staging Area. Другими словами, область Working Area чистая и в ней нет ничего, чтобы можно было сравнить с областью Repository.

В этом случае для команды

1
git diff
необходимо добавить ключ
1
--staged
. Тогда вывод будет примерно таким:

$ git diff --staged
diff --git a/index.html b/index.html
index 8fbea1c..fff54a9 100644
--- a/index.html
+++ b/index.html
@@ -5,6 +5,6 @@
   <title>Index</title>
 </head>
 <body>
-
+  <h1>Header 1</h1>
 </body>
 </html>

Ключ

1
--staged
указывает, что необходимо сравнивать область Repository с областью Staging Area.

Итак, разобрались со вторым случаем. Команда

1
git diff --staged
производит сравнение области Repository с областью Staging Area.

Repository

Третий вариант - файл

1
index.html
отслеживается, в него внесены изменения, которые проиндексированы (
1
git add
) и зафиксированы (
1
git commit
).

В этом случае область Working Area и Staging Area чистые от изменений, поэтому область Repository нельзя сравнивать с ними - там нет ничего для сравнения.

Поэтому команда

1
git diff
или
1
git diff --staged
ничего не покажет - сравнивать то не с чем!

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

В этом случае команда сравнения

1
git diff
будет выглядеть примерно таким образом:

$ git diff 0644c20  73a4c4c
diff --git a/index.html b/index.html
index fff54a9..8fbea1c 100644
--- a/index.html
+++ b/index.html
@@ -5,6 +5,6 @@
   <title>Index</title>
 </head>
 <body>
-  <h1>Header 1</h1>
+
 </body>
 </html>

Здесь

1
0644c20
- это первые семь символов hash-суммы последнего commit’а,
1
73a4c4c
- первые семь символов hash-суммы предпоследнего commit’а.

Их можно получить командой:

$ git log --oneline

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

Итак, разобрались с третим вариантом - когда сравниваются командой

1
git diff hash_sum_1 hash_sum_2
между собой различные коммиты, расположенные в области Repository.

На этом все.


Решил написать небольшой обзор по редактору Remarkable

Озадачился выбором markdown-редактора под Linux. После недолгих поисков с легким и приятным удивлением нашел две программы под эту операционную систему. Все течет, все меняется и вот теперь под Linux есть два достойных markdown-редактора.

Первый редактор - это Remarkable, второй редактор - это Haroopad. После недолгого сравнения обеих программ оставил в своей коллекции редактор Remarkable по нескольким причинам.

Обзор Remarkable

Чем мне понравился Remarkable? Это простая и интуитивно понятная программа, с таким же приятным и понятным интерфейсом. Что называется - установил и пользуйся.

Одна особенность этого редактора - на момент написания статьи существует только версия под Linux (преимущественно под Ubuntu).

После установки и запуска редактор будет иметь такой примерный вид:

Remarkable - markdown editor

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

Вставка, открытие и сохранение документа; иконки Undo и Redo; форматирование текста - Bold, Italic, Stroke; удобные кнопки для вставки на страницу изображения, ссылки или timestamp:

Remarkable - insert images and links

Окно редактора Remarkable разбито на две части - левая для редактирования, правая - для предпросмотра. Все как и должно быть в нормальном markdown-редакторе.

Предварительный просмотр можно отключить:

Remarkable - disable live preview

Есть также “ночной” режим работы:

Remarkable - night mode

Программа поддерживает синтаксис Github Flavoured Markdown:

Markdown Support

Markdown Support

На GitHub имеется подробный пример markdown-синтаксиса - Remarkable Demo, созданного в этом редакторе. Там есть что посмотреть и чему поучиться - своеобразная реклама редактора Remarkable.

Ниже приведен официальный демо-ролик, показывающий возможности редактора Remarkable:

Одна приятная и мощная особенность редактора - возможность экспортирования в HTML или PDF.

Remarkable автоматически подсчитывает число строк, слов и символов в тексте и отображает эту информацию в строке состояния.

Заключение

Мой субъективный вывод - программа Remarkable является отличным выбором markdown-редактора под Linux. Простая, удобная, интуитивно понятная.

В качестве редактора для ежедневной работы контент-менеджера - лучший выбор под операционной системой Linux. Все самое нужное - под рукой и ничего лишнего. Стоит ли упоминать, что данная статья написана в редакторе Remarkable.

На этом все.


Продолжаю изучение темы Git и GitHub. На повестке дня стоит вопрос - каким образом можно изменить ссылку существующего репозитория?

Нет - не так! Попробую зайти с другой стороны и сказать иначе. Имеется готовый репозиторий Template, размещенный на сервере GitHub. Этот репозиторий является шаблоном (template starter) при создании разнообразных проектов. Нечто похожим на известный HTML5 Boilerplate.

Репозиторий Template клонируется на локальную машину с именем разрабатываемого проекта, такой командой:

$ git clone https://github.com/gearmobile/template.git project

Затем в созданном репозитории Project разрабатывается требуемый проект.

Но есть одно НО - необходимо преобразовать видоизмененный репозиторий Project в отдельный, самостоятельный репозиторий. Конечно, по большому счету, это уже и есть отдельный, самостоятельный репозиторий.

Но вот ссылка у репозитория Project указывает на оригинал - репозиторий Template. И если произвести

1
push
на GitHub, то произойдет обновление репозитория Template.

А этого крайне нежелательно допустить, так как этот репозиторий является стартовым, чистым листом для всех новых проектов!

У меня же стоит такая задача - скопировать стартовый репозиторий Template на локальную машину, преобразовать его в конкретный проект, вновь залить на GitHub уже как самостоятельный репозиторий с именем проекта в качестве имени репозитория. Как поступить?

Можно решить вопрос несколькими способами. Ниже приведу пару из них - самых простых и доступных для моего понимания вечного newbie в Git\GitHub. Может быть, по мере освоения темы дополню статью более универсальным и грамотным способом.

Правка config

У клонированного на локальную машину репозитория ссылка на его удаленный оригинал размещена в конфигурационном файле

1
config
по пути
1
.git/config
, в секции
1
[remote "origin"]
, в переменной с именем
1
url
:

$ cat .git/config

[core]
  repositoryformatversion = 0
  filemode = true
  bare = false
  logallrefupdates = true
  ignorecase = true
  precomposeunicode = true

[remote "origin"]
  url = https://github.com/gearmobile/template.git
  fetch = +refs/heads/*:refs/remotes/origin/*

[branch "master"]
  remote = origin
  merge = refs/heads/master

Поэтому в локальном репозитории Project можно просто изменить эту ссылку с помощью любого текстового редактора.

Отредактирую файл

1
config
и изменю в нем ссылку с

https://github.com/gearmobile/template.git

на

https://github.com/gearmobile/project.git

… где последняя - это ссылка на новый пустой репозиторий Project, который я создал на GitHub.

Теперь конфигурационный файл

1
config
для локального репозитория Project будет выглядеть таким образом (обратить внимание на переменную
1
url
):

$ cat .git/config

[core]
  repositoryformatversion = 0
  filemode = true
  bare = false
  logallrefupdates = true
  ignorecase = true
  precomposeunicode = true

[remote "origin"]
  url = https://github.com/gearmobile/project.git
  fetch = +refs/heads/*:refs/remotes/origin/*

[branch "master"]
  remote = origin
  merge = refs/heads/master

Все - теперь локальный репозиторий Project является абсолютно самостоятельным и уникальным репозиторием, связанным ссылкой со своей удаленной копией на сервере GitHub.

Осталось только сделать

1
push
, чтобы залить на GitHub. Правда, здесь придется воспользоваться ключом
1
-f
(как это описано в предыдущей статье Откат коммитов на GitHub):

$ git push -f

Команда set-url

Второй способ практически идентичен предыдущему за тем лишь исключением, что он более правильный, так как для изменения url-адреса репозитория используется предназначенная для этого консольная команда Git -

1
set-url
.

Точно также создаю на локальной машине копию Another Project удаленного репозитория Template:

$ git clone https://github.com/gearmobile/template.git another-project

Ссылка в новом репозитории Another-Project все также указывает на свой оригинал - репозиторий Template:

$ cat .git/config

[core]
  repositoryformatversion = 0
  filemode = true
  bare = false
  logallrefupdates = true
  ignorecase = true
  precomposeunicode = true

[remote "origin"]
  url = https://github.com/gearmobile/template.git
  fetch = +refs/heads/*:refs/remotes/origin/*

[branch "master"]
  remote = origin
  merge = refs/heads/master

Создаю на GitHub новый репозиторий Another-Project, который будет удаленной копией локального (уже существующего) репозитория Another-Project. И изменяю ссылку на вновь созданный удаленный репозиторий Another-Project:

$ git remote set-url origin https://github.com/gearmobile/another-project.git

Проверяю, изменилась ли ссылка в конфигурационном файле

1
config
(переменная
1
url
):

$ cat .git/config

[core]
  repositoryformatversion = 0
  filemode = true
  bare = false
  logallrefupdates = true
  ignorecase = true
  precomposeunicode = true

[remote "origin"]
  url = https://github.com/gearmobile/another-project.git
  fetch = +refs/heads/*:refs/remotes/origin/*

[branch "master"]
  remote = origin
  merge = refs/heads/master

Да, ссылка была успешно изменена на новый удаленный репозиторий Another-Project. Можно вносить изменения и выполнять

1
push
на GitHub.

Небольшое заключение

Преимущество двух описанных выше способ в том, что не теряется история коммитов.

На этом пока все.


Небольшая статья, посвященная вопросу отката коммитов в GitHub.

В чем заключается вопрос, собственно? В том, что имеется определенный репозиторий, размещенный на сервере GitHub. У этого удаленного репозитория есть локальная синхронизированная версия на рабочей машине автора. Этот репозиторий изменяется с большей или меньшей периодичностью; все изменения фиксируются соответствующими коммитами.

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

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

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

Шаг первый

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

$ git log

Ниже показан краткий вывод команды

1
git log
- четыре самых поздних коммита репозитория:

commit ee3a2ae6888fb87d5013786f6cf3b18da63f7bbb
Author: gearmobile <gearmobile@gmail.com>
Date:   Mon Apr 6 17:48:08 2015 +0300

    End

commit 6d92268e42eace0c78a5150144645333b769623d
Author: gearmobile <gearmobile@gmail.com>
Date:   Mon Apr 6 17:06:35 2015 +0400

    Close to end

commit 855404bf39b5fabd45ba0c6f5702e7a28949a02e
Author: gearmobile <gearmobile@gmail.com>
Date:   Mon Apr 6 15:24:04 2015 +0400

    End 2014-07-16

commit 3eb55145a79f9f8f732338a0e80bd71b2325b6da
Author: gearmobile <gearmobile@gmail.com>
Date:   Mon Apr 6 12:48:17 2015 +0400

    End

Мне нужно вернуть репозиторий из состояния, описанного в коммите

1
ee3a2ae6888fb87d
, в состояние, зафиксированное в коммите
1
6d92268e42eace0c
.

Шаг второй

Для этого я воспользуюсь командой

1
reset
, с ключом
1
--hard
. Эта команда мне подходит потому, что я не собираюсь сохранять изменения, зафиксированные в коммите
1
ee3a2ae6888fb87d
, так как они полностью ошибочны.

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

$ git reset --hard 6d92268e42eace0c

где

1
6d92268e42eace0c
- начальные 16 символов hash-суммы коммита, на который я хочу “перепрыгнуть”. Полный вид hash-суммы в 40 символов можно не использовать - достаточно 16 символов для надежной идентификации конкретного коммита.

После выполнения этой команды локальный репозиторий будет сброшен к состоянию, зафиксированному в коммите

1
6d92268e42eace0c
. Другими словами, я избавился от последних ошибочных изменений и вернул репозиторий в предыдущее его состояние.

Однако, это всего лишь половина дела, так как откат коснулся только локального репозитория. В этой статье речь идет прежде всего об удаленном репозитории, размещенном на GitHub. Как добиться внесения аналогичных изменений на GitHub?

Казалось бы - просто! Достаточно выполнить команду:

$ git push

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

Вместо этого Git предложит выполнить комнаду

1
git pull
, чтобы привести локальный репозиторий к удаленному.

Шаг третий

Чтобы заставить Git выполнить обратную задачу - привести удаленный репозиторий к локальному, необходимо использовать ключ

1
-f
.

То есть, форсировать внесение изменений и тем самым “сказать” Git - синхронизировать удаленный репозиторий с локальным репозиторием несмотря на то, что последний имеет более ранюю версию:

$ git push -f

Заключение

Теперь, если проверить состояние удаленного репозитория на сервере GitHub, то увидим, что оно идентично состоянию локального репозитория. Но самое главное - удален последний ошибочный коммит, чего я и добивался.

На этом все,


Как оказалось, работа в программе EasyTAG - это нетривиальная задача. Интерфейс программы не является интуитивно понятным (по крайней мере - для меня).

У меня стояло несколько задач для обработки файлов аудио-книг:

  • одновременное редактирование тегов для нескольких mp3-файлов
  • добавление изображений к mp3-файлу
  • переименование mp3-файлов по их тегам

Во всех трех случаях мне пришлось заглядывать в справку F1 программы EasyTAG, чтобы разобраться с вопросами.

EasyTAG - редактирование нескольких mp3-файлов

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

  1. Выделить в окне EasyTAG все редактируемые mp3-файлы
  2. Изменить значение тега
  3. Нажать на значок “Ab” в поле тега

Редактирования нескольких файлов одновременно

  1. Сохранить внесенные изменения

EasyTAG - добавление изображения

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

Первый значок (шестренка) служит для открытия окна редактирования изображения (обложки) файла. Можно изменить заголовок изображения, а также его тип.

Второй значок служит для того, чтобы назначить изображение для всех выбранных mp3-файлов. Это аналог значка “Ab”, только для изображений.

Добавление изображения

EasyTAG - переименование mp3-файлов по тегам

Еще одна крайне важная и полезная функция, которая необходима в программе EasyTAG - это переименование mp3-файлов по значениям тега этого mp3-файла. Другими словами, имеется mp3-файл такого (примерного) вида -

1
2013-04-02-Альфред-ван-Вогт-Зачарованная деревня.mp3
. Это mp3-файл нужно переименовать в программе EasyTAG и привести к виду
1
Альфред-ван-Вогт - Зачарованная деревня.mp3
.

Программа EasyTAG обладает такой возможностью, вот только “добраться” (точнее - догадаться) до этой функции с первого раза не получиться. Ниже приведена последовательность действий для достижения этой цели.

  1. Выделить в окне EasyTAG все редактируемые mp3-файлы
  2. Нажать иконку “Show scanner”. Откроется окно “Tag and Filename Scan”, в котором в поле “Scanner” из выпадающего списка необходимо выбрать запись “Rename File and Directory”. Во втором поле окна размещен обширный список готовых масок для переименования файлов. Можно выбрать готовую маску, а можно создать свою - это очень просто сделать. Должно получиться нечто похожее на следующее:

Переименование файлов по тегам

  1. Нажать кнопку “Scan Files”, чтобы применить маску к ранее выбранным mp3-файлам.
  2. Закрыть окно “Tag and Filename Scan”, нажав кнопку “Close”.
  3. Сохранить внесенные изменения. При этом появиться окно с предупреждением о переименовании mp3-файла (-ов)

Подтверждение переименования файлов

В принципе, на это все.