2015-01-27 3 views
4

У меня есть следующая ситуация в моем репозитории GIT. Кто-то забыл сделать попытку хозяина, прежде чем совершать его изменения, а затем совершил свой собственный мастер. После этого он почему-то объединил источник/мастер в своего местного хозяина, а затем толкнул его. В результате, происхождение/мастер вроде бы «переключилось» на то, что было его местным мастером. Имею ли я смысл? Вот пример:Исправление истории в репозитории GIT

ПЕРЕД PUSH

x----x-----x----x----x----x----x-----x----x (MASTER) 

ПОСЛЕ PUSH

---------------------------------------------x---x (MASTER) 
|             | 
x----x-----x----x----x----x----x-----x----x------- 

Это своего рода перепутались в хранилище, так как все уже история, кажется, был на ветке.

После этого появились новые коммиты, которые были перенесены на нового хозяина, а затем по той причине, которая не имеет значения прямо сейчас, мы решили, что мы этого не хотим, поэтому нам удалось сбросить коммиты, и в то же время восстановить MASTER на свое старое место. Как это:

ПЕРЕД

---------------------------------------------x---x---x---x---x (MASTER) 
|             | 
x----x-----x----x----x----x----x-----x----x------- 

ПОСЛЕ

           (2) 
---------------------------------------------x---x---x---x---x-- 
|            |    | 
x----x-----x----x----x----x----x-----x----x-----x----------------x (MASTER) 
             (1)     (3) 

Как вы можете видеть, теперь, совершить который был Dónde на парня, который забыл ту тянуть были объединены в то, что первоначально был мастером , Это было достигнуто, как это:

git checkout <HASH OF COMMIT MARKED AS (1) > 
git checkout -b refactor_master 
git merge --no-ff <HASH OF COMMIT MARKED AS (2) > 
git push origin refactor_master 
git merge --strategy=ours mastergit checkout master 
git merge refactor_master 
git push origin master 

Это efectively внесли изменения, заложенные эти фиксации исчезает от мастера, а также обратился хозяин к тому, что было раньше. Однако у меня теперь есть «ветвь», которой не должно было существовать. Фактически последнее коммит, помеченное как (3), не вносит никаких изменений. Он только «переключает» мастеров. Есть ли способ заставить эти коммиты исчезнуть?

+0

Я не уверен, полностью ли я слежу за тем, что вы говорите, но я сделаю этот комментарий: у Git нет понятия «какая ветвь» произошла передача. На втором графике она могла быть обработана большей частью x в первой строке и 2 x на второй строке; он представляет один и тот же граф фиксации таким образом. – Nayuki

+0

Почему бы просто не вернуть мастера к последнему фиксации до того, как все плохие вещи произошли? –

+0

Что это значит, что «вся история теперь, похоже, была на ветке»? Где еще будет? Как мастер-ветвь может оказаться не в том месте? Имеет ли филиал физическое местоположение? –

ответ

2

Имеет смысл: то, что он сделал, было нарушением правила «главная линия развития является родительским».

Обратите внимание, что в самом git нет ничего, что могло бы обеспечить соблюдение этого правила. Это невозможно, по одной простой причине: кто определяет, какая строка является «главной линией»? Единственный возможный ответ на этот вопрос - «вы», где «вы» означает «кто бы ни запускал git, чтобы манипулировать графом фиксации». Так что это не действительно правило git, это правило «люди, которые используют git».

Всякий раз, когда вы запускаете git merge (или в этом случае он запускает его), вы выбираете свою текущую ветку как основную линию разработки и все, что вы объединяете, в качестве альтернативной линии, которая объединяется. Таким образом, если вы сделаете это:

$ git checkout master 
$ make-some-change; git add ...; git commit -m message 

$ git fetch origin # and let's assume this brings in a new commit 
$ git merge origin/master 

вы говорите мерзавцу, чтобы сохранить ваш мастер в качестве основной линии, и объединить в предыдущих изменениях, как ветка.

Обратите внимание, что последние две команды - git fetch, за которыми следует git merge -are, что git pull по умолчанию. Это, в свою очередь, означает, что «основная линия является первичным родителем» довольно часто нарушается и от нее нельзя полагаться, если вы не очень строги/осторожны.


Есть ли способ, чтобы сделать это [слияние] совершившим исчезают?

Да, но только путем написания новой строки фиксации («переписывание истории»).

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

------------------------A---M1--B--C--D 
/      /   \ 
o--o--o--o--o--o--o--o--o---x-------------M2 <-- master 

