¿Puedo almacenar la carpeta .git fuera de los archivos que quiero rastrear?

147

Tengo una idea inusual de usar git como sistema de respaldo. Digamos que tengo un directorio ./backup/myfiles y quiero hacer una copia de seguridad de eso usando git. Para mantener las cosas limpias, no quiero tener un directorio .git en la carpeta myfiles, así que pensé que podría crear ./backup/git_repos/myfiles. Al mirar los documentos de git, he intentado hacer esto:

$ cd backup/myfiles
$ mkdir ../git_repos/myfiles
$ git --git-dir=../git_repos/myfiles init
Initialized empty Git repository in backup/git_repos/myfiles/
$ git --git-dir="../git_repos/myfiles/" add foo
fatal: pathspec 'foo' did not match any files

Puedes ver el mensaje de error que llego allí. ¿Qué estoy haciendo mal?

Rory
fuente
9
Además de su idea de copia de seguridad, esto también se puede utilizar para mantener sus "archivos de puntos" (.bashrc, .vimrc, etc.) en el directorio de inicio mientras se mantiene la carpeta .git en otro lugar.
Philip
66
Respuesta más directa: stackoverflow.com/a/19548676/170352 (enterrado por viejos votos)
Brandon Bertelsen
1
En el caso de que no tenga acceso de escritura o no desee realizar ningún cambio en el directorio de trabajo (como agregar .git / etc.), esta respuesta a continuación de Leo (también enterrada por viejos votos positivos) es la mejor.
KobeJohn
1
@Philip, a menos que su repositorio de archivos de puntos también contenga submódulos Git. Git no admite submódulos en combinación con un árbol de trabajo externo.
maxschlepzig

Respuestas:

101
git --git-dir=../repo --work-tree=. add foo

Esto hará lo que quieras, pero obviamente apestará cuando tengas que especificarlo con cada comando git que uses.

Puede exportar GIT_WORK_TREE=.y GIT_DIR=../backupGit los recogerá en cada comando. Sin embargo, eso solo le permitirá trabajar cómodamente en un único repositorio por shell.

Prefiero sugerir unir el directorio .git a otro lugar, o crear un enlace simbólico al directorio .git desde su directorio principal de respaldo.

Bombe
fuente
1
Puede archivar el mismo sin especificar el git-dir y el árbol de trabajo en cada comando y sin ningún enlace simbólico. Mira mi respuesta.
niks
La desventaja de un enlace simbólico es que existe dentro del árbol de trabajo, y si algún otro proceso borra el árbol de trabajo, entonces ha perdido el enlace simbólico
Jeff
Además, si el OP no quería un subdirectorio .git en su árbol de trabajo, ¿por qué querría un enlace simbólico?
Jeff
@ Jeff: para una compensación. (En su caso de respaldo, eliminar su árbol de trabajo probablemente no sea una preocupación mayor para él que eliminar cualquier otro directorio (como el repositorio en sí)).
Sz.
1
Estoy usando esto junto con direnv para mis notas. Mi árbol de trabajo está en una carpeta en Dropbox y mi carpeta git en algún lugar afuera. De esta manera puedo tener mis cambios en todas las computadoras fácilmente, pero aún así puedo verificar qué ha cambiado y solo confirmar cuando algo significativo ha cambiado. Gracias
Paulo Phagula
170

Solo necesita asegurarse de que el repositorio sepa dónde está el árbol de trabajo y viceversa.

Para que el repositorio sepa dónde está el árbol de trabajo, establezca el valor de configuración core.worktree. Para que el árbol de trabajo sepa dónde está su directorio git, agregue un archivo llamado .git (¡no una carpeta!) Y agregue una línea como

gitdir: /path/to/repo.git

Desde git 1.7.5 el comando init aprendió una opción adicional para esto.

Puede inicializar un nuevo repositorio separado con

git init --separate-git-dir /path/to/repo.git

Esto inicializará el repositorio git en el directorio separado y agregará el archivo .git en el directorio actual, que es el directorio de trabajo del nuevo repositorio.

Anteriormente a 1.7.5 tenía que usar parámetros ligeramente diferentes y agregar el archivo .git usted mismo.

Para inicializar un repositorio separado, el siguiente comando vincula el árbol de trabajo con el repositorio:

git --git-dir=/path/to/repo.git --work-tree=. init && echo "gitdir: /path/to/repo.git" > .git

Su directorio actual será el árbol de trabajo y git usará el repositorio en /path/to/repo.git. El comando init establecerá automáticamente el core.worktreevalor especificado con el --git-dirparámetro.

Incluso podría agregar un alias para esto:

