Как скрыть историю фиксации одного пульта от других пультов? - PullRequest
0 голосов
/ 05 мая 2020

Предположим, у меня есть несколько пультов для одного репозитория. Большую часть времени я использую одну git учетную запись для целей разработки, и когда я закончу, я переношу sh последнюю версию на другой пульт. Как теперь скрыть историю фиксации первого пульта от второго?

1 Ответ

2 голосов
/ 05 мая 2020

Я покажу вам, как сделать то, о чем вы просили, а затем расскажу, почему это плохая идея. : -)

История в любом Git репозитории - это просто набор коммитов в этом репозитории, как обнаружено набором имен в этом репозитории . Этот процесс поиска работает в обратном направлении, потому что Git всегда работает в обратном направлении. Мы увидим больше об этом чуть позже.

Фон

Помните, что каждая фиксация имеет уникальный идентификатор ha sh. По сути, это настоящее имя коммита. Чтобы просмотреть фиксацию с git log, вы должны каким-то образом указать Git на ha sh ID фиксации. Затем Git может получить эту фиксацию из базы данных репозитория, при условии, что она в первую очередь находится в базе данных.

Каждая фиксация имеет полный снимок всех ваших файлов - это основные данные в фиксации - плюс некоторые метаданные: информация о самом коммите, например, кто его сделал, когда (отметка даты и времени) и почему (сообщение журнала). Большая часть этих метаданных - это просто Git, чтобы показать вам git log. Но одна важная часть информации в метаданных, необходимая самому Git, тоже здесь. Каждый коммит имеет список необработанных ha sh ID его родительских коммитов. У большинства коммитов есть только одна запись в этом списке, так как у них есть единственный родитель.

Этот ha sh ID означает, что если мы каким-то образом найдем start commit ha sh H, мы можем получить сам коммит, показать его и использовать для поиска его родительского (более раннего) коммита. Назовем этот коммит G. Мы говорим, что фиксация H указывает на фиксацию G:

      ... G <-H

Но G также указывает на более раннюю фиксацию - назовем это F - вот так:

  ... F <-G <-H

и, конечно, F тоже указывает назад:

... <-F <-G <-H

Так что все, что нам действительно нужно, это способ сказать Git: последний ha sh ID - _____ (заполните пустое поле ha sh ID).

Это то, что такое имя ветки и что оно делает: оно предоставляет последний ha sh ID коммита. Имя ветки указывает на фиксацию, так же как каждая фиксация указывает на более раннюю фиксацию. Таким образом, нам не нужно запоминать большие уродливые идентификаторы ha sh, с которыми люди не могут справиться. Нам нужно только запомнить названия веток. имена помнят большого уродливого ха sh ID:

... <-F <-G <-H   <-- master

... когда я закончу [делаю новые коммиты] ...

Давайте посмотрим на процесс создания нового коммита. Давайте прямо сейчас создадим новое имя ветки, например feature. Имя ветки должно указывать на какой-то существующий коммит - это правила в Git: имя ветки указывает на некоторую фиксацию. Из серии ...--F--G--H очевидным является ... последний:

...--F--G--H   <-- feature (HEAD), master

Нам нужен способ запомнить, какое имя ветки мы используем, поэтому К новому имени feature я добавил специальное имя HEAD. Вот что мы получим, если сделаем:

git checkout -b feature master

Мы все еще работаем с фиксацией H, но теперь мы on branch feature, как скажет git status. Специальное имя HEAD теперь прикреплено к feature вместо master.

Когда мы делаем новую фиксацию, она получает новое, никогда не использовавшееся-где-еще-раньше, никогда- to-be-used -where-else-ever-again commit ha sh I. Новая фиксация I указывает на существующую фиксацию H:

...--F--G--H   <-- master
            \
             I   <-- feature (HEAD)

Повторите несколько раз, и вы получите следующее:

...--F--G--H   <-- master
            \
             I--J--K   <-- feature (HEAD)

