tipos tag remote origin example etiquetas crear git git-subtree

tag - git remote add



Añadir subdirectorio de repos remoto con git-subárbol (2)

¿Hay alguna manera de agregar un subdirectorio de un repositorio remoto en un subdirectorio de mi repositorio con git-subárbol?

Supongamos que tengo este repositorio principal :

/ dir1 dir2

Y este repositorio de la biblioteca :

/ libdir some-file some-file-to-be-ignored

Quiero importar library / libdir en main / dir1 para que se vea así:

/ dir1 some-file dir2

Usando git-subárbol, puedo especificar importar a dir1 con el argumento --prefix , pero ¿puedo especificar que solo tome los contenidos de un directorio específico en el subárbol?

La razón para usar git-subárbol es que luego puedo sincronizar los dos repositorios.


He estado experimentando con esto, y he encontrado algunas soluciones parciales, aunque ninguna es completamente perfecta.

Para estos ejemplos, consideraré fusionar los cuatro archivos de contrib/completion/ de https://github.com/git/git.git en third_party/git_completion/ del repositorio local.

1. git diff | git aplicar

Esta es probablemente la mejor forma que he encontrado. Solo probé la fusión en un solo sentido; No he intentado enviar cambios de vuelta al repositorio aguas arriba.

# Do this the first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git # The next line is optional. Without it, the upstream commits get # squashed; with it they will be included in your local history. $ git merge -s ours --no-commit gitgit/master # The trailing slash is important here! $ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion $ git commit # In future, you can merge in additional changes as follows: # The next line is optional. Without it, the upstream commits get # squashed; with it they will be included in your local history. $ git merge -s ours --no-commit gitgit/master # Replace the SHA1 below with the commit hash that you most recently # merged in using this technique (i.e. the most recent commit on # gitgit/master at the time). $ git diff --color=never 53e53c7c81ce2c7c4cd45f95bc095b274cb28b76:contrib/completion gitgit/master:contrib/completion | git apply -3 --directory=third_party/git-completion # Now fix any conflicts if you''d modified third_party/git-completion. $ git commit

Dado que es incómodo tener que recordar el compromiso SHA1 más reciente que se fusionó desde el repositorio aguas arriba, he escrito esta función Bash que hace todo el trabajo por usted (tomándola del registro de git):

