Recuperar confirmación específica de un repositorio Git remoto

189

¿Hay alguna manera de recuperar solo una confirmación específica de un repositorio de Git remoto sin clonarlo en mi PC? La estructura del repositorio remoto es absolutamente la misma que la mía y, por lo tanto, no habrá conflictos, pero no tengo idea de cómo hacer esto y no quiero clonar ese enorme repositorio.

Soy nuevo en git, ¿hay alguna manera?

Varun Chitre
fuente
1
¿Su repositorio actual ya es un clon del remoto, o es completamente diferente?
CharlesB
Bueno, el repositorio es la fuente del kernel de Linux, y es más o menos lo mismo
Varun Chitre
Entonces, ¿es un clon o no?
CharlesB
1
No exactamente. Considere esto, deje que el repositorio remoto esté en la cabeza D y el mío esté en la cabeza A y B, C, D confirme detrás. Deseo fusionar commit B de un repositorio y C de otro y D de otro uno, ya que los commits B, C, D en estos repos son diferentes con sus propias especialidades
Varun Chitre
1
@VarunChitre, ¿puedes aceptar la otra respuesta de VonC?
CharlesB

Respuestas:

109

A partir de Git versión 2.5+ (Q2 2015), es posible obtener una sola confirmación (sin clonar el repositorio completo).

Ver commit 68ee628 por Fredrik Medley ( moroten) , 21 de mayo de 2015.
(Fusionada por Junio ​​C Hamano - gitster- en commit a9d3493 , 01 jun 2015)

Ahora tiene una nueva configuración (en el lado del servidor)

uploadpack.allowReachableSHA1InWant

Permita upload-packaceptar una solicitud de búsqueda que solicite un objeto al que se pueda acceder desde cualquier sugerencia de referencia. Sin embargo, tenga en cuenta que calcular el alcance de los objetos es computacionalmente costoso.
Por defecto es false.