В конце концов, вы закончили делать коммиты. Теперь вы можете git push на некоторый пульт, например origin. Это работает следующим образом: ваш Git вызывает другой Git - тот, который находится по URL-адресу, хранящемуся под удаленным именем, origin - и предлагает им некоторые фиксации по ha sh ID .

Они смотрят в свой репозиторий, чтобы узнать, есть ли у них этот ha sh ID. Если вы предложите им совершить K, они его не получат. Это заставляет ваш Git также предлагать им совершить J, потому что J является родительским элементом K, и это тоже является частью правил Git. У них этого не будет, поэтому ваш Git предложит I, а у них этого не будет, поэтому ваш Git предложит H. Здесь вполне могло быть H! Скажем так. Это позволяет вашему Git перестать предлагать идентификаторы ha sh.

Теперь ваш Git должен упаковать новые для них коммиты, I-J-K, и отправить их. Здесь вы увидите сообщения о подсчете и сжатии, а затем ваш Git отправит коммиты.

Теперь git push переходит в свою последнюю фазу: он отправляет им вежливый запрос: Если это Хорошо, установите имя вашей ветки ______ так, чтобы оно указывало на фиксацию K. Пока этот добавляет коммиты в одну из своих веток, без удаления каких-либо коммитов из этой ветки, они, вероятно, будут подчиняться этот запрос. Если это совершенно новое имя ветки, они с большей вероятностью подчинятся этому запросу.

Конечный результат таков: теперь у них есть имя их ветки, указывающее на последний коммит K в цепочке коммитов. Из K они найдут J, затем I, затем H и т. Д. Это история, которую они сейчас хранят в своем репозитории.

Что вы хотите

... как я могу скрыть свою историю коммитов первого удаленного от второй?

Не можете, или не совсем так. Однако вы можете сделать new commit (s), которые представляют собой другую историю , и отправить эти вместо .

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

git checkout -b other-guy master

Это даст вам в вашем Git репозитории эту серию имен и коммитов:

...--F--G--H   <-- master, other-guy (HEAD)
            \
             I--J--K   <-- feature

Ваш текущий коммит теперь коммит H. Ваше текущее имя ветки теперь other-guy.

Теперь вы можете сделать новый коммит - с совершенно новым, невиданным ранее ha sh ID, который мы назовем L с любым снимком, который вам нравится. Давайте пока не будем беспокоиться о как вы это сделаете и просто нарисуем результат:

             L   <-- other-guy (HEAD)
            /
...--F--G--H   <-- master
            \
             I--J--K   <-- feature

Теперь вы можете использовать:

git push other-remote other-guy:feature

Это ваш Git вызовите Git, хранящиеся под удаленным именем other-remote, и предложите им зафиксировать L. У них его не будет, поэтому ваш Git также предложит фиксацию H. У них может быть этот - он уже давно ходил - так что ваш Git, вероятно, может остановиться на этом, собрать L и отправить его.

Теперь ваш Git отправляет их Git вежливый запрос формы: Если все в порядке, укажите или создайте свое имя feature, указывающее на фиксацию L. Если они примут, что они имеют в их репозиторий :

...--H--L   <-- feature

(возможно, у них есть другое имя, например master, указывающее на H, мы просто не рисовали его здесь). Итак, их коммиты в их репозиторий находятся, начиная с их имени feature, которое идентифицирует фиксацию L. Они покажут фиксацию L. Затем они вернутся к родительскому элементу L H и покажут H и т. Д.

Обратите внимание, что они никогда не показывают I-J-K. Они не могут , потому что у них их нет . Они могут сейчас или в будущем, если они захотят и имеют доступ, получить их от вас и / или от любого другого Git, которому вы их отправили, или любого Git, у которого есть Git -sex с Git вы их отправили и тем самым забрали их, и так далее; но прямо сейчас они не заражены коммитами I-J-K.

(Gits в целом очень любят собирать новые коммиты. Gits в целом не любят отказываться от коммитов. Очень легко распространять коммиты вокруг, как инфекции.)

Простой способ сделать коммит L

Я обещал показать вам, как делать то, что вы хотите. Есть простой способ совершить фиксацию L после создания I-J-K, а именно использовать git merge --squash.

