Есть существующая старая ветка old в которой вся история из результирующих и промежуточных снимков. Необходимо выбрать из неё только результирующие снимки и создать на их основе отдельную «чистую» ветку, например, master
Решение не обязательно должно соответствовать рисункам, возможно, есть более грамотный вариант, не знаю.
Рис. 1 — старая ветка.
Рис. 2 — вариант со слиянием.
Рис. 3 — вариант с копированием.
Чёрные — результирующие снимки.
Белые — промежуточные снимки.
Ответ
Как работать, чтобы такая проблема не возникала?
«Промежуточные» коммиты нужны и полезны. Очень удобно бывает сохраниться где-то посреди разработки: чтобы попробовать другое решение, чтобы перенести работу на другую машину, просто для спокойствия.
Но в итоговой истории они не очень-то интересны. Более того, я лично не хотел бы, чтобы кто-то через месяцы и годы смотрел, как я изобретал велосипеды, писал по строчке в час и делал дурацкие ошибки. Нет, я хочу один финальный коммит, как будто я сел, возложил руки на клавиатуру и создал шедевр. Возможно, вам тоже так будет удобнее.
Что делать-то?
1. Используйте фичеветки (feature branches)
Для работы над каждой задачей делайте отдельную ветку от master. Делайте в ней сколько душе угодно коммитов. Когда результат будет готов — вы сможете собрать все эти коммиты в один или несколько осмысленных (инструкция ниже) и замержить в master
Мержить рекомендую с --no-ff. Так получается более информативная история: видно, где закончились коммиты одной фичи и начались коммиты другой.
Подробнее о фичеветках
Про разные модели работы, в том числе с фичеветками.
2. Пользуйтесь commit --amend для промежуточных коммитов
Исправили одну строчку в фичеветке и снова нужно сделать коммит? Проще переписать старый.
git add path/filename
git commit --amend --no-edit
Здесь --no-edit означает, что мы не хотим менять сообщение коммита — оставим старое.
Важно: Нужен хотя бы один новый коммит в ветке, который вы будете переписывать. Если вы только что сделали новую ветку от master, пока что нельзя делать --amend. Иначе вы перепишете коммит из master
Важно: commit --amend, rebase -i и другие операции по переписыванию истории нельзя выполнять в общих (стабильных) ветках вроде master
Как объединить коммиты по группам в новой ветке?
Предположим, у нас есть такая история коммитов. «Промежуточные» коммиты обозначены как bullshit, а «результативные» — result x. Самый новый коммит — сверху.
git log --oneline --graph --decorate
* 71e8ed5 (HEAD -> oldbranch) result 2
* 0955502 bullshit
* cf30c6f bullshit
* d590b3d result 1
* 8266a62 bullshit
* ce5b35c bullshit
Пересобрать их в более красивую (что очень субъективно) историю нам поможет команда rebase -i
Сначала сделаем новую ветку, в которой будет результат операции.
git checkout -b newbranch
Поскольку здесь мы хотим заребейзить всю ветку целиком, добавляем --root
git rebase -i --root
Если нужно переписать историю только до какого-то коммита (не включая его), указываем хеш этого коммита.
git rebase -i ce5b35c
Открывается редактор с вот таким содержимым. Здесь порядок коммитов обратный: самый новый коммит — снизу
pick ce5b35c bullshit
pick 8266a62 bullshit
pick d590b3d result 1
pick cf30c6f bullshit
pick 0955502 bullshit
pick 71e8ed5 result 2
# Rebase 71e8ed5 onto f7569aa (6 commands)
...
Что в этой ситуации можно сделать с коммитами? Нам интересны две команды:
# p, pick = use commit
# оставить коммит как есть
# s, squash = use commit, but meld into previous commit
# слить содержимое коммита с ПРЕДЫДУЩИМ коммитом
Объединяем (squash) коммиты группами: первый «промежуточный» после каждого результата станет основой нового результата.
pick ce5b35c bullshit
s 8266a62 bullshit
s d590b3d result 1
pick cf30c6f bullshit
s 0955502 bullshit
s 71e8ed5 result 2
Когда вы сохраните этот текст и выйдете из редактора, откроется новый, с текстом сообщения для нового коммита result 1, потом для result 2 и так далее — столько раз, сколько коммитов вы хотите в итоге оставить. Первым будет самый старый, последним — самый новый. По умолчанию вам предлагается вот такой шаблон:
# This is a combination of 3 commits.
# This is the 1st commit message:
bullshit
# This is the commit message #2:
bullshit
# This is the commit message #3:
result 1
# Please enter the commit message for your changes. Lines starting
Когда вы отредактируете и сохраните все новые сообщения, rebase -i завершится. Получится примерно такая история:
* 588a7ac (HEAD -> newbranch) result 2
* 2713fda result 1
Комментариев нет:
Отправить комментарий