коммиты B через D являются «на неправильной линии »в этот момент, потому что первый родитель слияния с фиксацией M2 равен x, а его второй родитель - D. Между тем commit A является первым родителем M1, а x - вторым родителем M1.

Если вы действительно заботитесь много о первом родительском правиле, вы можете сделать новую линию фиксаций отрываясь совершить x:

------------------------A---M1--B--C--D 
/      /   \ 
o--o--o--o--o--o--o--o--o---x-------------M2 <-- master 
          \ 
           A'--B'--C'--D' <-- new-master 

Здесь A' «s первый и единственный родитель совершает x , который был фиксацией вершины master, когда вещи сначала «поступили не так», как это было. Тогда первый и единственный родитель B' - это A 'и т. Д.

Если после того, как у вас есть этот график, вы стереть из вашей доски совершает A через M2 и сделать master точку фиксации D', вы будете иметь это:

o--o--o--o--o--o--o--o--o---x 
          \ 
           A'--B'--C'--D' <-- master 

и теперь вы можете «выправить» ссылка от x до A', и это выглядит как хорошая линейная история.

Вот и сложная часть: это просто график , который вы хотите. Для каждой фиксации на графике git хранит дерево : набор файлов для размещения в вашем рабочем каталоге, когда вы git checkout, которые совершают. Дерево вы хотите, чтобы каждое совершение A' через D' не могло быть точно таким же, как у исходных деревьев на A по D.

Это довольно уверен, что деревья вы хотите для B', C' и D' будут такими же, как те, что вы имели для B, C и D соответственно. Однако дерево, которое вы хотите для нового коммита A', вероятно, является тем, которое в настоящее время находится под объединением M1. Это может быть таким же, как и при фиксации A, но это может и не быть. Это действительно зависит от того, как A сравнивается с M1.

Есть несколько пустых способов создания новых коммитов без большой ручной работы, но их сложно описать в тексте. Кроме того, этот вид «переписывания истории» - часть, которая происходит, когда вы принудительно делаете старую метку master на new-master, совершает ошибку D', причиняет боль всем вашим разработчикам, которые совершают коммиты, у которых есть M2 в качестве их родительской фиксации. Они должны скопировать эти коммиты на новые коммиты с новыми D' в качестве их родителей.

Это зависит от вас и от того, стоит ли эта боль.

+0

Как «стереть» коммиты, чтобы уменьшить линию? Я не получил эту часть. – manugarciac

+0

Технически вы этого не делаете: вы не можете (ну, не без каких-либо более сложных черт ниже уровня git), но у вас нет * have *. Это метки ветвей и другие ссылки (теги, штампы и т. Д.), Которые делают видимыми и достижимыми видимость. Когда вы меняете какие-либо ярлыки (например, 'master'), которые делали старые фиксации видимыми, они - или могут также быть, за исключением восстановления, которое вы можете сделать примерно на месяц. Поэтому вы просто вынуждаете ветвь 'master' указывать на новую целевую фиксацию (используя' git branch' или 'git reset'), и все готово. – torek

+0

Если я создаю «нового мастера», когда я сделаю это текущим мастером, будет ли старый фиксатор на главной остановке быть видимым? – manugarciac

0

Ветвь git - это всего лишь ярлык, указывающий на индивидуальную фиксацию. Конец не знает, какая ветвь (-ы) есть/в настоящее время указывает на него; и он не знает историю, о которой отрасли ранее указывали на нее. Таким образом, единственное, что действительно имеет значение (и нетривиально для изменения), - это сама история фиксации.

Самый простой способ выяснить это, вероятно, следующее: Найти новейшее обязательство, что вы думаете, представляет собой разумное состояние вашего кодовую, и выполните следующие команды (скажем, что этот коммит хеш является 123abc):

git checkout -B master 123abc 
git push -f origin master 

Это сделает master точкой на 123abc как локально (на машине того, кто выполняет эти команды), так и на сервере. Когда остальные разработчики запустили git fetch, их origin/master переместится на 123abc, и они смогут проверить его и переместить их master там с git checkout -B master origin/master (я не совсем уверен в синтаксисе для этой команды, хотя, и у меня нет git repository под рукой.)

Предупреждение: Если у вас нет ответвления, указывающего на фиксации, которые новее, чем 123abc, эти коммиты, похоже, исчезнут. Если вы хотите посмотреть их содержимое позже, чтобы очистить его и повторно зафиксировать, вы должны начать с создания ветвей (ов) для этих коммитов, например. git branch tempbranch 567def.

 Смежные вопросы

  • Нет связанных вопросов^_^