Всего через два месяца после версии 2.6 вышел Git 2.7 с новыми возможностями, исправлениями и улучшениями производительности. Что интересного он нам приготовил? Я расскажу о нескольких новинках, которые показались интересными команде Bitbucket.
Команда git worktree появилась в Git 2.5, она позволяет выгружать и одновременно работать со многими ветками репозитория в отдельных папках. Например, если нужно сделать срочную правку, но при этом не хочется трогать текущую рабочую копию, можно просто выгрузить нужную ветку в новую папку с помощью команды:
Git 2.7 добавляет команду git worktree list, которая выводит список рабочих копий репозитория и веток, ассоциированных с ними:
Улучшена поддержка многих рабочих копий командой git bisect. Ссылки, которые использовались bisect для «хороших» и «плохих» коммитов, переехали из .git/refs/bisect в .git/refs/worktrees/$worktree_name/refs/bisect, поэтому теперь стала возможной одновременная работа bisect в разных рабочих копиях репозитория.
Кроме того, начиная с Git 2.7 можно использовать git clone, указав в качестве аргумента такую отдельную рабочую копию, — при этом будет создан независимый git-репозиторий, а не ещё одна рабочая копия существующего.
Примечательно, что отдельные рабочие копии могут быть созданы не только для веток. Как и множество других команд, git worktree add можно вызвать с указателем на коммит, будь то его хеш или тег:
Если вы фанат git rebase, то, скорее всего, знакомы с опцией --autostash. Она автоматически сохраняет все локальные изменения во временное хранилище (stash) до выполнения rebase, а после его завершения применяет их снова.
Это удобно, поскольку можно делать rebase на «грязной» рабочей копии. Для ещё большего удобства существует параметр rebase.autostash, который делает описанное поведением по умолчанию. Применить его глобально можно с помощью команды:
Этот параметр существует ещё с Git 1.8.4, но в Git 2.7 добавлена возможность отменить его с помощью опции --no-autostash. Скорее всего, эта опция добавлена для полноты, поскольку единственное, что она даёт при попытке выполнить rebase на «грязной» рабочей копии, — это соответствующее предупреждение:
Говоря о конфигурации, стоит также упомянуть о параметре stash.showPatch, который также появился в Git 2.7. При стандартных настройках команда git stash show выводит только краткую информацию о файлах во временном хранилище:
Если же дополнительно указать опцию -p, вывод будет дополнен расширенным описанием изменений файлов:
Параметр stash.showPatch делает это поведением по умолчанию. Применить его глобально можно с помощью аналогичной команды:
Как и в предыдущем случае, включённый параметр можно отменить и тем самым вернуться к старому краткому выводу, — с помощью опции --stat:
Будьте аккуратны: опция --no-patch не приводит к ошибке, однако она не отменяет stash.showPatch, как можно было бы ожидать.
git filter-branch — это универсальный инструмент для изменения истории репозитория. Поскольку каждый коммит имеет ссылки на родительские коммиты, а значит, транзитивно ссылается на все коммиты, являющиеся его предками, изменение одного коммита неизбежно влечет за собой изменение всех его потомков. Это, в свою очередь, означает, что операция даже элементарного изменения истории может занять какое-то время, если изменить нужно достаточно старый коммит.
В Git 2.7 появился изящный индикатор прогресса, который отображает предполагаемое время до окончания выполнения filter-branch:
Помимо этого, в случае если filter-branch не изменяет объекты в индексе или деревья, то индекс вообще не читается при выполнении команды, что значительно увеличивает её производительность. Опция --commit-filter на анимации выше изменяет только автора каждого коммита и не затрагивает ассоциированные с ними объекты деревьев. Изменение первых 1000 коммитов Bitbucket Server заняло всего 38 секунд при использовании Git 2.7.0, в то время как аналогичная операция с Git 2.6.0 потребовала 64 секунды, то есть прирост скорости составляет целых 40%. Тесты производительности, появившиеся в Git 2.7 вместе с этими улучшениями, показывают ещё более впечатляющее ускорение — до 60%.
Файлы .gitignore позволяют исключить из репозитория некоторые файлы, находящиеся в рабочей копии, т.е. они не будут добавляться в индекс. Шаблоны поддерживают флаг отрицания с помощью префикса !, чтобы было возможно отменить игнорирование определённого файла. Например, при таких шаблонах git будет игнорировать все файлы с расширением .json, кроме cat.json:
Однако в Git 2.6 нельзя было применить отрицание к файлу, находящемуся в уже игнорируемой папке.
Начиная с Git 2.7, второй пример работает ровно так, как этого стоило ожидать: с помощью ! теперь можно отменить игнорирование файлов в папках, которые иначе были бы игнорированы.
Это только небольшая часть плюшек, которые появились в Git 2.7. Полный список изменений можно найти в заметках к релизу, а также в комментариях к коммитам в репозитории самого Git:
Автор оригинальной статьи — Тим Петтерсен, участвовал в разработке JIRA, FishEye/Crucible и Stash. С начала 2013 года он рассказывает о процессах разработки, git, непрерывной интеграции и поставке (continuous integration/deployment) и инструментах Atlassian для разработчиков, особенно о Bitbucket. Тим регулярно публикует заметки об этих и других вещах в Twitter под псевдонимом @kannonboy.
Полноценный набор команд git worktree
Команда git worktree появилась в Git 2.5, она позволяет выгружать и одновременно работать со многими ветками репозитория в отдельных папках. Например, если нужно сделать срочную правку, но при этом не хочется трогать текущую рабочую копию, можно просто выгрузить нужную ветку в новую папку с помощью команды:
$ git worktree add -b hotfix/BB-1234 ../hotfix/BB-1234
Preparing ../hotfix/BB-1234 (identifier BB-1234)
HEAD is now at 886e0ba Merged in bedwards/BB-13430-api-merge-pr (pull request #7822)
Git 2.7 добавляет команду git worktree list, которая выводит список рабочих копий репозитория и веток, ассоциированных с ними:
$ git worktree list
/Users/kannonboy/src/bitbucket/bitbucket 37732bd [master]
/Users/kannonboy/src/bitbucket/staging d5924bc [staging]
/Users/kannonboy/src/bitbucket/hotfix/BB-1234 37732bd [hotfix/BB-1234]
Улучшена поддержка многих рабочих копий командой git bisect. Ссылки, которые использовались bisect для «хороших» и «плохих» коммитов, переехали из .git/refs/bisect в .git/refs/worktrees/$worktree_name/refs/bisect, поэтому теперь стала возможной одновременная работа bisect в разных рабочих копиях репозитория.
Кроме того, начиная с Git 2.7 можно использовать git clone, указав в качестве аргумента такую отдельную рабочую копию, — при этом будет создан независимый git-репозиторий, а не ещё одна рабочая копия существующего.
Примечательно, что отдельные рабочие копии могут быть созданы не только для веток. Как и множество других команд, git worktree add можно вызвать с указателем на коммит, будь то его хеш или тег:
$ git worktree add ../git-2.4.7 ca00f80
Preparing ../git-2.4.7 (identifier git-2.4.7)
HEAD is now at ca00f80 Git 2.4.7
$ git worktree add ../git-v2.6.0 v2.6.0
Preparing ../git-v2.6.0 (identifier git-v2.6.0)
HEAD is now at be08dee Git 2.6
$ git worktree add ../git-v2.7.0 v2.7.0
Preparing ../git-v2.7.0 (identifier git-v2.7.0)
HEAD is now at 7548842 Git 2.7
$ git worktree list
/Users/kannonboy/src/git 7548842 [master]
/Users/kannonboy/src/git-2.4.7 ca00f80 (detached HEAD)
/Users/kannonboy/src/git-v2.6.0 be08dee (detached HEAD)
/Users/kannonboy/src/git-v2.7.0 7548842 (detached HEAD)
Несколько улучшений git stash
Если вы фанат git rebase, то, скорее всего, знакомы с опцией --autostash. Она автоматически сохраняет все локальные изменения во временное хранилище (stash) до выполнения rebase, а после его завершения применяет их снова.
$ git rebase master --autostash
Created autostash: 54f212a
HEAD is now at 8303dca It's a kludge, but put the tuple from the database in the cache.
First, rewinding head to replay your work on top of it...
Applied autostash.
Это удобно, поскольку можно делать rebase на «грязной» рабочей копии. Для ещё большего удобства существует параметр rebase.autostash, который делает описанное поведением по умолчанию. Применить его глобально можно с помощью команды:
$ git config --global rebase.autostash true
Этот параметр существует ещё с Git 1.8.4, но в Git 2.7 добавлена возможность отменить его с помощью опции --no-autostash. Скорее всего, эта опция добавлена для полноты, поскольку единственное, что она даёт при попытке выполнить rebase на «грязной» рабочей копии, — это соответствующее предупреждение:
$ git rebase master --no-autostash
Cannot rebase: You have unstaged changes.
Please commit or stash them.
Говоря о конфигурации, стоит также упомянуть о параметре stash.showPatch, который также появился в Git 2.7. При стандартных настройках команда git stash show выводит только краткую информацию о файлах во временном хранилище:
$ git stash show
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Если же дополнительно указать опцию -p, вывод будет дополнен расширенным описанием изменений файлов:
diff --git a/package.json b/package.json
index c876b26..e21eeb3 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
"mkdirp": "^0.5.0",
"byline": "^4.2.1",
"express": "~3.3.4",
- "git-guilt": "^0.1.0",
+ "git-guilt": "^0.1.1",
"jsonfile": "^2.0.0",
"jugglingdb-sqlite3": "0.0.5",
"jugglingdb-postgres": "~0.1.0",
Параметр stash.showPatch делает это поведением по умолчанию. Применить его глобально можно с помощью аналогичной команды:
$ git config --global stash.showPatch true
Как и в предыдущем случае, включённый параметр можно отменить и тем самым вернуться к старому краткому выводу, — с помощью опции --stat:
$ git stash show --stat
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Будьте аккуратны: опция --no-patch не приводит к ошибке, однако она не отменяет stash.showPatch, как можно было бы ожидать.
Ускорение git filter-branch и индикатор прогресса
git filter-branch — это универсальный инструмент для изменения истории репозитория. Поскольку каждый коммит имеет ссылки на родительские коммиты, а значит, транзитивно ссылается на все коммиты, являющиеся его предками, изменение одного коммита неизбежно влечет за собой изменение всех его потомков. Это, в свою очередь, означает, что операция даже элементарного изменения истории может занять какое-то время, если изменить нужно достаточно старый коммит.
В Git 2.7 появился изящный индикатор прогресса, который отображает предполагаемое время до окончания выполнения filter-branch:
Помимо этого, в случае если filter-branch не изменяет объекты в индексе или деревья, то индекс вообще не читается при выполнении команды, что значительно увеличивает её производительность. Опция --commit-filter на анимации выше изменяет только автора каждого коммита и не затрагивает ассоциированные с ними объекты деревьев. Изменение первых 1000 коммитов Bitbucket Server заняло всего 38 секунд при использовании Git 2.7.0, в то время как аналогичная операция с Git 2.6.0 потребовала 64 секунды, то есть прирост скорости составляет целых 40%. Тесты производительности, появившиеся в Git 2.7 вместе с этими улучшениями, показывают ещё более впечатляющее ускорение — до 60%.
Улучшенное отрицание в .gitignore
Файлы .gitignore позволяют исключить из репозитория некоторые файлы, находящиеся в рабочей копии, т.е. они не будут добавляться в индекс. Шаблоны поддерживают флаг отрицания с помощью префикса !, чтобы было возможно отменить игнорирование определённого файла. Например, при таких шаблонах git будет игнорировать все файлы с расширением .json, кроме cat.json:
# .gitignore
*.json
!cat.json
Однако в Git 2.6 нельзя было применить отрицание к файлу, находящемуся в уже игнорируемой папке.
# .gitignore
/animals
!/animals/cat.json # <-- этот файл игнорируется в Git 2.6 и более ранних версиях
Начиная с Git 2.7, второй пример работает ровно так, как этого стоило ожидать: с помощью ! теперь можно отменить игнорирование файлов в папках, которые иначе были бы игнорированы.
Но и это ещё не всё!
Это только небольшая часть плюшек, которые появились в Git 2.7. Полный список изменений можно найти в заметках к релизу, а также в комментариях к коммитам в репозитории самого Git:
$ git log v2.6.0..v2.7.0
Автор оригинальной статьи — Тим Петтерсен, участвовал в разработке JIRA, FishEye/Crucible и Stash. С начала 2013 года он рассказывает о процессах разработки, git, непрерывной интеграции и поставке (continuous integration/deployment) и инструментах Atlassian для разработчиков, особенно о Bitbucket. Тим регулярно публикует заметки об этих и других вещах в Twitter под псевдонимом @kannonboy.
Комментарии (8)
tendium
06.02.2016 20:22+3> Однако в Git 2.6 нельзя было применить отрицание к файлу, находящемуся в уже игнорируемой папке.
Вот так работало…
/uploads/*
!uploads/.gitkeepdetouched
07.02.2016 01:09+2В Вашем примере первая строка игнорирует не саму папку uploads, а все файлы/папки в ней.
В 2.7 поддержали отмену игнорирования файла даже если какая-то из родительских папок игнорирована. То есть, исключить файл uploads/some_dir/file из приведённого ограничения Git 2.7 сможет, а 2.6 — нет.
booomerang
08.02.2016 01:25-1Однако в Git 2.6 нельзя было применить отрицание к файлу, находящемуся в уже игнорируемой папке.
Да, как-то наткнулся на такую проблему, в итоге решил ее таким образом:
В родительской папке выставил так, папка folder_name всё равно игнорировалась:
*
!folder_name
Но внутри этой папки я создал новый файл .gitignore(folder_name/.gitignore) с таким содержимым и вроде, как сработало:
!*
Shablonarium
Не совсем понятна разница между копией репозитория и независимым репозиторием создаваемым командой git clone с дополнительным аргументом. Если лрмать мозг, можно подумать, что она создает еще один удаленный репозиторий в источнике.
detouched
Разница в том, что при использовании git worktree репозиторий по-прежнему остаётся один, отсюда следующие преимущества:
Это не значит, что иметь два локальных репозитория с одного и того же remote — плохо. Но есть ряд случаев, когда путь с git worktree проще и удобнее.
Shablonarium
Ах вот оно что… Спасибо за комментарий.
Dreyk
В дополнение к вышесказанному, git worktree трекает все папки, которые им созданы, поэтому их можно увидеть в git worktree list