[alias]
    initexternal = !"f() { git --work-tree=. --git-dir=\"$1\" init && echo \"gitdir: $1\" >> .git; }; f"

Utilice el control de versiones de git en un directorio de trabajo de solo lectura

Con el conocimiento anterior, incluso puede configurar el control de versión de git para un directorio de trabajo sin tener permisos de escritura. Si usa --git-diren cada comando git o ejecuta cada comando desde el repositorio (en lugar del directorio de trabajo), puede omitir el archivo .git y, por lo tanto, no necesita crear ningún archivo dentro del directorio de trabajo. Ver también respuesta de Leo

niks
fuente
77
También puede hacer esto en un repositorio existente: mueva la carpeta .git a donde quiera, agregue el archivo .git para señalarlo, y luego puede continuar usando el repositorio como lo haría normalmente
joachim
Tenga en cuenta que solo puede emitir comandos git desde la raíz de su repositorio. Entrar en subcarpetas lo confunde. (Esto sucede independientemente de si el valor dado para gitdir es relativo o absoluto.)
joachim
2
La solución para esto es especificar 'core.worktree' en el archivo de configuración git real, es decir, el que está en la carpeta a la que apunta en .git.
joachim
2
Sí, eso es lo que describí en mi segunda oración de mi respuesta. El valor de configuración, core.worktreepor supuesto, se almacena en el archivo de configuración de la carpeta .git, donde apunta el archivo .git.
niks
1
Cuando utilicé estas instrucciones, descubrí que el repositorio que se hizo se convirtió en un repositorio simple y da el error fatal: core.worktree and core.bare do not make sense. Parece que solo cambia la configuración, por lo que no está claro resuelve eso.
Steven Lu
62

La --separate-git-diropción para git init(y git clone) se puede utilizar para lograr esto en mi versión de git ( 1.7.11.3). La opción separa el repositorio git del árbol de trabajo y crea un enlace simbólico agnóstico del sistema de archivos git (en la forma de un archivo llamado .git) en la raíz del árbol de trabajo. Creo que el resultado es idéntico a la respuesta de niks .

git init --separate-git-dir path/to/repo.git path/to/worktree
Freidora
fuente
2
+1 El uso de una opción de línea de comando para initsí mismo se ve más y más limpio
goncalopp
en windows, repo.gitse crea con su conjunto de atributos hiden. Luego lo cambio manualmente. ¿Sabes si esto es seguro?
PA.
+1 Sí git inclinó esta opción de línea de comando en la versión 1.7.5, que no estaba disponible en ese momento (si no recuerdo mal). He actualizado mi respuesta para sugerir que use este parámetro.
niks
25

Me resulta más simple revertir los directorios --work-treey --git-dirutilizados en la respuesta de niks:

$ cd read_only_repos
$ git --work-tree=/some/readonly/location/foo/ --git-dir=foo init
$ cd foo
$ git status
On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        .file_foo
        bar
        ...

Este enfoque tiene dos ventajas:

  • Elimina la necesidad de tener opciones de línea de comandos o .gitarchivos. Simplemente opera normalmente desde la raíz del repositorio.
  • Le permite versionar un sistema de archivos incluso si no lo posee. Git solo escribirá en la ubicación del repositorio.

La única advertencia que he encontrado es que en lugar de usar un .gitignorearchivo, editas info/exclude.

Luego puede usar el repositorio read_only_repos/foocomo un control remoto en sus propios repositorios incluso si los archivos originales no están bajo control de versión.

León
fuente
Bueno, este es exactamente el mismo comando. La única diferencia que veo es que intercambiaste el orden de los argumentos --work-tree y --git-dir. Y, por supuesto, no crea el archivo .git en el directorio de trabajo ya que no tiene acceso de escritura. Sin embargo, usar el control de versiones para un directorio sin acceso de escritura es un buen caso de uso. :-)
niks
3
Lo tienes. La clave es crear el repositorio fuera del directorio de trabajo.
Leo
44
Me gusta esto: me permite usar git en directorios que comparto con Dropbox, sin que los demás participantes sepan que git está en uso.
Quentin Stafford-Fraser
19

Es convencional nombrar un directorio que sea un repositorio git que tenga su árbol de trabajo en un lugar inusual con una extensión '.git', muy similar a un repositorio simple.

mkdir ../git_repos/myfiles.git

Si hubiera proporcionado la --work-treeopción en el momento de inicio, esto habría configurado automáticamente la core.worktreevariable de configuración, lo que significa que git sabrá dónde encontrar el árbol de trabajo una vez que especifique el directorio git.

git --git-dir=../git_repos/myfiles.git --work-tree=. init

Pero también puede establecer esta variable después del hecho.

