Czy mogę przechowywać folder .git poza plikami, które chcę śledzić?

147

Mam niezwykły pomysł, aby użyć gita jako systemu zapasowego. Powiedzmy, że mam katalog ./backup/myfiles i chcę utworzyć jego kopię zapasową za pomocą git. Aby zachować porządek, nie chcę mieć katalogu .git w folderze myfiles, więc pomyślałem, że mogę utworzyć ./backup/git_repos/myfiles. Patrząc na dokumenty git, próbowałem zrobić to:

$ 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

Możesz zobaczyć komunikat o błędzie, że tam dotarłem. Co ja robię źle?

Rory
źródło
9
Oprócz pomysłu na tworzenie kopii zapasowych, można to również wykorzystać do przechowywania plików „dotfiles” (.bashrc, .vimrc itp.) W katalogu domowym, podczas gdy folder .git w innym miejscu.
Philip
6
Najbardziej prosta odpowiedź: stackoverflow.com/a/19548676/170352 (zakopane z powodu starych głosów)
Brandon Bertelsen
1
W przypadku, gdy nie masz uprawnień do zapisu lub nie chcesz wprowadzać żadnych zmian w katalogu roboczym (np. Dodawanie .git / itp.), Ta odpowiedź poniżej przez Leo (również pogrzebana przez stare upvotes) jest najlepsza.
KobeJohn
1
@Philip, chyba że repozytorium dotfiles zawiera również moduły podrzędne Git. Git nie obsługuje modułów podrzędnych w połączeniu z zewnętrznym drzewem roboczym.
maxschlepzig

Odpowiedzi:

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

Zrobi to, co chcesz, ale oczywiście będzie do niczego, jeśli będziesz musiał to określić przy każdym poleceniu git, którego kiedykolwiek użyjesz.

Możesz eksportować, GIT_WORK_TREE=.a GIT_DIR=../backupGit odbierze je przy każdym poleceniu. Pozwoli to jednak wygodnie pracować tylko w jednym repozytorium na powłokę.

Wolałbym raczej zasugerować dowiązanie symboliczne katalogu .git do innego miejsca lub utworzenie dowiązania symbolicznego do katalogu .git z głównego katalogu kopii zapasowej.

Bombe
źródło
1
Możesz zarchiwizować to samo bez określania katalogu git-dir i drzewa roboczego w każdym poleceniu i bez żadnych dowiązań symbolicznych. Zobacz moją odpowiedź.
niks
Wadą linku symbolicznego jest to, że istnieje w drzewie roboczym, a jeśli jakiś inny proces wymazuje drzewo robocze, oznacza to, że straciłeś dowiązanie symboliczne
Jeff
Ponadto, jeśli OP nie chciałby mieć podkatalogu .git w swoim drzewie roboczym, po co miałby chcieć dowiązania symbolicznego?
Jeff
@Jeff: za kompromis. (W przypadku kopii zapasowej wyczyszczenie jego drzewa roboczego prawdopodobnie nie jest dla niego większym zmartwieniem niż wymazanie jakiegokolwiek innego katalogu (np. Samego repozytorium).)
Sz.
1
Używam tego razem z direnv do moich notatek. Moje drzewo robocze znajduje się w folderze w Dropbox, a mój folder git gdzieś na zewnątrz. W ten sposób mogę łatwo wprowadzać zmiany na wszystkich komputerach, ale nadal mogę sprawdzić, co się zmieniło i zatwierdzić tylko wtedy, gdy zmieni się coś ważnego. Dzięki
Paulo Phagula
170

Musisz tylko upewnić się, że repozytorium wie, gdzie znajduje się drzewo robocze i odwrotnie.

Aby repozytorium wiedziało, gdzie znajduje się drzewo robocze, ustaw wartość konfiguracyjną core.worktree. Aby powiadomić drzewo robocze, gdzie jest katalog git, dodaj plik o nazwie .git (nie folder!) I dodaj linię taką jak

gitdir: /path/to/repo.git

Od wersji 1.7.5 git polecenie init nauczyło się dodatkowej opcji do tego.

Możesz zainicjować nowe oddzielne repozytorium za pomocą

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

Spowoduje to zainicjowanie repozytorium git w oddzielnym katalogu i dodanie pliku .git do bieżącego katalogu, który jest katalogiem roboczym nowego repozytorium.

Wcześniej w wersji 1.7.5 trzeba było używać nieco innych parametrów i samodzielnie dodawać plik .git.

Aby zainicjować oddzielne repozytorium, następujące polecenie łączy drzewo robocze z repozytorium:

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

