Расширенные сценарии git

#git

#мерзавец #git

Вопрос:

У меня есть несколько сложных сценариев, которые я хотел бы решить, но я не знаю как. Я надеюсь, что вы сможете помочь:

  1. A - B - C - D - E . Я хотел бы добавить изменения из commit D в commit B и удалить commit D . Как?
  2. A - B - C . Commit B содержит файл, который не должен был быть зафиксирован. Как удалить его из коммита?
  3. A - B - C - D . Commit C содержит файл , который должен был быть зафиксирован с помощью commit B . Как удалить его из commit C и добавить в commit B ?

Пожалуйста, обратите внимание: все коммиты находятся только в моей рабочей копии и еще не были отправлены.

Ответ №1:

Мои ответы используют git rebase довольно много. Если вы новичок в перебазировании, обязательно сначала прочтите эту главу о перебазировании git

  1. git cherry-pick B Как вы сами выяснили, вы могли бы rebase -i A поменять местами ревизии C / D, объединив B D.
  2. git rebase -i B отметьте B как редактировать и примените изменения.

    Теперь, чтобы действительно удалить объекты из базы данных объектов, вам может потребоваться использовать filter-branch для всех ветвей, содержащих объект. После этого может потребоваться некоторое время для истечения срока действия reflogs. Если объект чувствителен к безопасности, вы можете захотеть git gc --aggressive --prune=tomorrow . В книге Pro Git есть раздел, посвященный удалению объектов.
    git-filter-branch Всегда не забывайте включать --tag-name-filter cat , смотрите Комментарии

  3. Если весь C должен быть в B, просто

    git rebase -i B отметьте C с помощью squash, и это сведет коммиты воедино.

    Если это становится более сложным, чем это (только один файл из C должен быть отправлен в B), я бы разделил задание на более мелкие шаги, которые могут быть решены, как в описанных выше шагах:

    • разделите фиксацию C на две части: одну (C.1), чтобы объединить в фиксацию B, и одну (C.2), чтобы сохранить как новую ‘C’. Например, из главы Git Pro, на которую ссылались выше:

      Например, если вы хотите разделить коммит, вы должны указать ‘edit’ для этого коммита:

       pick   fc62e55 added file_size
      pick   9824bf4 fixed little thing
      edit   21d80a5 added number to log
      pick   76b9da6 added the apply command
      pick   c264051 Revert "added file_size"
        

      И затем, когда вы попадаете в командную строку, вы отменяете этот коммит и создаете два (или более) новых. Допустим, 21d80a5 изменил два файла, file1 и file2, и вы хотели разделить их на отдельные коммиты. Вы могли бы сделать это после перебазирования в командную строку :

       $ git reset HEAD^
      $ git add file1
      $ git commit 'first part of split commit'
      $ git add file2
      $ git commit 'second part of split commit'
      $ git rebase --continue
        

      И теперь вместо 5 коммитов у вас будет 6.

    • перебазирование для объединения коммитов B и C.1

Комментарии:

1. Не могли бы вы подробнее рассказать о «не забудьте включить --tag-name-filter cat «? Почему это опасно, и почему это всегда должно быть включено?

2. @unutbu: по умолчанию ветвь фильтра git «перезаписывает» (фильтрует) дерево версий, доступное по ссылкам, указанным в командной строке (или HEAD). Использование --all вы можете привести к перезаписи почти всех имен ссылок). Однако имена тегов не перезаписываются, если не присутствует --tag-name-filter параметр. Это может привести к неприятным сюрпризам при забвении (внезапно теги преобразуются в «устаревшие» коммиты, которые уже давно были переписаны). Теперь на практике теги используются в основном для опубликованных версий, и вам все равно не следует переписывать опубликованную историю .

3. … Тем не менее, мне бы понравилось, если бы cat фильтр имен тегов был установлен по умолчанию.

4. Большое спасибо за ваш подробный ответ. Я прочитаю это и предоставленные ссылки и проверю позже.

5. @sehe: Спасибо. 1) выбор вишни не сработал, это создало конфликты. Тем не менее, я использовал git rebase -i и переключал коммиты C и D и изменил pick перед D на f .