git --git-dir=../git_repos/myfiles.git config core.worktree "$(pwd)"

Una vez que haya hecho esto, el comando agregar debería funcionar como se esperaba.

git --git-dir=../git_repos/myfiles.git add foo
CB Bailey
fuente
Descubrí que si usted hace un CD para ../git_repos/myfiles.git primero, en lugar de estar en el árbol de trabajo real, 'git add foo' simplemente funcionará, y no necesita especificar --git-dir all el tiempo.
Steve Folly
1
Es cierto, pero creo que la mayoría de las personas tienden a trabajar en su árbol de trabajo en lugar de en sus repositorios. Por supuesto, si está usando un árbol de trabajo separado, probablemente esté haciendo algo 'especial' y bien podría estar usando algunas macros para ayudar.
CB Bailey
6

Usar gitdentro del repositorio:

cd ./backup/git_repos/myfiles
git init --bare
git config core.worktree ../myfiles
git config core.bare false

De ahora en adelante, puede usar gitdentro del ./backup/git_repos/myfilesdirectorio, sin establecer ninguna variable de entorno o parámetros adicionales.

To1ne
fuente
Esta parece ser la mejor respuesta, pero recibo el mensaje, warning: core.bare and core.worktree do not make sense¿eso significa que no ha funcionado?
hazrpg
1
Todavía creo que funciona, pero se queja de estar desnudo al configurar el árbol de trabajo. Es por eso que me estoy preparando core.barepara falsedespués.
To1ne
Ah! Eso tiene mas sentido ahora. Gracias por eso.
hazrpg
1

Puede crear un script "nodgit" (No Dot GIT) con algo como

#!/bin/sh
gits=/usr/local/gits
    x=`pwd`
    testdir() {( cd $1; pwd; )}
    while [ "$x" != "/" ]; do
      y=`echo $x|sed -e "s/\//__/g"`
      if ([ -d "$gits/$y" ]); then
        export GIT_DIR="$gits/$y"
        export GIT_WORK_TREE="$x"
        if ([ "$1" = "nodinit" ]); then
          mkdir -p "$GIT_DIR"
          git init --bare; exit $?
        elif ([ "$1" = "shell" ]); then
          bash; exit $?
        else
          exec git "$@"
        fi
      fi
      x=`testdir "$x/.."`
    done

Puede llamar a nodgit en lugar de git y establecerá las variables según sea necesario buscando un repositorio de git. Por ejemplo, digamos que tiene un repositorio (desnudo) en / usr / local / gits / __ home__foo_wibbles y está en / home / foo / wibbles / one, entonces encontrará el directorio de trabajo correcto (/ home / foo / wibbles) y el repositorio .

Oh, también puede usar "shell nodgit" para obtener un shell con los vars correctos establecidos para que pueda usar comandos git viejos y simples.

Paul Hedderly
fuente
0

Suponiendo que sus myfilesdirectorios ya existan y tengan algún contenido, ¿podría vivir con esto?

cd ~/backup
git init
git add myfiles

El .gitdirectorio estará en backup, no en myfiles.

Abie
fuente
Aunque eso solucionaría lo que quiero, prefiero simplemente almacenar la carpeta myfiles en git, y nada más.
Rory
Puede mantener una selección de los archivos que desea gitrastrear utilizando el .gitignorearchivo. Si desea añadir *y !myfilesa la misma, se realizará un seguimiento únicamente ese directorio. Pero si desea un repositorio separado para otro directorio, tendría un problema ...
To1ne
0

Creo guiones que se parecen a

~ / bin / git-slash:

#!/usr/bin/sh

export GIT_DIR=/home/Version-Control/cygwin-root.git/
export GIT_WORK_TREE=/

git --git-dir=$GIT_DIR --work-tree=$GIT_WORK_TREE "$@"

exit $?

Es redundante usar --git_dir = $ GIT_DIR, pero me recuerda que también puedo establecer variables de entorno fuera del script.

El ejemplo anterior es para rastrear cambios locales en los archivos del sistema cygwin.

Puede hacer uno de esos scripts para cualquier proyecto importante que lo necesite, pero / sin /.git es mi uso principal.

Lo anterior es lo suficientemente pequeño como para crear un alias o función de shell, si elimina la redundancia.

Si hago esto con la frecuencia suficiente, reviviría el espacio de trabajo para la asignación de repositorio de

"Boxes, Links, and Parallel Trees: Elements of a Configuration Management System", 
in Workshop Proceedings of the Software Management Conference. 1989.

cuya contraparte moderna más cercana es las asignaciones o vistas de Perforce , que admiten pagos parciales, así como la no colocación del espacio de trabajo y el repositorio.

Krazy Glew
fuente