Puis-je stocker le dossier .git en dehors des fichiers que je souhaite suivre?

147

J'ai une idée inhabituelle d'utiliser git comme système de sauvegarde. Alors disons que j'ai un répertoire ./backup/myfiles et je veux le sauvegarder en utilisant git. Pour garder les choses propres, je ne veux pas avoir de répertoire .git dans le dossier myfiles, alors j'ai pensé que je pourrais créer ./backup/git_repos/myfiles. En regardant la documentation git, j'ai essayé de faire ceci:

$ 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

Vous pouvez voir le message d'erreur J'y arrive. Qu'est-ce que je fais mal?

Rory
la source
9
En plus de votre idée de sauvegarde, cela peut également être utilisé pour conserver vos "dotfiles" (.bashrc, .vimrc, etc.) dans le répertoire personnel tout en gardant le dossier .git ailleurs.
Philip
6
Réponse la plus simple: stackoverflow.com/a/19548676/170352 (enterré à cause d'anciens votes positifs)
Brandon Bertelsen
1
Dans le cas où vous n'avez pas d'accès en écriture ou ne souhaitez pas apporter de modifications au répertoire de travail (comme l'ajout de .git / etc.), cette réponse ci-dessous par Leo (également enterrée par d'anciens votes positifs) est la meilleure.
KobeJohn
1
@Philip, sauf si votre référentiel dotfiles contient également des sous-modules Git. Git ne prend pas en charge les sous-modules en combinaison avec un arbre de travail externe.
maxschlepzig

Réponses:

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

Cela fera ce que vous voulez mais sera évidemment nul lorsque vous devrez le spécifier avec chaque commande git que vous utiliserez.

Vous pouvez exporter GIT_WORK_TREE=.et GIT_DIR=../backupGit les récupérera à chaque commande. Cela ne vous permettra cependant que de travailler confortablement dans un seul référentiel par shell.

Je suggère plutôt de créer un lien symbolique vers le répertoire .git ailleurs ou de créer un lien symbolique vers le répertoire .git à partir de votre répertoire de sauvegarde principal.

Bombe
la source
1
Vous pouvez archiver la même chose sans spécifier le git-dir et l'arbre de travail dans chaque commande et sans aucun lien symbolique. Voyez ma réponse.
niks
L'inconvénient d'un lien symbolique est qu'il existe dans l'arbre de travail, et si un autre processus efface l'arbre de travail, alors vous avez perdu le lien symbolique
Jeff
De plus, si l'OP ne voulait pas de sous-répertoire .git dans son arbre de travail, pourquoi voudrait-il un lien symbolique?
Jeff
@Jeff: pour un compromis. (Dans son cas de sauvegarde, effacer son arbre de travail n'est probablement pas plus important pour lui que d'effacer tout autre répertoire (comme le dépôt lui-même).)
Sz.
1
J'utilise ceci avec direnv pour mes notes. Mon arbre de travail se trouve dans un dossier de Dropbox et mon dossier git quelque part à l'extérieur. De cette façon, je peux facilement avoir mes modifications sur tous les ordinateurs, tout en étant capable de vérifier ce qui a changé et de ne valider que lorsque quelque chose d'important a changé. Merci
Paulo Phagula
170

Il vous suffit de vous assurer que le référentiel sait où se trouve l'arbre de travail et vice versa.

Pour indiquer au référentiel où se trouve l'arborescence de travail, définissez la valeur de configuration core.worktree. Pour que l'arborescence de travail sache où se trouve le répertoire git, ajoutez un fichier nommé .git (pas un dossier!) Et ajoutez une ligne comme

gitdir: /path/to/repo.git

Depuis git 1.7.5, la commande init a appris une option supplémentaire pour cela.

Vous pouvez initialiser un nouveau référentiel séparé avec

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

Cela initialisera le référentiel git dans le répertoire séparé et ajoutera le fichier .git dans le répertoire actuel, qui est le répertoire de travail du nouveau référentiel.

Avant la version 1.7.5, vous deviez utiliser des paramètres légèrement différents et ajouter vous-même le fichier .git.

Pour initialiser un référentiel distinct, la commande suivante relie l'arborescence de travail au référentiel:

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

Votre répertoire actuel sera l'arborescence de travail et git utilisera le référentiel à /path/to/repo.git. La commande init définira automatiquement la core.worktreevaleur spécifiée avec le --git-dirparamètre.

Vous pouvez même ajouter un alias pour cela:

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

Utiliser le contrôle de version git sur un répertoire de travail en lecture seule

Avec les connaissances ci-dessus, vous pouvez même configurer le contrôle de version git pour un répertoire de travail sans avoir les autorisations d'écriture. Si vous utilisez --git-dirsur chaque commande git ou exécutez chaque commande à partir du référentiel (au lieu du répertoire de travail), vous pouvez omettre le fichier .git et n'avez donc pas besoin de créer de fichiers dans le répertoire de travail. Voir aussi la réponse des Léos

niks
la source
7
Vous pouvez également le faire dans un dépôt existant: déplacez le dossier .git où vous le souhaitez, ajoutez le fichier .git pour qu'il pointe vers lui, puis vous pouvez simplement continuer à utiliser le dépôt comme vous le feriez normalement
joachim
Notez que vous ne pouvez émettre des commandes git qu'à partir de la racine de votre dépôt. Aller dans les sous-dossiers le confond! (Cela se produit que la valeur donnée pour gitdir soit relative ou absolue.)
joachim
2
Le correctif pour cela est de spécifier 'core.worktree' dans le fichier de configuration git réel, c'est-à-dire celui du dossier sur lequel vous pointez dans .git.
joachim
2
Oui, c'est ce que j'ai décrit dans ma deuxième phrase de ma réponse. La valeur de configuration core.worktreeest bien entendu stockée dans le fichier de configuration du dossier .git, vers lequel pointe le fichier .git.
niks
1
Je trouve que lorsque j'ai utilisé ces instructions, le dépôt est devenu un dépôt nu et il donne l'erreur fatal: core.worktree and core.bare do not make sense. On dirait que changer la configuration pour que ce ne soit pas nu résout cela.
Steven Lu
62

L' --separate-git-diroption pour git init(et git clone) peut être utilisée pour accomplir cela sur ma version de git ( 1.7.11.3). L'option sépare le référentiel git de l'arborescence de travail et crée un lien symbolique git indépendant du système de fichiers (sous la forme d'un fichier nommé .git) à la racine de l'arborescence de travail. Je pense que le résultat est identique à la réponse de Niks .