Si combina esa configuración del lado del servidor con un clon superficial ( git fetch --depth=1), puede solicitar una sola confirmación (consulte t/t5516-fetch-push.sh:

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

Puede usar el git cat-filecomando para ver que se ha obtenido la confirmación:

git cat-file commit $SHA1

Se le puede decir a " git upload-pack" que sirve " git fetch" que sirva confirmaciones que no están en la punta de ninguna referencia, siempre que sean accesibles desde una referencia, con una uploadpack.allowReachableSHA1InWant configuración variable.


La documentación completa es:

upload-pack: opcionalmente permite buscar sha1 accesible

Con la uploadpack.allowReachableSHA1InWantopción de configuración establecida en el lado del servidor, " git fetch" puede realizar una solicitud con una línea "querer" que nombra un objeto que no se ha anunciado (probablemente se haya obtenido fuera de banda o desde un puntero de submódulo).
Solo transfer.hideRefsse procesarán los objetos accesibles desde las puntas de las ramas, es decir, la unión de las ramas anunciadas y las ramas ocultas por .
Tenga en cuenta que hay un costo asociado de tener que retroceder en el historial para verificar la accesibilidad.

Esta característica se puede usar cuando se obtiene el contenido de un determinado commit, para el cual se conoce sha1, sin la necesidad de clonar todo el repositorio, especialmente si se usa una búsqueda superficial .

Los casos útiles son, por ejemplo,

  • repositorios que contienen archivos grandes en el historial,
  • obtener solo los datos necesarios para un pago de submódulo,
  • cuando comparte un sha1 sin decir a qué rama exacta pertenece y en Gerrit, si piensa en términos de commits en lugar de números de cambio.
    (El caso de Gerrit ya se ha resuelto allowTipSHA1InWantya que cada cambio de Gerrit tiene una referencia).

Git 2.6 (Q3 2015) mejorará ese modelo.
Ver commit 2bc31d1 , commit cc118a6 (28 de julio de 2015) por Jeff King ( peff) .
(Fusionada por Junio ​​C Hamano - gitster- en commit 824a0be , 19 de agosto de 2015)

refs: soporte negativo transfer.hideRefs

Si oculta una jerarquía de referencias usando la transfer.hideRefsconfiguración, no hay forma de anular esa configuración para "mostrarla".
Este parche implementa un ocultamiento "negativo" que hace que las coincidencias se marquen inmediatamente como ocultas, incluso si otra coincidencia lo oculta.
Nos encargamos de aplicar las coincidencias en orden inverso a la forma en que nos las alimenta la maquinaria de configuración, ya que eso permite que nuestro trabajo habitual de precedencia de configuración "el último gana" (y las entradas .git/config, por ejemplo, se anularán /etc/gitconfig).

Entonces ahora puedes hacer:

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

para esconderse refs/secreten todos los repositorios, excepto un bit público en un repositorio específico.


Git 2.7 (nov / dic 2015) mejorará nuevamente:

Consulte commit 948bfa2 , commit 00b293e (05 de noviembre de 2015), commit 78a766a , commit 92cab49 , commit 92cab49 , commit 92cab49 (03 de noviembre de 2015), commit 00b293e , commit 00b293e (05 de noviembre de 2015) y commit 92cab49 , commit 92cab49 , commit 92cab49 , cometer 92cab49 (03 de noviembre de 2015) por Lukas Fleischer ( lfos) .
Ayudado por: Eric Sunshine ( sunshineco) .
(Fusionada por Jeff King - peff- en commit dbba85e , 20 de noviembre de 2015)

config.txt: documentar la semántica de hideRefscon espacios de nombres

En este momento, no hay una definición clara de cómo transfer.hideRefsdebería comportarse cuando se establece un espacio de nombres.
Explique que los hideRefsprefijos coinciden con nombres despojados en ese caso. Así es como hideRefsse manejan actualmente los patrones en el paquete de recepción.

hideRefs: agrega soporte para emparejar referencias completas

Además de hacer coincidir las referencias despojadas, ahora se pueden agregar hideRefspatrones con los que se compara la referencia completa (sin tirar).
Para distinguir entre coincidencias despojadas y completas, esos nuevos patrones deben tener el prefijo circunflejo ( ^).

De ahí la nueva documentación :

transfer.hideRefs:

Si se utiliza un espacio de nombres, el prefijo del espacio de nombres se elimina de cada referencia antes de que coincida con los transfer.hiderefspatrones.
Por ejemplo, si refs/heads/masterse especifica en transfer.hideRefsy el espacio de nombres corriente foo, entonces refs/namespaces/foo/refs/heads/master se omite de los anuncios, pero refs/heads/mastery refs/namespaces/bar/refs/heads/mastertodavía se anuncian como las denominadas líneas de "tener".
Para hacer coincidir las referencias antes de eliminar, agregue un ^delante del nombre de referencia. Si combina !y ^, !debe especificarse primero.


R .. menciona en los comentarios la configuración uploadpack.allowAnySHA1InWant, que permite upload-packaceptar una fetchsolicitud que solicita cualquier objeto. (El valor predeterminado es false).

Ver commit f8edeaa (noviembre de 2016, Git v2.11.1) por David "novalis" Turner ( novalis) :

upload-pack: opcionalmente permite buscar cualquier sha1

Parece un poco tonto hacer una comprobación de accesibilidad en el caso en el que confiamos en que el usuario tenga acceso a absolutamente todo en el repositorio.

Además, es picante en un sistema distribuido: tal vez un servidor anuncia una referencia, pero otro ha tenido un empuje forzado a esa referencia, y tal vez las dos solicitudes HTTP terminan dirigidas a estos servidores diferentes.

VonC
fuente
44
¿Puedes dar un ejemplo más completo sobre cómo crear un clon de repositorio con solo este commit? Lo intenté pero fallé ... ¡Gracias!
Lars Bilke
1
Quiero empujar a GitHub. Tal vez no permiten esto.
Lars Bilke
2
@LarsBilke estamos hablando de clonar o tirar aquí, no empujar. Y estoy bastante seguro de que GitHub aún no tiene Git 2.5 en el lado del servidor.
VonC
2
Ahora, incluso mejor, no existe la uploadpack.allowAnySHA1InWantpenalización de cálculo de accesibilidad (y el vector DoS).
R .. GitHub DEJA DE AYUDAR AL HIELO
1
¡Gracias! Me parece curioso que lo describan como "confiar en que el usuario acceda" en lugar de "confiar en que los autores del repositorio no presionarán basura al azar que no tienen la intención de hacer público".
R .. GitHub DEJA DE AYUDAR A HIELO
98

Solo clonas una vez, por lo que si ya tienes un clon del repositorio remoto, extraerlo no descargará todo nuevamente. Simplemente indique qué rama desea obtener o busque los cambios y verifique la confirmación que desea.

Obtener datos de un nuevo repositorio es muy barato en ancho de banda, ya que solo descargará los cambios que no tenga. Piense en términos de Git haciendo lo correcto, con una carga mínima.

Git almacena todo en la .gitcarpeta. Una confirmación no se puede recuperar y almacenar de forma aislada, necesita todos sus antepasados. Están relacionados entre sí .


Sin embargo, para reducir el tamaño de descarga, puede pedirle a git que busque solo objetos relacionados con una rama o confirmación específica:

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

Esto descargará solo las confirmaciones contenidas en la rama remota branch (y solo las que se pierden) y las almacenará origin/branch. Luego puede fusionar o finalizar la compra.

También puede especificar solo una confirmación SHA1:

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

Esto descargará solo el commit del SHA-1 96de5297df870 especificado (y sus antepasados ​​que echa de menos), y lo almacenará como una rama remota (no existente) origin/foo-commit.

CharlesB
fuente
3
Parece que estás confundiendo lo que significa clon. Cuando obtiene cambios de un repositorio remoto, no lo clona, ​​solo obtiene los commits en su historial. Luego, eliges qué compromiso quieres ver o
combinas
1
Todavía descarga muchos datos (430mb) con git fetch. El commit requerido es de unos pocos kbs. ¿No hay un comando especial para hacer esto realmente? ¿Y qué pasa si quiero eliminar el repositorio 'git fetched'? donde esta guardado
Varun Chitre
9
Esto está bastante desactualizado ahora. Tenemos tanto la capacidad de realizar un clon superficial como de obtener un solo commit . Los clones superficiales ahora pueden empujar y buscar normalmente, sin tener que conocer el historial completo del proyecto, por lo que ya no es correcto decir que un commit no puede existir solo sin sus antepasados. Lo que dices sobre buscar después del clon inicial es muy cierto, pero también tenemos opciones aún más baratas.
Theodore Murdock
66
El último comando (usando SHA1 commit) no funciona para mí. El comando silenciosamente hace "algo" por un tiempo y luego sale sin ningún mensaje o aparente efecto secundario.
HRJ
1
@HRJ Sí, también encontré esto, en Ubuntu 16.04 con Git 2.7.4-0ubuntu1.3. Sin embargo, cuando se usa 2.16.2-0ppa1~ubuntu16.04.1desde el PPA git-core, esto funciona como debería. Suena como un error que se solucionó. No se pudo encontrar una referencia a eso con una búsqueda rápida. Si alguien me puede dar una pista sobre eso, me encantaría que esta solución sea compatible.
gertvdijk
62

Hice un tirón en mi repositorio de git:

git pull --rebase <repo> <branch>

Permití a git extraer todo el código de la rama y luego fui a restablecer el commit que me interesaba.

git reset --hard <commit-hash>

Espero que esto ayude.

Piu Sharma
fuente
1
¡Ninguna de las respuestas funcionó, sin embargo, esta me salvó la vida! ¡Gracias un montón!
michaeltintiuc
El reinicio ... ¡duro funcionó para mí después de la clonación! Gracias.
Nick-ACNB
3
-1: comandos "destructivos" como git reset --hard, cuando se comparten en soluciones generalizadas, pueden llevar a las personas a trampas donde pierden datos (o, en este caso: en un estado donde recuperar sus datos no es trivial).
Yaauie
54

Simplemente puede obtener una confirmación única de un repositorio remoto con

git fetch <repo> <commit>

dónde,

  • <repo>puede ser un nombre de repositorio remoto (por ejemplo origin) o incluso una URL de repositorio remoto (por ejemplo https://git.foo.com/myrepo.git)
  • <commit> puede ser el commit SHA1

por ejemplo

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

después de recuperar el commit (y los antepasados ​​que faltan), simplemente puede verificarlo con

git checkout FETCH_HEAD

Tenga en cuenta que esto lo llevará al estado de "cabeza separada".

Fluir
fuente
10
Cuando intento fetchuna revolución específica como la que haces allí, git falla con el código de error 1 y sin salida. ¿Era esto algo que solía funcionar en versiones anteriores? (Soy v2.0.2.)
Jack O'Connor
2
Editar: Funciona si ya tengo el commit local, como si ya hubiera hecho un completo fetch, aunque en ese caso no estoy seguro de qué uso.
Jack O'Connor
2
De hecho, esto ya no parece funcionar para mí con git 2.0.2. :(
Flujo
2
git checkout FETCH_HEADayuda
lzl124631x
1
¡Este método no funcionará con una búsqueda superficial (por ejemplo --depth=1)!
kingmakerking
16

Simplemente puede obtener el repositorio remoto con:

git fetch <repo>

dónde,

  • <repo>puede ser un nombre de repositorio remoto (por ejemplo origin) o incluso una URL de repositorio remoto (por ejemplo https://git.foo.com/myrepo.git)

por ejemplo:

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

después de buscar los repositorios, puede fusionar los commits que desee (ya que la pregunta es sobre recuperar un commit, en su lugar, puede usar cherry-pick para elegir solo un commit):

git merge <commit>
  • <commit> puede ser el commit SHA1

por ejemplo:

git cherry-pick 0a071603d87e0b89738599c160583a19a6d95545

o

git merge 0a071603d87e0b89738599c160583a19a6d95545

Si es la última confirmación que desea fusionar, también puede usar la variable FETCH_HEAD:

git cherry-pick (or merge) FETCH_HEAD
Sergio
fuente
Esto requiere una configuración de cuenta Git en una máquina. No funciona con una cuenta de prueba. ¿Tienes algo que funciona bajo una cuenta de prueba?
jww
A qué te refieres ? no puedes hacer git fetch?
Sérgio
Ummm, entonces el comando sería git config set uploadpack.allowReachableSHA1InWant ?
Alexander Mills
2

Esto funciona mejor:

git fetch origin specific_commit
git checkout -b temp FETCH_HEAD

nombre "temp" lo que quieras ... aunque esta rama podría quedar huérfana

Alexander Mills
fuente
Claramente no con versiones más antiguas como git 1.8.x
Sorin
1

Finalmente encontré una manera de clonar commit específico usando git cherry-pick . Suponiendo que no tiene ningún repositorio en local y está extrayendo una confirmación específica de forma remota,

1) crear repositorio vacío en local y git init

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

3) git fetch origin [esto no moverá sus archivos a su espacio de trabajo local a menos que se fusione]

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

Hecho. De esta manera, solo tendrá los archivos de esa confirmación específica en su local.

Enter-long-commit-hash:

Puede obtener esto usando -> git log --pretty = oneline

Surya Deepak
fuente
0

Si la confirmación solicitada está en las solicitudes de extracción del repositorio remoto, puede obtenerla por su 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
Noam Manos
fuente