Pregunta

Tengo mi repositorio Git que, en la raíz, tiene dos subdirectorios:

/finisht
/static

Cuando esto estaba en SVN , / finisht se verificó en un solo lugar, mientras que / static se revisó en otro lugar, de este modo:

svn co svn+ssh://admin@domain.com/home/admin/repos/finisht/static static

¿Hay alguna manera de hacer esto con Git?

¿Fue útil?

Solución

EDIT : a partir de Git 2.19, esto es finalmente posible, como se puede ver en esta respuesta: https://stackoverflow.com/a/52269934/2988 .

Considere la posibilidad de aumentar esa respuesta.

Nota: en Git 2.19, solo se implementa el soporte del lado del cliente, todavía falta el soporte del lado del servidor, por lo que solo funciona cuando se clonan repositorios locales. También tenga en cuenta que los grandes hosters Git, por ejemplo. GitHub, en realidad no usa el servidor Git, ellos usan su propia implementación, por lo que incluso si el soporte aparece en el servidor Git, no significa automáticamente que funcione en los hosters Git. (OTOH, ya que no usan el servidor Git, podrían implementarlo más rápido en sus propias implementaciones antes de que aparezca en el servidor Git).


No, eso no es posible en Git.

Implementar algo como esto en Git sería un esfuerzo sustancial y significaría que ya no se podría garantizar la integridad del repositorio del lado del cliente. Si está interesado, busque discusiones en " clon disperso " y " búsqueda dispersa " en la lista de correo de git.

En general, el consenso en la comunidad de Git es que si tiene varios directorios que siempre se verifican de forma independiente, entonces estos son realmente dos proyectos diferentes y deberían vivir en dos repositorios diferentes. Puedes pegarlos nuevamente usando Submódulos Git .

Otros consejos

Lo que estás tratando de hacer se llama compra dispersa , y esa función se agregó en git 1.7.0 (febrero de 2012). Los pasos para hacer un clon disperso son los siguientes:

mkdir <repo>
cd <repo>
git init
git remote add -f origin <url>

Esto crea un repositorio vacío con su control remoto, y recupera todos los objetos pero no los verifica. Entonces haz:

git config core.sparseCheckout true

Ahora necesita definir qué archivos / carpetas desea retirar en realidad. Esto se hace enumerándolos en .git / info / sparse-checkout , por ejemplo:

echo "some/dir/" >> .git/info/sparse-checkout
echo "another/sub/tree" >> .git/info/sparse-checkout

Por último, pero no menos importante, actualice su repositorio vacío con el estado desde el control remoto:

git pull origin master

Ahora tendrás los archivos " desprotegidos " para some / dir y another / sub / tree en su sistema de archivos (con esas rutas aún), y no hay otras rutas presentes.

Es posible que desee consultar tutorial ampliado y probablemente deberías leer la documentación oficial para realizar un pago rápido .

Como una función:

function git_sparse_clone() (
  rurl="$1" localdir="$2" && shift 2

  mkdir -p "$localdir"
  cd "$localdir"

  git init
  git remote add -f origin "$rurl"

  git config core.sparseCheckout true

  # Loops over remaining args
  for i; do
    echo "$i" >> .git/info/sparse-checkout
  done

  git pull origin master
)

Uso:

git_sparse_clone "http://github.com/tj/n" "./local/location" "/bin"

Tenga en cuenta que esto seguirá descargando todo el repositorio desde el servidor & # 8211; Solo se reduce el tamaño de la caja. Por el momento no es posible clonar un solo directorio. Pero si no necesita el historial del repositorio, al menos puede ahorrar ancho de banda creando un clon poco profundo. Consulte la respuesta de udondon a continuación para obtener información sobre cómo combinar la poca profundidad a clon y desprotección dispersa.

Puede combinar las características checkout disperso y Clone superficial . El clon superficial corta el historial y el pago disperso solo extrae los archivos que coinciden con sus patrones.

git init <repo>
cd <repo>
git remote add origin <url>
git config core.sparsecheckout true
echo "finisht/*" >> .git/info/sparse-checkout
git pull --depth=1 origin master

Necesitará un mínimo de git 1.9 para que esto funcione. Lo probé solo con 2.2.0 y 2.2.2.

De esta manera aún podrás empujar , lo cual no es posible con git archive .

git clone --filter de Git 2.19

Esta opción realmente omitirá la obtención de objetos innecesarios del servidor:

git clone --depth 1 --no-checkout --filter=blob:none \
  "file://$(pwd)/server_repo" local_repo
cd local_repo
git checkout master -- mydir/

El servidor debe configurarse con:

git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1

Se hizo una extensión al protocolo remoto Git para admitir esta función en v2.19.0, pero no hay soporte de servidor en ese momento. Pero ya puede ser probado localmente.

TODO: --filter = blob: none omite todos los blobs, pero aún recupera todos los objetos del árbol. Pero en un repositorio normal, esto debería ser muy pequeño en comparación con los archivos en sí, por lo que ya es suficientemente bueno Consultado en: https://www.spinics.net/lists/git/msg342006.html Devs respondió que un --filter = tree: 0 está trabajando para hacer eso. Se agregó en 2.20 .

Recuerde que --depth 1 ya implica --single-branch , vea también: ¿Cómo puedo clonar una única rama en Git?

file: // $ (path) es necesario para superar git clone protocol shenanigans: ¿Cómo copiar de forma superficial un repositorio de git local con una ruta relativa?

El formato de --filter se documenta en man git-rev-list .

Documentos en el árbol de Git:

Pruébalo

#!/usr/bin/env bash
set -eu

list-objects() (
  git rev-list --all --objects
  echo "master commit SHA: $(git log -1 --format="%H")"
  echo "mybranch commit SHA: $(git log -1 --format="%H")"
  git ls-tree master
  git ls-tree mybranch | grep mybranch
  git ls-tree master~ | grep root
)

# Reproducibility.
export GIT_COMMITTER_NAME='a'
export GIT_COMMITTER_EMAIL='a'
export GIT_AUTHOR_NAME='a'
export GIT_AUTHOR_EMAIL='a'
export GIT_COMMITTER_DATE='2000-01-01T00:00:00+0000'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'

rm -rf server_repo local_repo
mkdir server_repo
cd server_repo

# Create repo.
git init --quiet
git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1

# First commit.
# Directories present in all branches.
mkdir d1 d2
printf 'd1/a' > ./d1/a
printf 'd1/b' > ./d1/b
printf 'd2/a' > ./d2/a
printf 'd2/b' > ./d2/b
# Present only in root.
mkdir 'root'
printf 'root' > ./root/root
git add .
git commit -m 'root' --quiet

# Second commit only on master.
git rm --quiet -r ./root
mkdir 'master'
printf 'master' > ./master/master
git add .
git commit -m 'master commit' --quiet

# Second commit only on mybranch.
git checkout -b mybranch --quiet master~
git rm --quiet -r ./root
mkdir 'mybranch'
printf 'mybranch' > ./mybranch/mybranch
git add .
git commit -m 'mybranch commit' --quiet

echo "# List and identify all objects"
list-objects
echo

# Restore master.
git checkout --quiet master
cd ..

# Clone. Don't checkout for now, only .git/ dir.
git clone --depth 1 --quiet --no-checkout --filter=blob:none "file://$(pwd)/server_repo" local_repo
cd local_repo

# List missing objects from master.
echo "# Missing objects after --no-checkout"
git rev-list --all --quiet --objects --missing=print
echo

echo "# Git checkout fails without internet"
mv ../server_repo ../server_repo.off
! git checkout master
echo

echo "# Git checkout fetches the missing directory from internet"
mv ../server_repo.off ../server_repo
git checkout master -- d1/
echo

echo "# Missing objects after checking out d1"
git rev-list --all --quiet --objects --missing=print

en un solo repositorio sin herramientas feas de terceros como repo .

Imagina almacenando enormes blobs directamente en el repositorio sin ningún tercero feo extensiones .

Imagínese si GitHub permitiría por archivo / metadatos de directorio como estrellas y permisos, así que puedes almacenar todas tus cosas personales en un solo repositorio.

Imagínese si los submódulos se trataron exactamente como directorios regulares : solo solicite un SHA de árbol, y un mecanismo similar a DNS resuelve su solicitud , en primer lugar en su local ~ / .git , luego primero a los servidores más cercanos (el espejo / caché de su empresa) y terminando en GitHub.

Para otros usuarios que solo quieran descargar un archivo / carpeta de github, simplemente use:

svn export <repo>/trunk/<folder>

por ejemplo

svn export https://github.com/lodash/lodash.com/trunk/docs

(sí, eso es svn aquí. al parecer, en 2016 todavía necesitas svn para simplemente descargar algunos archivos github)

Cortesía: Descargue una sola carpeta o directorio desde un repositorio de GitHub

Importante : asegúrate de actualizar la URL de github y de reemplazar / tree / master / con '/ trunk /'.

Como script bash:

git-download(){
    folder=${@/tree\/master/trunk}
    folder=${folder/blob\/master/trunk}
    svn export $folder
}

