Posso armazenar a pasta .git fora dos arquivos que desejo rastrear?

147

Eu tenho uma idéia incomum de usar o git como um sistema de backup. Então, digamos que eu tenho um diretório ./backup/myfiles e quero fazer backup usando o git. Para manter as coisas limpas, não quero ter um diretório .git na pasta myfiles, então pensei em criar ./backup/git_repos/myfiles. Analisando os documentos do git, tentei fazer o seguinte:

$ 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

Você pode ver a mensagem de erro que chego lá. O que estou fazendo de errado?

Rory
fonte
9
Assim como sua idéia de backup, isso também pode ser usado para manter seus "arquivos de ponto" (.bashrc, .vimrc, etc) no diretório inicial, mantendo a pasta .git em outro lugar.
Philip
6
A maioria resposta straighforward: stackoverflow.com/a/19548676/170352 (enterrado por causa da upvotes velhos)
Brandon Bertelsen
1
No caso de você não ter acesso de gravação ou não desejar fazer alterações no diretório ativo (como adicionar .git / etc.), esta resposta abaixo de Leo (também enterrada por upvotes antigos) é a melhor.
KobeJohn
1
@ Phillip, a menos que seu repositório dotfiles também contenha sub-módulos Git. O Git não suporta submódulos em combinação com uma árvore de trabalho externa.
maxschlepzig

Respostas:

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

Isso fará o que você deseja, mas obviamente será péssimo quando você precisar especificá-lo com todos os comandos git que você usar.

Você pode exportar GIT_WORK_TREE=.e o GIT_DIR=../backupGit irá buscá-los em cada comando. Isso permitirá que você trabalhe confortavelmente em um único repositório por shell.

Prefiro sugerir a ligação simbólica do diretório .git para outro lugar ou a criação de um link simbólico para o diretório .git a partir do diretório principal de backup.

Bombe
fonte
1
Você pode arquivar o mesmo sem especificar o git-dir e a árvore de trabalho em todos os comandos e sem links simbólicos. Veja minha resposta.
Niks
A desvantagem de um link simbólico é que ele existe dentro da árvore de trabalho, e se algum outro processo apaga a árvore de trabalho, então você perdeu a ligação simbólica
Jeff
Além disso, se o OP não queria um subdiretório .git em sua árvore de trabalho, por que ele desejaria um link simbólico?
Jeff
@ Jeff: para uma troca. (No seu caso de backup, acabar com sua árvore de trabalho provavelmente não é uma preocupação maior para ele do que acabar com qualquer outro diretório (como o próprio repositório).)
Sz.
1
Estou usando isso junto com o direnv para minhas anotações. Minha árvore de trabalho está em uma pasta no Dropbox e minha pasta git em algum lugar fora. Dessa forma, eu posso ter minhas alterações em todos os computadores facilmente, mas ainda assim posso verificar o que mudou e somente confirmar quando algo significativo mudar. Obrigado
Paulo Phagula 31/03/19
170

Você só precisa garantir que o repositório saiba onde está a árvore de trabalho e vice-versa.

Para que o repositório saiba onde está a árvore de trabalho, defina o valor da configuração core.worktree. Para que a árvore de trabalho saiba onde está o diretório git, adicione um arquivo chamado .git (não uma pasta!) E adicione uma linha como

gitdir: /path/to/repo.git

Desde o git 1.7.5, o comando init aprendeu uma opção extra para isso.

Você pode inicializar um novo repositório separado com

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

Isso inicializará o repositório git no diretório separado e adicionará o arquivo .git no diretório atual, que é o diretório de trabalho do novo repositório.

Anteriormente à 1.7.5, era necessário usar parâmetros ligeiramente diferentes e adicionar o arquivo .git.

Para inicializar um repositório separado, o seguinte comando vincula a árvore de trabalho ao repositório:

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

