Как оформить заказ на момент времени (т. Е. Зафиксировать) ветки Git? - PullRequest
2 голосов
/ 30 января 2012

Я хочу откатить свою ветку Git до определенного коммита.Поэтому я запускаю git log и нахожу хеш коммита SHA и запускаю git checkout <myhash>.

Обычно это работает просто отлично, но на этот раз что-то было подозрительно.Когда я снова посмотрел на git log, я увидел, что последний коммит правильный, но я пропускаю много коммитов дальше.Меня осенило: этот коммит является частью слияния из другой ветви (another), и я вижу историю этой ветки перед слиянием.

master's log, pre-merge:        D-C-----B'-B-A-----F
another's log:              7-6-----5-4--------3-2-F
master's log, post-merge: M-7-6-D-C-5-4-B'-B-A-3-2-F

Я проверяю коммит 5 иполучить коммиты в истории от another:

5-4-3-2-F

Но я бы хотел коммитов в истории от master, после слияния:

5-4-B'-B-A-3-2-F

I 'мы сделали репо , где вы можете проверить это:

$ git clone git://github.com/henrik242/Git-Branch-Test.git
$ cd Git-Branch-Test
$ git checkout -t origin/another
$ git log  ## The commits are named "test [2-7]"
$ git checkout master
$ git log  ## master's original commits are named "test [A-D]".  
$ git checkout 68c1226a0c  ## test 5 
$ git log  ## We now have the commits in the history from the "another" branch,
           ## even though this commit exists in the "master" branch as well

Я могу почти использовать git rebase -i, чтобы делать то, что я хочу:

$ git branch back-in-time
$ git checkout back-in-time
$ git rebase -i 18b1a648bc  ## the SHA1 of 'test 4', the commit before 'test 5'

Редактор появляется: Удалить нежелательные коммиты.Сохраните и выйдите, и git log теперь показывает нужные коммиты:

5-4-B'-B-A-3-2-F

Проблема в том, что git rebase -i не показывает коммиты в том же порядке, что и git log, что затрудняетвыбрать правильные коммиты.

Ответы [ 3 ]

1 голос
/ 31 января 2012

Слияние не смешивает коммиты, оно просто объединяет ветви, как показано в ответе @Jefromi.Поэтому, конечно, вы можете восстановить состояние дерева в любой момент времени, но если время до слияния, вам нужно будет позаботиться о том, чтобы выбрать правильную ветвь.Итак, я думаю, вы должны использовать git log --oneline --graph, чтобы вы могли выбрать последний коммит в соответствующей ветке в данный момент времени.Когда вы проверяете этот коммит, он, очевидно, будет иметь только историю коммитов этой ветви в данный момент времени.

Если вы хотите действительно смешивать коммиты из обеих веток, вам понадобится рабочий процесс, который используетgit cherry-pick или git rebase -i, чтобы упорядочить коммиты из обеих ветвей в хронологическом порядке в прямую ветвь.Это требует всевозможных проблем, наименьшее количество конфликтов слияния.Кроме того, промежуточные состояния, скорее всего, будут семантически несовместимыми.

0 голосов
/ 04 февраля 2012

Я просто отвечу на свой вопрос: чтобы этот рабочий процесс работал (и имел какой-то смысл), я должен использовать топологическое упорядочение журнала git: git log --topo-order. Таким образом, журналы выглядят так:

master's log, pre-merge:                D-C-B'-B-A-F
another's log:              7-6-5-4-3-2------------F
master's log, post-merge: M-7-6-5-4-3-2-D-C-B'-B-A-F

Теперь порядок в git rebase -i имеет смысл, так как он также в топологическом порядке. Просто отрежьте нижние коммиты, пока не достигнете коммита и не сохраните его, и вуаля!

(Как примечание, мне сначала понадобился этот рабочий процесс, потому что я попытался точно определить, какой коммит привел к ошибке. Впоследствии я понял, что git bisect справляется с этим лучше меня.)

0 голосов
/ 31 января 2012

Я не могу сказать это достаточно раз: история, о которой вы просите, никогда не существовала. Если вы хотите создать его вручную, но нет ничего такого, что вы могли бы проверить. Слияние никоим образом не объединяет в себе какую-то объединенную историю. Если ваш вопрос «как мне вручную создать такую ​​историю», задайте ее. Но вы, похоже, уже знаете, как использовать rebase.

Я думаю, что ваше основное недоразумение может быть результатом вывода git-log. Порядок коммитов показан git log по сути произвольным выбором. История не линейна, но она должна перечислять их один за другим, так и есть. Этот порядок не означает, что один коммит является родительским для предыдущего, и, следовательно, это не означает, что если вы выберете один из этих коммитов, под ним будет напечатано объединение всего. Он может распечатывать списки коммитов, как вы указали в своем вопросе, но в вашем примере репозитория это выглядит как история Вы можете увидеть это с gitk --all или git log --graph.

1 - 2 - 3 - 4 - 5 - 6 - 7 (another-branch)
 \                       \
  A - B - C - D - --------M (master)

Вот и все. Слияние не волшебным образом соединяет эти ветви. Он создает один коммит, чьи родители являются объединенными коммитами, содержащий комбинацию содержимого объединенных коммитов. Таким образом, вы можете проверить коммит 2 или коммит B, но «коммит 2 в основной ветке» отсутствует. Вы можете, если хотите, проверить коммит B и объединить коммит 2 и посмотреть, что вы получите.

Список коммитов, которые вы говорите, что вы хотите, насколько я могу судить, это то, что вы получили бы, объединив коммиты 5 и B, и сгладив их произвольным образом. Если вы просто хотите получить результат слияния:

git checkout -b foo <commit-5>
git merge <commit-B>

Если вам на самом деле нужна плоская история:

git checkout -b foo <commit-5>
git rebase -i <commit-B>
# reorder them however it is you wanted them

Но это все пользовательские решения. Вы создаете состояние хранилища, которого никогда не было. Так как его никогда не было, вы должны его создать.


Что касается обсуждения хэшей в комментариях, опять же, SHA1 однозначно идентифицирует коммит. SHA1 для коммита 2 - это SHA1 только для этого коммита. Вы не доказали, что я не прав в своем тестовом репо, вы доказали, что я прав: там только один коммит 2 и у него только один хэш.

Коммит - это объект, содержащий несколько вещей: метаданные (имя автора / коммиттера, адрес электронной почты, дата; сообщение о коммите), ссылку на его родительский (ые) объект и дерево, которое он представляет (воспринимайте его как снимок содержимое). Таким образом, commit 2 представляет этот конкретный снимок. Ни больше ни меньше. Его родитель - коммит 1. Это не вопрос временных меток; коммит знает своих родителей по их SHA1. Родитель коммита 2 - коммит 1, несмотря ни на что. Существует только один коммит 2, независимо от того, достигли ли вы его из основной ветви или из другой ветви - посмотрите на рисунок, и вы увидите, как он находится в основной ветви, через 7, 6, 5, 4, 3.

...