Всего через два месяца после версии 2.6 вышел Git 2.7 с новыми возможностями, исправлениями и улучшениями производительности. Что интересного он нам приготовил? Я расскажу о нескольких новинках, которые показались интересными команде Bitbucket.
Полноценный набор команд 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.
Автор: detouched