Seu diretório atual será a árvore de trabalho e o git usará o repositório em /path/to/repo.git. O comando init definirá automaticamente o core.worktreevalor conforme especificado com o --git-dirparâmetro

Você pode até adicionar um alias para isso:

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

Use o controle de versão git em um diretório de trabalho somente leitura

Com o conhecimento acima, você pode configurar o controle de versão git para um diretório ativo sem ter permissões de gravação. Se você usar --git-dirtodos os comandos git ou executar todos os comandos do repositório (em vez do diretório ativo), poderá deixar de fora o arquivo .git e, portanto, não precisará criar nenhum arquivo no diretório ativo. Veja também a resposta do Leos

niks
fonte
7
Você também pode fazer isso em um repositório existente: mova a pasta .git para onde quiser, adicione o arquivo .git para apontá-lo e, em seguida, continue usando o repositório como faria normalmente
joachim
Observe que você só pode emitir comandos git a partir da raiz do seu repositório. Entrar em subpastas confunde! (Isto acontece se ou não o valor dado para gitdir é relativo ou absoluto.)
Joachim
2
A correção para isso é especificar 'core.worktree' no arquivo de configuração real do git, ou seja, aquele na pasta que você aponta para o .git.
Joachim
2
Sim, foi o que descrevi na minha segunda frase da minha resposta. core.worktreeObviamente, o valor da configuração é armazenado no arquivo de configuração da pasta .git, para onde o arquivo .git aponta.
Niks
1
Descobri que, quando usei essas instruções, o repositório feito se tornou um repositório simples e deu o erro fatal: core.worktree and core.bare do not make sense. Parece que apenas alterar a configuração para que não fique vazio resolve isso.
9788 Steven Universo Lu
62

A --separate-git-diropção para git init(e git clone) pode ser usada para fazer isso na minha versão do git ( 1.7.11.3). A opção separa o repositório git da árvore de trabalho e cria um link simbólico independente do sistema de arquivos git (na forma de um arquivo chamado .git) na raiz da árvore de trabalho. Eu acho que o resultado é idêntico à resposta dos niks .

git init --separate-git-dir path/to/repo.git path/to/worktree
frigideira
fonte
2
+1 Usando uma opção de linha de comando para initsi parece mais limpo e mais clara
goncalopp
no Windows, repo.gité criado com seu conjunto de atributos hiden. Eu então mudo manualmente. Você sabe se isso é seguro?
PA.
+1 Sim, o git apoiou esta opção de linha de comando na versão 1.7.5, que não estava disponível na época (se bem me lembro). Atualizei minha resposta para sugerir o uso desse parâmetro.
Niks
25

Acho mais simples reverter os diretórios --work-treee --git-dirusados ​​na resposta do 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
        ...

Essa abordagem tem duas vantagens:

  • Isso elimina a necessidade de ter opções ou .gitarquivos de linha de comando . Você apenas opera normalmente de dentro da raiz do repositório.
  • Ele permite a versão de um sistema de arquivos, mesmo que você não o possua. O Git gravará apenas no local do repositório.

A única ressalva que encontrei é que, em vez de usar um .gitignorearquivo, você edita info/exclude.

Você pode usar o repositório read_only_repos/foocomo um controle remoto em seus próprios repositórios, mesmo que os arquivos originais não estejam sob controle de versão.

Leo
fonte
Bem, este é exatamente o mesmo comando. A única diferença que vejo é que você trocou a ordem dos argumentos --work-tree e --git-dir. E é claro que você não cria o arquivo .git no diretório de trabalho, pois não possui acesso de gravação. No entanto, o uso do controle de versão para um diretório sem acesso de gravação é um bom caso de uso. :-)
NIKS
3
Você entendeu. A chave é criar o repositório fora do diretório de trabalho.
26416 Leo
4
Eu gosto disso - ele me permite usar o git nos diretórios que estou compartilhando com o Dropbox, sem que os outros participantes saibam que o git está em uso.
Quentin Stafford-Fraser
19