Twój bieżący katalog będzie drzewem roboczym, a git użyje repozytorium pod adresem /path/to/repo.git. Polecenie init automatycznie ustawi core.worktreewartość określoną w --git-dirparametrze.

Możesz nawet dodać do tego alias:

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

Użyj kontroli wersji git w katalogu roboczym tylko do odczytu

Mając powyższą wiedzę, możesz nawet skonfigurować kontrolę wersji git dla katalogu roboczego bez posiadania uprawnień do zapisu. Jeśli używasz --git-dirprzy każdym poleceniu git lub wykonujesz każde polecenie z repozytorium (zamiast katalogu roboczego), możesz pominąć plik .git i dlatego nie musisz tworzyć żadnych plików w katalogu roboczym. Zobacz także odpowiedź Leosa

niks
źródło
7
Możesz to zrobić również w istniejącym repozytorium: przenieś folder .git w dowolne miejsce, dodaj plik .git, aby go wskazać, a następnie możesz po prostu kontynuować korzystanie z repozytorium tak, jak zwykle
joachim
Zauważ, że możesz wydawać polecenia git tylko z katalogu głównego repozytorium. Przechodzenie do podfolderów dezorientuje to! (Dzieje się tak niezależnie od tego, czy podana wartość gitdir jest względna czy bezwzględna).
joachim
2
Rozwiązaniem tego problemu jest określenie „core.worktree” w aktualnym pliku konfiguracyjnym git, tj. Tym w folderze wskazanym w .git.
joachim
2
Tak, to właśnie opisałem w drugim zdaniu mojej odpowiedzi. Wartość konfiguracyjna core.worktreejest oczywiście przechowywana w pliku konfiguracyjnym folderu .git, na który wskazuje plik .git.
niks
1
Odkryłem, że kiedy użyłem tych instrukcji, tworzenie repozytorium stało się czystym repozytorium i powoduje błąd fatal: core.worktree and core.bare do not make sense. Wygląda na to, że zmiana konfiguracji, aby nie była pusta, rozwiązuje ten problem.
Steven Lu
62

W mojej wersji git ( ) można to osiągnąć --separate-git-dirza pomocą opcji git init(i ). Opcja oddziela repozytorium git od drzewa roboczego i tworzy dowiązanie symboliczne git niezależne od systemu plików (w postaci pliku o nazwie ) w katalogu głównym drzewa roboczego. Myślę, że wynik jest identyczny z odpowiedzią ników .git clone1.7.11.3.git

git init --separate-git-dir path/to/repo.git path/to/worktree
Frytkownica
źródło
2
+1 initSamo użycie opcji wiersza poleceń wygląda jaśniej i czyściej
goncalopp
w repo.gitsystemie Windows jest tworzony z ustawionym atrybutem Hiden. Następnie zmieniam go ręcznie. Czy wiesz, czy to jest bezpieczne?
PA.
+1 Tak, git oparł tę opcję wiersza poleceń w wersji 1.7.5, która nie była wtedy dostępna (jeśli dobrze pamiętam). Zaktualizowałem moją odpowiedź, aby zasugerować użycie tego parametru.
niks
25

Uważam, że łatwiej jest odwrócić katalogi --work-treei --git-dirużywane w odpowiedzi niksa:

$ 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
        ...

Takie podejście ma dwie zalety:

  • Eliminuje potrzebę posiadania jakichkolwiek opcji lub .gitplików wiersza poleceń . Po prostu działasz normalnie z poziomu katalogu głównego repozytorium.
  • Pozwala na wersję systemu plików, nawet jeśli nie jesteś jego właścicielem. Git zapisze tylko w lokalizacji repozytorium.

Jedynym zastrzeżeniem, jakie napotkałem, jest to, że zamiast używać .gitignorepliku, edytujesz info/exclude.

Możesz następnie używać repozytorium read_only_repos/foojako zdalnego we własnych repozytoriach, nawet jeśli oryginalne pliki nie są pod kontrolą wersji.

Lew
źródło
To jest dokładnie to samo polecenie. Jedyną różnicą, jaką widzę, jest zamiana kolejności argumentów --work-tree i --git-dir. Oczywiście nie tworzysz pliku .git w katalogu roboczym, ponieważ nie masz do niego dostępu do zapisu. Niemniej jednak, używanie kontroli wersji dla katalogu bez prawa do zapisu jest dobrym przykładem użycia. :-)
niks
3
Masz to. Kluczem jest utworzenie repozytorium poza katalogiem roboczym.
Leo
4
Podoba mi się to - pozwala mi używać git w katalogach, które udostępniam Dropbox, bez wiedzy innych uczestników, nawet że git jest używany.
Quentin Stafford-Fraser
19

