(Во-первых, примечание: я утверждаю, что стандартная практика не обязательно все это интересно, потому что ваш Git репозиторий ваш . Вы можете делать все, что работает для вы! Так что стандартная практика интересна только , если она работает для вас. Возможно, вам придется попробовать несколько разных подходов.)
TL; DR
Я рекомендую запустить:
git fetch upstream
git checkout desired-branch
git merge --ff-only upstream/theirbranch # or upstream/branch, or upstream/master
(вы можете получить и оформить заказ в любом порядке). Если вам действительно нравится git pull
, вы можете заставить git pull
запускать выборку и слияние, но я не люблю git pull
и предпочитаю делать это в два или более шагов. (У меня также есть псевдоним для merge --ff-only
, git mff
.)
Теперь вы можете удалить любое или все имена, которые вы сделали, чтобы сделать этот пиар, как на своем ноутбуке. Git хранилище и в вашей вилке GitHub. Эти имена почти не используют дисковое пространство, но они будут использовать «свободное пространство» (умственную энергию), чтобы отслеживать их, поэтому я рекомендую удалить их.
Это --ff-only
объединение сбой в некоторых случаях; в этих случаях, см. длинное обсуждение ниже.
Long
Помните эти вещи о Git:
Git все о совершает . Git не о файлах и даже не о ответвлениях , а о коммитах . Сохраняет данные хранилища - снимки файлов - и метаданные, например, кто их сделал и когда. Все коммиты доступны только для чтения: никакие части коммитов не могут быть изменены. Истинное имя коммита - это большой некрасивый идентификатор ha sh, и этот идентификатор ha sh чрезвычайно чувствителен к каждому биту данных и метаданных, хранящихся внутри коммита, так что изменить содержимое буквально невозможно коммита: если вы возьмете один, измените его немного и положите обратно, вы получите новый и другой коммит с новым и другим га sh ID.
Имена ветвей , такие как master
, develop
и т. Д., Полезны, поскольку они позволяют найти коммиты. Истинное имя каждого коммита - это большое уродливое удостоверение личности, которое никто не может вспомнить. Но у нас нет для запоминания большого уродливого идентификатора га sh, потому что у нас есть компьютер, чтобы запомнить его под именем!
Слово ответвление неоднозначно. Всякий раз, когда кто-то говорит о какой-то ветке Git, убедитесь, что вы знаете, имеют ли они в виду название ветки или что-то еще. Это только косвенно связано с вашей проблемой здесь, но стоит помнить всегда. См. Также Что именно мы подразумеваем под «ветвью»? В общем, очевидно, что слово branch означает имя ветви или некоторый набор коммитов, заканчивающийся на один , указанный именем ветви . Некоторые люди также используют его для обозначения имя удаленного слежения (я постараюсь не делать этого здесь).
Ваш репозиторий ваш, У вас есть свои собственные имена филиалов. Ваши имена ветвей не являются другими именами ветвей Git. На самом деле ваша Git и их Git доля являются коммитами по их идентификаторам ha sh. (Поскольку коммиты на 100% доступны только для чтения, если ваши Git и их Git могут буквально делиться коммитами, это нормально. Если нет, ваши Git и их Git могут иметь отдельные копии. Копии не может изменить, как мы уже отмечали.)
Кроме того, имена филиалов , Git имеют больше способов, то есть больше names - по которому он может запомнить идентификатор * * конкретного конкретного коммита. Одним из этих видов имен является имя для удаленного отслеживания , например origin/master
. Имя удаленного слежения - ваша Git память некоторых других Git ответвлений name (и идентификатор ha sh они хранится в названии этой ветки).
Эти два последних пункта являются ключом к решению вашей ситуации.
Я раздвоил репо ...
Это означает, что вы использовали некоторый хостинг-провайдер, такой как GitHub, для создания второго Git репозитория, основанного на первом Git репозитории. То есть на стороне GitHub вы сделали клон . Ваш clone-on-GitHub теперь не зависит от их clone-on-GitHub.
Вы, вероятно, также сделали клон на свой собственный компьютер (ноутбук или что-то еще), так что, вероятно, есть три клонов, существующих на данный момент. Хорошо! В Git мире, вы получаете клона, а они получают клона, а каждый получает клона ! Нет проблем с большим количеством клонов ... ну, за исключением одного: каждый клон имеет свои собственные имена ветвей . Это может быть много имен веток для управления.
В клоне, который, в частности, делает GitHub, есть нечто особенное, когда вы используете кликающую веб-кнопку "fork this репозиторий". На самом деле, есть несколько специфических вещей, но здесь важно следующее: этот клон копии все имена веток из репозитория, который вы разветвляете, к вашему клону GitHub. Ваш клон GitHub имеет только ветви имен, а не имен удаленного отслеживания .
Если вы впоследствии запустили:
git clone <github-url>
, чтобы скопировать ваш форк для нового клона на вашем ноутбуке этот третий клон не скопировал все "свои" имена ветвей. Но подождите: кто такие они здесь?
Мы уже говорили, что на GitHub есть два интересных клона. Значение они здесь зависит от того, какой URL вы использовали. Если вы использовали URL-адрес оригинального репозитория , то до того, как вы сделали форк, «они» - это оригинальный репозиторий. Если вы использовали URL своего форка, «они» - это ваш форк.
Если вы только что разветвили их хранилище, ваш fork имеет все те же имена веток (и сохраненные значения ha sh ID) и все те же коммиты (с их уникальными идентификаторами ha sh), что и их fork. Так что, в некотором смысле, не имеет значения, кого вы клонировали. Но со временем ваша вилка и ее вилка могут разойтись, так как вы и / или они добавляют больше коммитов в ваш и / или свой репозиторий. Если вы и они добавили разные коммиты или по-разному обновите свои и их имена ветвей , тогда это начнет иметь значение.
Обычно в этот момент вы создадите два из того, что Git называет remotes в клоне на вашем ноутбуке. remote - это просто короткое имя, подобное origin
, где у нас (ноутбук) Git будет храниться URL-адрес для какого-либо другого Git хранилища. Когда вы запустили git clone <url>
, ваш Git создал этот стандартный origin
пульт. Поскольку на GitHub есть два интересных репозитория - ваш форк и их форк - вы вполне можете захотеть добавить второй пульт, чтобы у вас был один пульт для каждой вилки. Стандартное имя для этого второго пульта - upstream
. (Это не особенно хорошее имя, потому что некоторые другие вещи в Git называются upstream в разное время, но это достаточно распространено, поэтому мы будем использовать его здесь.)
Имена удаленного отслеживания
Давайте вернемся к тому факту, что ваш клон на стороне ноутбука не скопировал имена ветвей любой вилки в имена ветвей вашего клона ноутбука, и Посмотрите также, почему кнопка "fork" на GitHub сделала копирование всех названий ветвей своих форков на ваш форк. Все это имеет отношение к именам удаленного отслеживания . 1 Ваш ноутбук Git создает имена удаленного отслеживания для каждого филиала имени, которое ваш ноутбук Git видит в пульте гиты. Эти удаленные Gits имеют имена на вашем ноутбуке: origin
и upstream
. Таким образом, ваш ноутбук Git может прикрепить эти имена перед именами branch и превратить GitHub Gits master
- почти наверняка их два - в origin/master
и upstream/master
. Он превращает GitHub Gits 'develop
в origin/develop
и upstream/develop
. Это повторяется для каждого имени ветви в каждого удаленного.
Стоимость сохранения всех этих дополнительных имен очень низкая: по сути это нет место на диске вообще. Это потому, что Git - это коммитов , а коммиты имеют идентификаторы ha sh. Предположим, что origin/master
говорит commit a1234567...
, а upstream/master
говорит commit a1234567...
. Ваш собственный Git уже имеет коммит a1234567...
, поэтому все ваши Git должны хранить только несколько пар имя-значение: origin/master=a1234567...
, upstream/master=a1234567...
.
Хороший Что касается имен удаленного слежения, то это:
Они практически не занимают места вообще. (Git обычно хранит их в .git/packed-refs
, то есть в одном файле с записями, а не в нескольких файлах, поэтому они, как правило, занимают даже меньше, чем блок диска. Ваши собственные имена ветвей уже дешевы в плане хранения , поскольку большинство из них хранятся в одном дисковом блоке, но они даже дешевле.)
Они автоматически обновляют . Когда вы запускаете git fetch origin
, ваш Git вызывает Git в origin
(ваш ответвление на GitHub). Ваш Git получает от своих Git все новые необходимые коммиты и другие необходимые объекты, а затем обновляет все ваши (1260 *) имена удаленного отслеживания (для ноутбука), чтобы они соответствовали всем вашим (GitHub-fork) именам веток. Когда вы запускаете git fetch upstream
, ваш Git вызывает Git на upstream
(их ответвление на GitHub). Ваш Git получает от своих Git любые новые коммиты и другие необходимые объекты, а затем обновляет все ваши upstream/*
имена для удаленного слежения, чтобы они соответствовали всем именам их ветвей.
Возможно, вы захотите добавить --prune
к вашим git fetch
командам или установить fetch.prune
на true
в вашей конфигурации Git, чтобы ваш Git удалял из имен удаленного отслеживания любые имена веток, которые "их" Git (ваш или их ответвление на GitHub) больше не имеют. Без --prune
обновление на шаге 2 выше никогда не замечает, что они, кем бы они ни были, удалили feature/tall
, поэтому ваш origin/feature/tall
или upstream/feature/tall
- где бы он ни был - висит как устаревшее имя удаленного слежения. С --prune
или fetch.prune
ваш ноутбук Git замечает, что это имя должно go удалить, и удаляет его.
Итак: почему кнопка GitHub "fork a repository" не создала удаленное имена вместо имен веток? Ну, только GitHub действительно может ответить на это; но если бы они были, вам нужен какой-то способ манипулирования именами удаленного отслеживания на GitHub. Так как они этого не сделали, они только должны предоставить вам возможность манипулировать именами веток на GitHub. Обратите внимание, что на GitHub нет кнопки для fetch: вы не можете заставить работать свою вилку GitHub git fetch
! Поскольку git fetch
используется на ноутбуке для обновления имен удаленного отслеживания, отсутствие fetch
на GitHub означает, что у вас нет возможности обновить там имена удаленного отслеживания.
1 Исторически, имена для удаленного слежения фактически следовали за различными решениями, которые привели ко всему этому, но я думаю, что имеет больше смысла следовать логике c другим путем.
Передача коммитов: git fetch
и git push
Существует два распространенных способа получения коммитов в Git хранилище. Мы уже упоминали один из них выше, а именно git fetch
. Вы запускаете git fetch <em>remote</em>
, и ваш Git находит сохраненный URL-адрес с удаленного имени - например, URL для origin
- и вызывает Git в этом месте.
То Git перечисляет для вашего Git все имена его ветвей (и имена тегов, и другие внутренние имена, но здесь мы в действительности только смотрим на имена ветвей). Каждое имя ветви идентифицирует один коммит, который является tip ветви. Все ранее коммиты, которые достижимы в этой ветви, доступны с использованием этого имени ветви. Подробное обсуждение концепции достижимости см. Думайте как (а) Git. Понимание достижимости является ключом к использованию Git, поэтому вы должны определенно поработать над этим, если понятия незнакомы.
На этом этапе ваш Git может запросить у Git любые коммиты и другие внутренние Git объекты, которые ваш Git хочет или нуждается, но не имеет. Этот шаг на самом деле довольно интересный и входит во многие теории графов, но мы можем просто принять как должное, что два Gits делают это довольно хорошо. Они находят разумно минимальный набор Git объектов, которые есть у их Git, которые нужны вашему Git. Они сжимают эти объекты - вот о чем здесь все сообщения counting objects
и compressing objects
- и отправляют их. Ваш Git помещает их в вашу коллекцию, добавляя коммиты и другие внутренние объекты в ваш репозиторий на вашем ноутбуке. Это позволяет вашему Git обновить ваши имена для удаленного слежения: теперь у вас есть все коммиты, которые у них есть, плюс любые ваши коммиты, которые вы не дали им.
Примечание что ваши имена для удаленного отслеживания, по сути, предварительно зарезервированы для их Git. Вы не называете ни одну из своих собственных ветвей origin/master
или origin/develop
или тому подобное. 2 Так что Git может свободно sma sh и заменить любое или все ваши имена для удаленного слежения: ни одно из ваших имен ветвей не затронуто.
Если вы хотите go другим способом, противоположностью fetch будет pu sh. 3 Но есть асимметрия здесь. Когда вы запускаете git push origin <em>branch</em>
, ваш Git вызывает другой Git, снова просматривая URL-адрес пульта. Но на этот раз вместо того, чтобы они перечисляли имена своих ветвей и тому подобное и передавали коммиты в ваш Git, ваш Git отправляет их коммиты и другие внутренние Git объекты , Вы отправляете им любые коммиты, которые им нужны, чтобы сделать коммит-коммит вашей собственной ветки branch
полезным - это включает в себя любые достижимые коммиты, которые у вас есть, а они нет - и еще раз мы получаем все подсчитывающие и сжимающие объекты сообщения. Но теперь, отправив все необходимые коммиты на их Git, ваш Git просит - обычно вежливо - чтобы они установили свое имя ветви branch
ha sh Идентификатор коммита, который также является кончиком вашей ветви branch
.
Они не задают имя для удаленного отслеживания! (В частности, GitHub даже не имеет имен для удаленного слежения.) Они не задают другое имя зарезервированного пространства. Они устанавливают свое имя branch .
Когда ваш Git делает вежливый запрос, они отклонят запрос , если он им не понравится. Если вы создаете новое имя ветки, им обычно это нравится. Однако если вы обновляете существующее, им не понравится обновление, если новый га sh ID относится к коммиту, из которого предыдущий ха sh Идентификатор того же имени недоступен.
То есть рассмотрим цепочку коммитов:
...--G--H <-- branch
Теперь мы добавим несколько новых коммитов в конец:
...--G--H <-- branch
\
I--J
и предлагаем переместить свое имя branch
с H
на J
. Если они это сделают, коммит H
останется достижимым: начиная с J
и работая в обратном направлении, мы go с J
до I
и затем до H
. Так что этот запрос будет принят. Но если мы сделаем это вместо этого:
...--G--H <-- branch
\
K--L
и попросим их указать свое имя branch
, чтобы указать L
, они откажутся, потому что H
не может быть достигнуто с L
. Достижимыми коммитами от L
являются L
, затем K
, затем G
, затем другие коммиты до G
.
Git. Это означает, что изменение имя branch
должно быть fast-forward . Перемещение branch
с H
на J
- ускоренная перемотка вперед; перемещение branch
из H
в L
не ускоренное. 4
2 Технически, вы можете . Git имеет внутренние пространства имен, так что Git может держать все как есть. Это не очень хорошая идея: у вас, вероятно, нет этих внутренних пространств имен, и вы испортите это. 14
3 Это не pu sh и тяга , это pu sh и выборка! Это историческая случайность и я думаю, что это приводит к большой путанице, но это то, что есть.
4 Для принудительного обновления sh без ускоренной перемотки вперед, вы можете использовать --force
или добавьте флаг +
к refspe c, чего мы здесь не определили. Оба они превращают вежливый запрос в команду. Они могут по-прежнему отказываться, но мы не будем беспокоиться об этих деталях здесь.
Запросы на получение
Сами запросы на получение (PR) сами являются хост-провайдером -specifi c особенность. Git не имеет запросов на извлечение! (Git имеет команду git request-pull
, но она генерирует сообщение электронной почты.) Обратите внимание, что если у нас есть форк GitHub, мы можем git push
на него. Все в порядке: мы можем обновить нашу форк. Наши операции git push
будут успешными, если они ускорены, и в особых случаях мы можем git push --force
добиться успеха, даже если они не ускорены. Таким образом, мы можем git push
все, что мы хотим, нашей вилке GitHub, которую мы называем origin
. Это позволяет нам изменять форму нашей вилки GitHub, как нам нравится. Наша вилка будет хранить коммитов , как и любой Git репозиторий. Он будет хранить их под именами ветвей , как и любой Git репозиторий. У него нет имен для удаленного отслеживания - они указаны c для нашего ноутбука Git, но это нормально: нам не нужен наш форк для имен удаленного отслеживания.
Но мы могли бы хотите получить наши коммиты в GitHub, который не наш, по адресу, который мы храним под именем upstream
. Как мы это сделаем?
Если - это большое значение, если это вообще не так - владельцы этого другого форка GitHub должны были дать нам разрешение на запись в свой репозиторий, мы могли бы просто git push
наш коммит напрямую upstream
. Но они действительно должны доверять нам со своим репозиторием.
GitHub может предложить какое-то специальное пространство имен: полу-защищенные шаблоны имен веток, которые владельцы upstream
fork могли бы дать нам писать о том, что они сами не будут использовать. GitHub может иметь механизм принудительного исполнения, чтобы все это работало. Но они этого не делают. Вместо этого GitHub отправляет нам запросов на получение .
Прежде чем мы сделаем PR, мы начнем с git push
- наши сделанные на ноутбуке коммиты на нашу собственную вилку GitHub на origin
. Они фиксируют go по своим идентификаторам ha sh, обновляя имена веток в нашей ветке GitHub в соответствии с нашими git push
командами. В конце концов, у нас в ветке GitHub есть имя ветки, которое указывает на какой-нибудь коммит-коммит, который нам нравится, который мы хотим предложить людям, которые работают с вилкой GitHub, которую мы называем upstream
.
Именно на этом указать, что мы делаем запрос на извлечение. Мы используем интерфейс GitHub для отправки коммитов с нашего форка GitHub на их форк GitHub (как если бы это было git push
), но они отображаются в форке на upstream
под особыми именами, которыми управляют люди GitHub. 5 / sup > У нас нет агентства в этом процессе, кроме нажатия кнопки «сделать пиар»: GitHub выбирает специальное имя и создает имя для пиара. Затем GitHub также отправляет электронную почту, слабые сообщения и т. Д. c. - что бы это ни было уместно - чтобы предупредить людей, которые запускают разветвление, которое мы называем upstream
, о том, что для них есть новый запрос на извлечение.
Все в настоящее время до их .
5 Это пространство имен refs/pull/*
. Вещи в этом пространстве имен нумеруются: каждый PR или выпуск получает уникальный счетный номер в репозитории GitHub, а когда мы создаем новый PR, GitHub присваивает ему номер - скажем, 123
для конкретности - и создает имена в форме refs/pull/123/head
и может быть, а может и нет refs/pull/123/merge
. Имя merge
создается тогда и только тогда, когда стороннее программное обеспечение GitHub решает, что наш PR может быть объединен; в этом случае ссылка merge
указывает на коммит слияния, который GitHub Git уже сделал. Ссылка head
относится конкретно к коммиту в кончике ветки, который мы выбрали, когда нажимали кнопку «сделать запрос на извлечение».
Если мы добавим sh новых коммитов в наш PR, head
ref обновляется, а merge
ref уничтожается и создается новый, если это возможно, с использованием тех же правил, что и всегда.
После PR
При этом У каждого, кто контролирует форк GitHub, который мы называем upstream
, есть разные варианты. У них есть запрос на получение, который имеет номер. Они могут проверить запрос на извлечение. Они могут принести его в репозиторий Git на своем ноутбуке, используя git fetch
и специальные имена, созданные GitHub для PR (см. Сноску 5). Или они могут просто использовать различные щелкающие кнопки в веб-интерфейсе.
Если они действительно используют эти щелкающие кнопки, GitHub, в частности, предлагает три кнопки, которые GitHub помечают следующим образом:
-
Объединить. Это делает прямое git merge
, так же, как это делает Git. 6 Все ваши коммиты с их идентификаторами ha sh теперь доступны из любой ветви, в которую они слились. В их ветви существует одна новая фиксация слияния; этот новый коммит слияния еще не существует в вашем Git хранилище. 7
Squa sh и слияние. В действительности это запускает git merge --squash
, хотя не совсем так, как Git, потому что в командной строке Git, git merge --squash
фактически ничего не фиксирует. в этом случае они делают один новый коммит в своей ветке, который объединяет вашу работу, но они не принимают ни одного из ваших коммитов.
- * 1562 Перебазировать и объединить. Это в действительности запускает
git rebase --no-ff
, копирование всех ваших коммитов на новые с новыми и разными идентификаторами га sh.
Это, наконец, приносит нам на ваш вопрос:
как вытащить слияние из апстрима, не получая остальные ветки из апстрима, которые мне не нужны / не нужны
Ответ на это зависит от того, что вы хотите в каждом из ваших двух репозиториев: ваш GitHub и ваш репозиторий для ноутбука.
Если они осуществили реальное слияние , вы можете сделать:
git fetch upstream
git checkout desired-branch
git merge --ff-only upstream/theirbranch
потому что ваши коммиты из вашей ветки с именем branch
теперь находятся в их ветке. Все, что вам нужно сделать, это добавить этот окончательный коммит слияния. Вам больше не нужны дополнительные имена, чтобы помнить подсказки веток, которые вы использовали для создания и отправки запроса на получение, поэтому не стесняйтесь их удалять.
Если они выполнили squa sh и объединили или перебазировать и объединить , это --ff-only
будет терпеть неудачу . Теперь все зависит от вас: хотите ли вы отказаться от своих первоначальных коммитов в пользу каких-либо коммитов , которые они вложили в своих upstream/theirbranch
? Независимо от того, делаете вы это или нет, теперь у вас есть все инструменты Git, доступные вам. Их коммиты находятся на upstream/theirbranch
: вы можете просмотреть их с помощью git log
. Ваши коммиты доступны через названия ваших веток. Вы можете использовать git branch -f
или git reset --hard
, чтобы отменить некоторые или все ваши коммиты. Вы можете переименовать ваши ветви, чтобы сохранить ваши старые коммиты, в то время как вы убедитесь, что они работают. Вы можете делать все что угодно! В конце концов, ваш репозиторий ваш .
6 На самом деле, поскольку GitHub уже сделал это - он находится в refs/pull/<em>number</em>/merge
- на самом деле они не должен сделать что-нибудь здесь. Если при слиянии возникли конфликты, эта ссылка не существует, и опция «слияние» отключена.
7 Поскольку GitHub использует предварительно сделанное слияние, технически вы могли бы захватить что слить и иметь его в своем хранилище. Я не знаю наверняка, использует ли GitHub существующее слияние - они могли бы, но не обязаны - но это можно было бы выяснить экспериментально. Обратите внимание, однако, что GitHub может в любой момент изменить свою работу, поэтому, вероятно, неразумно рассчитывать на это так или иначе.