Получить конкретный коммит из удаленного Git-репозитория


189

Есть ли способ получить только один конкретный коммит из удаленного репозитория Git, не клонируя его на моем ПК? Структура удаленного репо абсолютно такая же, как у меня, и поэтому никаких конфликтов не будет, но я понятия не имею, как это сделать, и я не хочу клонировать этот огромный репозиторий.

Я новичок в мерзавце, есть ли способ?


1
Ваш существующий репозиторий уже является клоном удаленного или он совершенно другой?
CharlesB

Ну, репозиторий - это источник ядра Linux, и он почти такой же
Varun Chitre

так это клон или нет?
CharlesB

1
Не совсем. Подумайте об этом: пусть удаленный репозиторий находится во главе D, а мой - в голове А и отстает от коммитов B, C, D. Я хочу объединить коммит B из одного репо и C из другого и D из другого, поскольку коммиты B, C, D в этих репо отличаются от их собственных особенностей
Varun Chitre

1
@VarunChitre вы можете принять другой ответ от VonC?
CharlesB

Ответы:


109

Начиная с Git версии 2.5+ (второй квартал 2015 года), получение одного коммита (без клонирования полного репо) действительно возможно.

См. Коммит 68ee628 от Fredrik Medley ( moroten) , 21 мая 2015 г.
(объединено с Junio ​​C Hamano gitster- в коммите a9d3493 , 01 июня 2015 г.)

Теперь у вас есть новый конфиг (на стороне сервера)

uploadpack.allowReachableSHA1InWant

Разрешить upload-packпринимать запрос на выборку, запрашивающий объект, доступный из любой ссылки ref. Тем не менее, обратите внимание, что вычисление достижимости объекта является вычислительно дорогим.
Значения по умолчанию для false.