git-merge-subpath() { local SQUASH if [[ $1 == "--squash" ]]; then SQUASH=1 shift fi if (( $# != 3 )); then local PARAMS="[--squash] SOURCE_COMMIT SOURCE_PREFIX DEST_PREFIX" echo "USAGE: ${FUNCNAME[0]} $PARAMS" return 1 fi # Friendly parameter names; strip any trailing slashes from prefixes. local SOURCE_COMMIT="$1" SOURCE_PREFIX="${2%/}" DEST_PREFIX="${3%/}" local SOURCE_SHA1 SOURCE_SHA1=$(git rev-parse --verify "$SOURCE_COMMIT^{commit}") || return 1 local OLD_SHA1 local GIT_ROOT=$(git rev-parse --show-toplevel) if [[ -n "$(ls -A "$GIT_ROOT/$DEST_PREFIX" 2> /dev/null)" ]]; then # OLD_SHA1 will remain empty if there is no match. local RE="^${FUNCNAME[0]}: [0-9a-f]{40} $SOURCE_PREFIX $DEST_PREFIX/$" OLD_SHA1=$(git log -1 --format=%b -E --grep="$RE" / | grep --color=never -E "$RE" | tail -1 | awk ''{print $2}'') fi local OLD_TREEISH if [[ -n $OLD_SHA1 ]]; then OLD_TREEISH="$OLD_SHA1:$SOURCE_PREFIX" else # This is the first time git-merge-subpath is run, so diff against the # empty commit instead of the last commit created by git-merge-subpath. OLD_TREEISH=$(git hash-object -t tree /dev/null) fi && if [[ -z $SQUASH ]]; then git merge -s ours --no-commit "$SOURCE_COMMIT" fi && git diff --color=never "$OLD_TREEISH" "$SOURCE_COMMIT:$SOURCE_PREFIX" / | git apply -3 --directory="$DEST_PREFIX" || git mergetool if (( $? == 1 )); then echo "Uh-oh! Try cleaning up with |git reset --merge|." else git commit -em "Merge $SOURCE_COMMIT:$SOURCE_PREFIX/ to $DEST_PREFIX/ # Feel free to edit the title and body above, but make sure to keep the # ${FUNCNAME[0]}: line below intact, so ${FUNCNAME[0]} can find it # again when grepping git log. ${FUNCNAME[0]}: $SOURCE_SHA1 $SOURCE_PREFIX $DEST_PREFIX" fi }

Úselo así:

# Do this the first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git $ git-merge-subpath gitgit/master contrib/completion third_party/git-completion # In future, you can merge in additional changes as follows: $ git fetch gitgit $ git-merge-subpath gitgit/master contrib/completion third_party/git-completion # Now fix any conflicts if you''d modified third_party/git-completion.

2. git read-tree

Si nunca va a realizar cambios locales en los archivos fusionados, es decir, siempre estará sobrescrito con el subdirectorio local con la última versión de la versión anterior, entonces un enfoque similar pero más simple es usar git read-tree :

# Do this the first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git # The next line is optional. Without it, the upstream commits get # squashed; with it they will be included in your local history. $ git merge -s ours --no-commit gitgit/master $ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion $ git commit # In future, you can *overwrite* with the latest changes as follows: # As above, the next line is optional (affects squashing). $ git merge -s ours --no-commit gitgit/master $ git rm -rf third_party/git-completion $ git read-tree --prefix=third_party/git-completion/ -u gitgit/master:contrib/completion $ git commit

Encontré una publicación de blog que afirmaba poder fusionar (sin sobreescribir) usando una técnica similar, pero no funcionó cuando lo probé.

3. git subárbol

De hecho, encontré una solución que usa el git subtree , gracias a http://jrsmith3.github.io/merging-a-subdirectory-from-another-repo-via-git-subtree.html , pero es increíblemente lenta (cada git subtree split comando git subtree split continuación me lleva 9 minutos para un repositorio de 28 MB con 39000 confirmaciones en un Xeon X5675 dual, mientras que las otras soluciones que encontré tardan menos de un segundo.

Si puedes vivir con la lentitud, debería ser viable:

# Do this the first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git $ git checkout gitgit/master $ git subtree split -P contrib/completion -b temporary-split-branch $ git checkout master $ git subtree add --squash -P third_party/git-completion temporary-split-branch $ git branch -D temporary-split-branch # In future, you can merge in additional changes as follows: $ git checkout gitgit/master $ git subtree split -P contrib/completion -b temporary-split-branch $ git checkout master $ git subtree merge --squash -P third_party/git-completion temporary-split-branch # Now fix any conflicts if you''d modified third_party/git-completion. $ git branch -D temporary-split-branch

Tenga en cuenta que --squash para evitar contaminar el repositorio local con muchas confirmaciones, pero puede eliminar --squash si prefiere conservar el historial de confirmaciones.

Es posible que las divisiones posteriores se puedan hacer más rápido usando --rejoin (ver https://.com/a/16139361/691281 ) - No lo --rejoin .

4. Subárbol completo de repo git

El OP indicó claramente que desea fusionar un subdirectorio de un repositorio en sentido ascendente en un subdirectorio del repositorio local. Sin embargo, si desea fusionar un repositorio completo ascendente en un subdirectorio de su repositorio local, existe una alternativa más simple, limpia y mejor respaldada:

# Do this the first time: $ git subtree add --squash --prefix=third_party/git https://github.com/git/git.git master # In future, you can merge in additional changes as follows: $ git subtree pull --squash --prefix=third_party/git https://github.com/git/git.git master

O si prefiere evitar repetir el URL del repositorio, puede agregarlo como control remoto:

# Do this the first time: $ git remote add -f -t master --no-tags gitgit https://github.com/git/git.git $ git subtree add --squash --prefix=third_party/git gitgit/master # In future, you can merge in additional changes as follows: $ git subtree pull --squash --prefix=third_party/git gitgit/master # And you can push changes back upstream as follows: $ git subtree push --prefix=third_party/git gitgit/master # Or possibly (not sure what the difference is): $ git subtree push --squash --prefix=third_party/git gitgit/master

Ver también:

5. Submódulo entero de repo git

Una técnica relacionada son los submódulos de git , pero vienen con advertencias molestas (por ejemplo, las personas que clonan su repositorio no clonarán los submódulos a menos que llamen a git clone --recursive ), por lo que no investigué si pueden soportar subcampos.


Pude hacer algo como esto agregando :dirname al comando read-tree. (Nótese que en realidad estoy tratando de aprender git y git-subárboles esta semana, y tratando de configurar un entorno similar a cómo tenía mis proyectos en subversión usando svn: externals; mi punto es que podría haber una mejor o más fácil que los comandos que estoy mostrando aquí ...)

Entonces, por ejemplo, usando su estructura ejemplo arriba:

git remote add library_remote _URL_TO_LIBRARY_REPO_ git fetch library_remote git checkout -b library_branch library_remote/master git checkout master git read-tree --prefix=dir1 -u library_branch:libdir