git init --separate-git-dir path/to/repo.git path/to/worktree
Friteuse
la source
2
+1 L'utilisation d'une option de ligne de commande pour initelle-même semble plus claire et plus propre
goncalopp
dans windows, repo.gitest créé avec son jeu d'attributs caché. Je le change ensuite manuellement. Savez-vous si c'est sûr?
PA.
+1 Oui, git a appuyé cette option de ligne de commande dans la version 1.7.5, qui n'était pas disponible à l'époque (si je me souviens bien). J'ai mis à jour ma réponse pour suggérer d'utiliser ce paramètre.
niks
25

Je trouve plus simple d'inverser les répertoires --work-treeet --git-dirutilisés dans la réponse 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
        ...

Cette approche a deux avantages:

  • Il supprime le besoin d'avoir des options de ligne de commande ou des .gitfichiers. Vous opérez simplement normalement à partir de la racine du référentiel.
  • Il vous permet de versionner un système de fichiers même si vous ne le possédez pas. Git n'écrira que dans l'emplacement du référentiel.

La seule mise en garde que j'ai rencontrée est qu'au lieu d'utiliser un .gitignorefichier, vous modifiez info/exclude.

Vous pouvez ensuite utiliser le référentiel read_only_repos/foocomme distant dans vos propres référentiels même si les fichiers d'origine ne sont pas sous contrôle de version.

Leo
la source
Eh bien, c'est exactement la même commande. La seule différence que je vois est que vous avez permuté l'ordre des arguments --work-tree et --git-dir. Et bien sûr, vous ne créez pas le fichier .git dans le répertoire de travail car vous n'y avez pas accès en écriture. Néanmoins, utiliser le contrôle de version pour un répertoire sans accès en écriture est un bon cas d'utilisation. :-)
niks
3
Tu l'as eu. La clé est de créer le dépôt en dehors du répertoire de travail.
Leo
4
J'aime ça - cela me permet d'utiliser git sur les répertoires que je partage avec Dropbox, sans que les autres participants sachent même que git est utilisé.
Quentin Stafford-Fraser
19