É convencional nomear um diretório que seja um repositório git que tenha sua árvore de trabalho em um local incomum com uma extensão '.git', como um repositório vazio.

mkdir ../git_repos/myfiles.git

Se você tivesse fornecido a --work-treeopção no momento do init, isso configuraria automaticamente a core.worktreevariável de configuração, o que significa que o git saberá onde encontrar a árvore de trabalho depois de especificar o diretório git.

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

Mas você também pode definir essa variável após o fato.

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

Depois de fazer isso, o comando add deve funcionar como esperado.

git --git-dir=../git_repos/myfiles.git add foo
CB Bailey
fonte
Eu descobri que se você cdar para ../git_repos/myfiles.git primeiro, em vez de estar na árvore de trabalho real, 'git add foo' funcionará apenas e você não precisará especificar --git-dir all A Hora.
911 Steve Folly
1
É verdade, mas acho que a maioria das pessoas costuma trabalhar em sua árvore de trabalho, e não em seus repositórios. Obviamente, se você estiver usando uma árvore de trabalho desanexada, provavelmente estará fazendo algo 'especial' e poderá usar algumas macros para ajudar.
223 CB Bailey
6

Use gitdentro do repositório:

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

A partir de agora, você pode usar gitdentro do ./backup/git_repos/myfilesdiretório, sem definir nenhuma variável de ambiente ou parâmetros adicionais.

To1ne
fonte
Esta parece ser a melhor resposta, mas recebo a mensagem warning: core.bare and core.worktree do not make sense, isso significa que não funcionou?
hazrpg
1
Eu ainda acho que funciona, mas ele reclama de estar nu ao definir a árvore de trabalho. É por isso que eu estou definindo core.barea falsetarde.
To1ne 18/07/19
Ah! Isso faz mais sentido agora. Obrigado por isso.
hazrpg
1

Você pode criar um script "nodgit" (No Dot GIT) com 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

Você pode chamar nodgit no lugar do git e ele definirá as variáveis ​​conforme necessário, procurando um repositório git. Por exemplo, digamos que você tenha um repositório (vazio) em / usr / local / gits / __ home__foo_wibbles e você esteja em / home / foo / wibbles / one, ele encontrará o diretório de trabalho correto (/ home / foo / wibbles) e repositório .

Ah, você também pode usar "nodgit shell" para obter um shell com os vars corretos definidos, para que você possa usar comandos git antigos e simples.

Paul Hedderly
fonte
0

Supondo que seus myfilesdiretórios já existam e tenham algum conteúdo, você poderia conviver com isso:

cd ~/backup
git init
git add myfiles

O .gitdiretório estará dentro backup, não dentro myfiles.

Abie
fonte
Embora isso consiga consertar o que eu quero, prefiro apenas armazenar a pasta myfiles no git e nada mais.
Rory
Você pode manter uma seleção dos arquivos que deseja gitrastrear usando o .gitignorearquivo. Se você adicionar *e !myfilesa ele, apenas esse diretório será rastreado. Mas se você quer um repo separado para outro diretório, você teria um problema ...
To1ne
0

Eu crio scripts que parecem

~ / bin / barra-git:

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

É redundante usar --git_dir = $ GIT_DIR, mas me lembra que também posso definir variáveis ​​de ambiente fora do script.

O exemplo acima é para rastrear alterações locais nos arquivos do sistema cygwin.

Pode criar um desses scripts para qualquer projeto importante que precise disso - mas / sem /.git é o meu principal uso.

O acima é pequeno o suficiente para criar um apelido ou função de shell, se você eliminar a redundância.

Se eu fizer isso com bastante frequência, reviveu o espaço de trabalho para repositório de mapeamento de

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

cuja contraparte moderna mais próxima são os mapeamentos ou visualizações do Perforce , suportando checkouts parciais, bem como a não colocação de espaço de trabalho e repo.

Krazy Glew
fonte