Если вы объедините эту конфигурацию на стороне сервера с поверхностным clone ( git fetch --depth=1), вы можете запросить один коммит (см . t/t5516-fetch-push.sh:

git fetch --depth=1 ../testrepo/.git $SHA1

Вы можете использовать git cat-fileкоманду, чтобы увидеть, что фиксация была получена:

git cat-file commit $SHA1

« git upload-pack« обслуживает » git fetch» может быть сказано, чтобы обслуживать коммиты, которые не находятся на кончике какого-либо ссылки, если они достижимы из ссылки с uploadpack.allowReachableSHA1InWant помощью переменной конфигурации.


Полная документация:

upload-pack: опционально разрешить выборку доступного sha1

С uploadpack.allowReachableSHA1InWantпараметром конфигурации, установленным на стороне сервера, « git fetch» может сделать запрос со строкой «хотеть», которая называет объект, который не был объявлен (вероятно, был получен вне диапазона или из указателя подмодуля). Будут обрабатываться
только объекты, доступные из подсказок ветвей, т.е. объединение объявленных веток и скрытых веток transfer.hideRefs.
Обратите внимание, что это связано с необходимостью обходить историю, чтобы проверить достижимость.

Эта функция может использоваться при получении содержимого определенного коммита, для которого известен sha1, без необходимости клонирования всего репозитория, особенно если используется мелкая выборка .

Полезные случаи, например,

  • репозитории, содержащие большие файлы в истории,
  • получение только необходимых данных для проверки субмодуля,
  • при совместном использовании sha1, не сообщая, к какой именно ветви он принадлежит, и в Gerrit, если вы думаете не в виде чисел, а в терминах коммитов.
    (Случай с Gerrit уже раскрыт, так allowTipSHA1InWantкак каждое изменение Gerrit имеет ссылку.)

Git 2.6 (3 квартал 2015 года) улучшит эту модель.
Смотрите коммит 2bc31d1 , коммит cc118a6 (28 июля 2015 г.) Джеффа Кинга ( peff) .
(Слиты Junio C Hamano - gitster- в фиксации 824a0be , 19 Aug 2015)

refs: поддержка отрицательная transfer.hideRefs

Если вы скрываете иерархию ссылок, используя transfer.hideRefsконфигурацию, то позже невозможно переопределить эту конфигурацию, чтобы «показать» ее.
Этот патч реализует «отрицательное» скрытие, которое заставляет совпадения немедленно помечаться как скрытые, даже если другое совпадение скрыло бы это.
Мы позаботимся о том, чтобы применить совпадения в обратном порядке от того, как они передаются нам механизмом конфигурации, поскольку это позволяет нашему обычному приоритету конфигурации «последний выиграл» (и записи в .git/config, например, будут переопределять /etc/gitconfig).

Итак, теперь вы можете сделать:

git config --system transfer.hideRefs refs/secret
git config transfer.hideRefs '!refs/secret/not-so-secret'

скрыть refs/secretво всех репо, кроме одного публичного бита в одном конкретном репо.


Git 2.7 (ноябрь / декабрь 2015) снова улучшится:

См. Коммит 948bfa2 , коммит 00b293e (05 ноября 2015 г.), коммит 78a766a , коммит 92cab49 , коммит 92cab49 , коммит 92cab49 (03 ноя 2015 г.), коммит 00b293e , коммит 00b293e (05 ноября 2015 г.) и коммит 92cab49 , коммит 92cab49 , коммит 92cab49 , совершить 92cab49 (03 ноября 2015 г.) Лукасом Флейшером ( lfos) .
Помогает: Эрик Саншайн ( sunshineco) .
(Слиты Джефф Кинг - peff- в фиксации dbba85e , 20 ноября 2015)

config.txt: документировать семантику hideRefsс пространствами имен

В настоящее время нет четкого определения того, как transfer.hideRefsвести себя, когда задано пространство имен.
Объясните, что hideRefsв этом случае префиксы совпадают с разделенными именами. Вот как hideRefsшаблоны в настоящее время обрабатываются в receive-pack.

hideRefs: добавить поддержку для сопоставления полных ссылок

В дополнение к сопоставлению разделенных ссылок, теперь можно добавить hideRefsшаблоны, с которыми сопоставляется полный (необрезанный) элемент ref.
Чтобы различать пропущенные и полные совпадения, эти новые шаблоны должны иметь префикс при помощи круговой рамки ( ^).

Отсюда новая документация :

transfer.hideRefs:

Если пространство имен используется, префикс пространства имен удаляется из каждой ссылки перед сопоставлением с transfer.hiderefsшаблонами.
Например, если refs/heads/masterуказано в transfer.hideRefsи текущее пространство имен foo, а затем refs/namespaces/foo/refs/heads/master опускается от рекламы , но refs/heads/masterи refs/namespaces/bar/refs/heads/masterпо - прежнему рекламировали в качестве так называемых «иметь» линии.
Чтобы сопоставить ссылки перед удалением, добавьте ^перед именем ссылки. Если вы объединяете !и ^, !должны быть указаны в первую очередь.


R .. упоминает в комментариях конфиг uploadpack.allowAnySHA1InWant, который позволяет upload-packпринять fetchзапрос, который запрашивает любой объект вообще. (По умолчанию false).

Смотрите коммит f8edeaa (ноябрь 2016, Git v2.11.1) Дэвида "novalis" Тернера ( novalis) :

upload-pack: опционально разрешить выборку любого ша1

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

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


4
Можете ли вы привести более полный пример того, как создать клон репо с помощью этого единственного коммита? Я пытался, но не смог .. Спасибо!
Ларс Билке

1
Я хочу подтолкнуть к GitHub. Может быть, они этого не позволяют.
Ларс Билке

2
@LarsBilke, мы говорим о клоне или тянуть здесь, а не толкать. И я уверен, что GitHub еще не имеет Git 2.5 на стороне сервера.
VonC

2
Теперь, что еще лучше, uploadpack.allowAnySHA1InWantбез штрафа за вычисление достижимости (и вектор DoS).
R .. GitHub ОСТАНОВИТЬ ЛЬДА

1
Спасибо! Я нахожу забавным, что они описывают это как «доверять пользователю доступ», а не «доверяют авторам репо, чтобы они не выдавали случайную чушь, которую они не собираются публиковать».
R .. GitHub ОСТАНОВИТЬ ЛЬДА

98

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

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

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


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

git fetch origin refs/heads/branch:refs/remotes/origin/branch

Это позволит загрузить только коммиты, содержащиеся в удаленной ветке branch (и только те, которые вы пропустили) , и сохранить их в origin/branch. Затем вы можете объединить или оформить заказ.

Вы также можете указать только коммит SHA1:

git fetch origin 96de5297df870:refs/remotes/origin/foo-commit

Это загрузит только фиксацию указанного SHA-1 96de5297df870 (и его предков, которые вы пропустили) и сохранит ее как (несуществующую) удаленную ветку origin/foo-commit.


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

1
Он все еще загружает много данных (430 МБ) с помощью git fetch. Требуемый коммит всего несколько килобайт. Специальной команды для этого нет? А что если я захочу удалить репозиторий 'git fetched'? где это хранится?
Варун Читре

9
Это довольно устарело сейчас. У нас есть возможность выполнять как мелкий клон , так и получить один коммит . Мелким клонам теперь разрешено толкать и извлекать как обычно, без необходимости знать всю историю проекта, поэтому уже не правильно говорить, что коммит не может существовать один без своих предков. То, что вы говорите о получении после первоначального клона, очень верно, но у нас также есть даже более дешевые варианты.
Теодор Мердок

6
Последняя команда (использующая коммит SHA1) не работает для меня. Команда какое-то время молча делает «что-то», а затем завершается без сообщения или явного побочного эффекта.
HRJ

1
@ HRJ Да, я тоже сталкивался с этим в Ubuntu 16.04 с Git 2.7.4-0ubuntu1.3. Однако при использовании 2.16.2-0ppa1~ubuntu16.04.1из PPA git-core это работает как надо. Похоже, ошибка, которая была исправлена. Не удалось найти ссылку на это с помощью быстрого поиска. Если кто-то может подсказать мне это, я бы хотел, чтобы это исправление было перенесено.
gertvdijk

62

Я сделал натяжение моего репозитория git:

git pull --rebase <repo> <branch>

Позволив git вытащить весь код для ветки, а затем я сделал сброс к коммиту, который меня заинтересовал.

git reset --hard <commit-hash>

Надеюсь это поможет.


1
Ни один из ответов не сработал, но этот спас мне жизнь! Огромное спасибо!
michaeltintiuc

Сброс - у меня сработало после клонирования! Спасибо.
Ник-ACNB

3
-1: «деструктивные» команды типа git reset --hard , когда они используются совместно в обобщенных решениях, могут привести людей в ловушки, где они теряют данные (или, в данном случае: в состоянии, когда получение их данных нетривиально).
yaauie

54

Вы можете просто получить один коммит удаленного репо с

git fetch <repo> <commit>

где,

  • <repo>может быть именем удаленного репо (например origin) или даже URL удаленного репо (например https://git.foo.com/myrepo.git)
  • <commit> может быть SHA1 коммит

например

git fetch https://git.foo.com/myrepo.git 0a071603d87e0b89738599c160583a19a6d95545

После того, как вы получили коммит (и пропавших предков), вы можете просто оформить его с помощью

git checkout FETCH_HEAD

Обратите внимание, что это приведет вас в состояние «оторванная голова».


10
Когда я пытаюсь выполнить fetchконкретную версию, как вы делаете, git завершается с ошибкой с кодом ошибки 1 и без вывода. Это было то, что раньше работало в прошлых версиях? (Я v2.0.2.)
Джек О'Коннор

2
Изменить: Это работает, если у меня уже есть тот, который фиксирует локально, например, если я уже сделал полный fetch, хотя в этом случае я не уверен, какой смысл использовать.
Джек О'Коннор

2
Действительно, мне кажется, что с git 2.0.2 это больше не работает. :(
Поток

2
git checkout FETCH_HEADпомогает.
lzl124631x

1
Этот метод не будет работать с мелкой выборкой (например --depth=1)!
создание короля

16

Вы можете просто получить удаленное репо с:

git fetch <repo>

где,

  • <repo>может быть именем удаленного репо (например origin) или даже URL удаленного репо (например https://git.foo.com/myrepo.git)

например:

git fetch https://git.foo.com/myrepo.git 

После того, как вы получили репо, вы можете объединить нужные коммиты (поскольку вопрос заключается в получении одного коммита, вместо слияния вы можете использовать cherry-pick, чтобы выбрать только один коммит):

git merge <commit>
  • <commit> может быть SHA1 коммит

например:

git cherry-pick 0a071603d87e0b89738599c160583a19a6d95545

или

git merge 0a071603d87e0b89738599c160583a19a6d95545

Если это последний коммит, который вы хотите объединить, вы также можете использовать переменную FETCH_HEAD:

git cherry-pick (or merge) FETCH_HEAD

Это требует настройки учетной записи Git на компьютере. Он не работает под тестовым аккаунтом. У вас есть что-то, что работает под тестовой учетной записью?
jww

что ты имеешь в виду ? ты не можешь сделать git fetch?
Сержио

Ммм так команда будет git config set uploadpack.allowReachableSHA1InWant ?
Александр Миллс

2

Это работает лучше всего:

git fetch origin specific_commit
git checkout -b temp FETCH_HEAD

назовите "temp" как хотите ... эта ветка может быть осиротевшей


Ясно, что НЕ со старыми версиями git, такими как 1.8.x
Сорин

1

Наконец я нашел способ клонировать конкретный коммит с помощью git cherry-pick . Предполагая, что у вас нет локального хранилища и вы извлекаете конкретный коммит из удаленного,

1) создать пустой репозиторий в локальном и git init

2) git remote add origin " url-of-repository" "

3) происхождение git fetch [это не переместит ваши файлы в ваше локальное рабочее пространство, если вы не объединитесь]

4) git cherry-pick " Enter-long-commit-hash-that-you-need" "

Готово. Таким образом, у вас будут только файлы из этого конкретного коммита в вашем локальном компьютере.

Введите длиной фиксации-хэш:

Вы можете получить это используя -> git log --pretty = oneline



0

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

# Add the remote repo path, let's call it 'upstream':
git remote add upstream https://github.com/repo/project.git

# checkout the pull ID, for example ID '60':
git fetch upstream pull/60/head && git checkout FETCH_HEAD
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.