Учитывая это:

...--F--G--H   <-- master, other-guy (HEAD)
            \
             I--J--K   <-- feature

, вы можете запустить git merge --squash feature, а затем git commit. git merge --squash сообщает Git: Сделайте все, что вы сделали бы для настоящего слияния, но затем остановитесь без фиксации. Когда я делаю коммит, делаю его обычным повседневным коммитом с одним родителем, а не слиянием с двумя его родителями.

Git теперь объединяет разницу от коммита H до коммита H - никаких изменений - с отличием от H до K, и применяет все эти изменения к снимку в H, в результате чего снимок имеет K. Этот снимок еще не зафиксирован , но вы запускаете git commit, заполняете сообщение фиксации, как хотите, и теперь это:

             L   <-- other-guy (HEAD)
            /
...--F--G--H   <-- master
            \
             I--J--K   <-- feature

, и вы готовы к git push зафиксировать L и попросить кого-нибудь называть это feature.

Почему вам, вероятно, не стоит этого делать

Как только вы это сделаете один раз, ваш следующий запуск позиция в вашем собственном репозитории следующая:

             L   <-- other-guy (HEAD)
            /
...--F--G--H   <-- master
            \
             I--J--K   <-- feature

Один из ваших двух пультов имеет ту же настройку, за исключением того, что в нем полностью отсутствует фиксация L. Если вы хотите отправить туда фиксацию L, на этот раз вам нужно будет использовать другое имя, кроме feature: их имя feature запоминает фиксацию K. Вы можете сказать им принудительно отказаться от I-J-K в пользу L, но если вы это сделаете, вы уже отказались от выполнения того, о чем просили: теперь оба других пульта только могут найти фиксацию L (по крайней мере, через их имя feature).

Если вы хотите разработать больше вещей, теперь у вас есть проблема: вы начинаете с фиксации K или начинаете с фиксации L? Если вы начнете с L, ваша собственная история новой работы, которую вы делаете, не будет иметь истории I-J-K. В конце концов, история - это набор коммитов, найденных по какому-либо имени и работающих в обратном порядке .

Итак, в конечном итоге вы делаете одно из двух:

  • создайте много историй, которые вы игнорируете (ваши собственные feature ветки, которые вы отказываетесь, начиная следующую из фиксации L в этом случае), или
  • начинаете делать git merge --squash, когда вы сделали git merge --squash.

Давайте посмотрим, как последний работает:

git checkout feature

теперь дает:

             L   <-- other-guy
            /
...--F--G--H   <-- master
            \
             I--J--K   <-- feature (HEAD)

Мы делаем больше коммитов:

             L   <-- other-guy
            /
...--F--G--H   <-- master
            \
             I--J--K--M--N   <-- feature (HEAD)

Теперь мы go в squa sh -merge:

git checkout other-guy
git merge --squash feature

Это объединяет работу - например, сравнивает H vs L, чтобы найти "наш "изменения" и H против N, чтобы найти "свои" изменения и объединить эти изменения. Это часто работает нормально ... но если что-то, что мы делали в M-N , отменяет что-то в I-J-K или касается тех же строк , как что-то, что мы делали в I-J-K , мы получаем конфликт слияния .

Есть способы справиться с этим, и в итоге мы получаем:

             L--O   <-- other-guy
            /
...--F--G--H   <-- master
            \
             I--J--K--M--N   <-- feature (HEAD)

где O имеет сжатие результат объединения M и N. Теперь вы можете git push две разные истории в двух разных местах.

Это действительно может работать. Со временем это становится болезненным. Есть еще одна проблема, «похожая на заражение»: вы отправили коммиты I-J-K-M-N другому Git. Скорее всего, эти коммиты распространятся на большее количество Git клонов, а оттуда дойдут до клона, от которого вы пытались сохранить эти секреты. Даже если этого не происходит, очень легко обмануть себя, выполнив git push other-guy feature (хотя, к счастью, после первого раунда это обычно отклоняется как ошибка «не перемотка вперед»).

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

...