Nota Este método descarga una carpeta, no la clona / desprotege. No puedes reenviar los cambios al repositorio. Por otro lado, esto se traduce en una descarga más pequeña en comparación con el pago disperso o el pago superficial.

Si nunca planeas interactuar con el repositorio desde el cual clonaste, puedes hacer un clon de git completo y reescribir tu repositorio usando git filter-branch --subdirectory-filter . De esta manera, al menos se conservará la historia.

Git 1.7.0 tiene & # 8220; comprobaciones dispersas & # 8221 ;. Ver & # 8220; core.sparseCheckout & # 8221; en la git config manpage , & # 8220; Comprobación dispersa & # 8221; en el git read-tree página de manual , y & # 8220; Salto de área de trabajo & # 8221; en el git update-index página de manual .

La interfaz no es tan conveniente como la SVN & # 8217; s (por ejemplo, no hay manera de hacer un checkout disperso en el momento de un clon inicial), pero la funcionalidad básica sobre la cual se pueden construir interfaces más simples ahora está disponible.

Esto parece mucho más simple:

git archive --remote=<repo_url> <branch> <path> | tar xvf -

No es posible clonar el subdirectorio solo con Git, pero a continuación hay algunas soluciones.

rama del filtro

Es posible que desee volver a escribir el repositorio para que parezca que trunk / public_html / haya sido su raíz del proyecto, y descartar el resto del historial (usando filter-branch ), pruébelo en la rama de pago:

git filter-branch --subdirectory-filter trunk/public_html -- --all

Notas: el - que separa las opciones de las ramas de filtro de las opciones de revisión, y el --allito para volver a escribir todas las ramas y etiquetas. Toda la información, incluidos los tiempos de confirmación originales o la información de combinación, se conservará . Este comando respeta el archivo .git / info / grafts y las referencias en el espacio de nombres refs / replace / , por lo que si tiene algún injerto o reemplazo refs definido, ejecutar este comando los hará permanentes.

  

¡Advertencia! El historial reescrito tendrá diferentes nombres de objeto para todos los objetos y no convergerá con la rama original. No podrá empujar y distribuir fácilmente la rama reescrita sobre la rama original. No utilice este comando si no conoce todas las implicaciones, y evite usarlo de todos modos, si un solo compromiso simple sería suficiente para solucionar su problema.


Checkout disperso

Aquí hay pasos simples con el enfoque de extracción de información que llenará el directorio de trabajo de manera escasa, por lo que puede Dígale a Git qué carpeta (s) o archivo (s) en el directorio de trabajo vale la pena revisar.

  1. Repone el repositorio como de costumbre ( --no-checkout es opcional):

    git clone --no-checkout git@foo/bar.git
    cd bar
    

    Puede omitir este paso, si ya ha clonado su repositorio.

    Sugerencia: para repositorios grandes, considere clon superficial ( --depth 1 ) para verificar solo la última revisión y / y --single-branch solamente.

  2. Habilita la opción sparseCheckout :

    git config core.sparseCheckout true
    
  3. Especifique las carpetas para el pago disperso ( sin espacio al final):

    echo "trunk/public_html/*"> .git/info/sparse-checkout
    

    o edita .git/info/sparse-checkout.

  4. Compruebe la rama (por ejemplo, master ):

    git checkout master
    

Ahora deberías haber seleccionado las carpetas en tu directorio actual.

Puede considerar enlaces simbólicos si tiene demasiados niveles de directorios o filiales filtrando en su lugar.


Acabo de escribí un script para GitHub .

Uso:

python get_git_sub_dir.py path/to/sub/dir <RECURSIVE>

Aquí hay un script de shell que escribí para el caso de uso de un solo subdirectorio de pago disperso

coSubDir.sh

localRepo=$1
remoteRepo=$2
subDir=$3


# Create local repository for subdirectory checkout, make it hidden to avoid having to drill down to the subfolder
mkdir ./.$localRepo
cd ./.$localRepo
git init
git remote add -f origin $remoteRepo
git config core.sparseCheckout true

# Add the subdirectory of interest to the sparse checkout.
echo $subDir >> .git/info/sparse-checkout

git pull origin master

# Create convenience symlink to the subdirectory of interest
cd ..
ln -s ./.$localRepo$subDir $localRepo

Esto clonará una carpeta específica y eliminará todo el historial no relacionado con ella.

git clone --single-branch -b {branch} git@github.com:{user}/{repo}.git
git filter-branch --subdirectory-filter {path/to/folder} HEAD
git remote remove origin
git remote add origin git@github.com:{user}/{new-repo}.git
git push -u origin master
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top