Il est conventionnel de nommer un répertoire qui est un dépôt git qui a son arbre de travail dans un endroit inhabituel avec une extension '.git', un peu comme un dépôt nu.

mkdir ../git_repos/myfiles.git

Si vous aviez fourni l' --work-treeoption au moment de l'initialisation, cela aurait automatiquement configuré la core.worktreevariable de configuration, ce qui signifie que git saura où trouver l'arborescence de travail une fois que vous aurez spécifié le répertoire git.

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

Mais vous pouvez également définir cette variable après coup.

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

Une fois que vous avez fait cela, la commande add devrait fonctionner comme prévu.

git --git-dir=../git_repos/myfiles.git add foo
CB Bailey
la source
J'ai trouvé que si vous cd vers ../git_repos/myfiles.git d'abord, plutôt que d'être dans le vrai arbre de travail, 'git add foo' fonctionnera simplement, et vous n'avez pas besoin de spécifier --git-dir all le temps.
Steve Folly
1
C'est vrai, mais je pense que la plupart des gens ont tendance à travailler dans leur arbre de travail plutôt que dans leurs référentiels. Bien sûr, si vous utilisez un arbre de travail détaché, vous faites probablement quelque chose de «spécial» et vous pouvez très bien utiliser des macros pour vous aider.
CB Bailey
6

Utiliser gità l'intérieur du repo:

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

À partir de maintenant, vous pouvez utiliser gità l'intérieur du ./backup/git_repos/myfilesrépertoire, sans définir de variables d'environnement ni de paramètres supplémentaires.

To1ne
la source
Cela semble être la meilleure réponse, mais je reçois le message. warning: core.bare and core.worktree do not make senseCela signifie-t-il que cela n'a pas fonctionné?
hazrpg
1
Je pense toujours que cela fonctionne, mais il se plaint d'être nu lors de la configuration de l'arbre de travail. Voilà pourquoi je suis mise core.bareà la falsesuite.
To1ne
Ah! Cela a plus de sens maintenant. Merci pour ça.
hazrpg
1

Vous pouvez créer un script "nodgit" (No Dot GIT) avec quelque chose comme

#!/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

Vous pouvez appeler nodgit à la place de git et il définira des variables si nécessaire en recherchant un dépôt git. Par exemple, disons que vous avez un dépôt (nu) dans / usr / local / gits / __ home__foo_wibbles et que vous êtes dans / home / foo / wibbles / one alors il trouvera le bon répertoire de travail (/ home / foo / wibbles) et le repo .

Oh, vous pouvez également utiliser "nodgit shell" pour obtenir un shell avec le bon jeu de variables afin que vous puissiez utiliser les anciennes commandes git.

Paul Hedderly
la source
0

En supposant que vos myfilesrépertoires existent déjà et contiennent du contenu, pourriez-vous vivre avec ceci:

cd ~/backup
git init
git add myfiles

Le .gitrépertoire sera dans backup, pas dans myfiles.

Abie
la source
Bien que cela répare en quelque sorte ce que je veux, je préfère simplement stocker le dossier myfiles sous git, et rien d'autre.
Rory
Vous pouvez conserver une sélection des fichiers que vous souhaitez gitsuivre à l'aide du .gitignorefichier. Si vous ajoutez *et !myfilesà lui, seul ce répertoire sera suivi. Mais si vous voulez un dépôt séparé pour un autre répertoire, vous auriez un problème ...
To1ne
0

Je crée des scripts qui ressemblent à

~ / 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 $?

Il est redondant d'utiliser --git_dir = $ GIT_DIR, mais cela me rappelle que je peux également définir des variables d'environnement en dehors du script.

L'exemple ci-dessus sert à suivre les modifications locales des fichiers système cygwin.

Je peux créer un tel script pour tout projet majeur qui en a besoin - mais / sans /.git est mon utilisation principale.

Ce qui précède est suffisamment petit pour créer un alias ou une fonction de shell, si vous éliminez la redondance.

Si je fais cela assez souvent, je raviverais l'espace de travail au mappage de référentiel de

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

dont l'homologue moderne le plus proche est les mappages ou vues Perforce , prenant en charge les extractions partielles ainsi que la non-colocation de l'espace de travail et du dépôt.

Krazy Glew
la source