Zwykle nazywa się katalog będący repozytorium git, które ma swoje drzewo robocze w nietypowym miejscu z rozszerzeniem „.git”, podobnie jak zwykłe repozytorium.

mkdir ../git_repos/myfiles.git

Gdybyś podał tę --work-treeopcję w czasie inicjalizacji, to automatycznie core.worktreeustawiłoby to zmienną konfiguracyjną, co oznacza, że ​​git będzie wiedział, gdzie znaleźć drzewo robocze po określeniu katalogu git.

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

Ale możesz ustawić tę zmienną również po fakcie.

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

Gdy to zrobisz, polecenie add powinno działać zgodnie z oczekiwaniami.

git --git-dir=../git_repos/myfiles.git add foo
CB Bailey
źródło
Odkryłem, że jeśli najpierw udasz się do ../git_repos/myfiles.git, zamiast znajdować się w prawdziwym drzewie roboczym, 'git add foo' po prostu zadziała i nie musisz określać --git-dir all czas.
Steve Folly
1
To prawda, ale myślę, że większość ludzi ma tendencję do pracy w swoim drzewie roboczym, a nie w repozytoriach. Oczywiście, jeśli używasz odłączonego drzewa roboczego, prawdopodobnie robisz coś „specjalnego” i być może używasz do pomocy makr.
CB Bailey,
6

Użyj gitwewnątrz repozytorium:

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

Od teraz możesz używać gitwewnątrz ./backup/git_repos/myfileskatalogu, bez ustawiania jakichkolwiek zmiennych środowiskowych czy dodatkowych parametrów.

To1ne
źródło
Wydaje się, że to najlepsza odpowiedź, ale otrzymuję wiadomość, warning: core.bare and core.worktree do not make senseczy to oznacza, że ​​nie zadziałało?
hazrpg
1
Nadal uważam, że to działa, ale narzeka, że ​​jest odsłonięty podczas ustawiania drzewa roboczego. Dlatego siadam core.barena falsepóźniej.
To1ne
Ach! To ma teraz więcej sensu. Dziękuję za to.
hazrpg
1

Możesz stworzyć skrypt „nodgit” (bez kropki GIT) z czymś w rodzaju

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

Możesz wywołać nodgit zamiast git, który ustawi zmienne w razie potrzeby, szukając repozytorium git. Na przykład, powiedzmy, że masz (nagie) repozytorium w / usr / local / gits / __ home__foo_wibbles i jesteś w / home / foo / wibbles / one, wtedy znajdzie on właściwy katalog roboczy (/ home / foo / wibbles) i repozytorium .

Aha, możesz także użyć "powłoki nodgit", aby uzyskać powłokę z poprawnymi ustawionymi zmiennymi, dzięki czemu możesz używać zwykłych starych poleceń git.

Paul Hedderly
źródło
0

Zakładając, że Twoje myfileskatalogi już istnieją i zawierają treści, czy możesz z tym żyć:

cd ~/backup
git init
git add myfiles

.gitKatalog będzie backup, nie myfiles.

Abie
źródło
Chociaż to trochę naprawiłoby to, czego chcę, wolałbym po prostu przechowywać folder myfiles w git i nic więcej.
Rory
Możesz zachować wybór plików, które chcesz gitśledzić za pomocą .gitignorepliku. Jeśli dodasz *i !myfilesdo niego, tylko ten katalog będzie śledzony. Ale jeśli chcesz osobne repozytorium dla innego katalogu, miałbyś problem ...
To1ne
0

Tworzę skrypty, które wyglądają

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

Używanie --git_dir = $ GIT_DIR jest zbędne, ale przypomina mi, że mogę ustawić zmienne środowiskowe również poza skryptem.

Powyższy przykład służy do śledzenia lokalnych zmian w plikach systemowych cygwin.

Potrafię stworzyć jeden taki skrypt dla każdego dużego projektu, który tego potrzebuje - ale / bez /.git jest moim głównym zastosowaniem.

Powyższe jest wystarczająco małe, aby utworzyć alias lub funkcję powłoki, jeśli wyeliminujesz nadmiarowość.

Gdybym robił to wystarczająco często, przywróciłbym obszar roboczy do mapowania repozytorium

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

którego najbliższym współczesnym odpowiednikiem są mapowania lub widoki Perforce , obsługujące częściowe kasy, a także niekolokację obszaru roboczego i repozytorium.

Krazy Glew
źródło