Bisakah saya menyimpan folder .git di luar file yang ingin saya lacak?

147

Saya punya ide yang tidak biasa untuk menggunakan git sebagai sistem cadangan. Jadi katakanlah saya memiliki direktori ./backup/myfiles dan saya ingin mendukungnya menggunakan git. Agar semuanya tetap bersih, saya tidak ingin memiliki direktori .git di folder myfiles, jadi saya pikir saya bisa membuat ./backup/git_repos/myfiles. Dari melihat dokumen git, saya sudah mencoba melakukan ini:

$ 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

Anda dapat melihat pesan kesalahan yang saya dapatkan di sana. Apa yang saya lakukan salah?

Rory
sumber
9
Seperti halnya ide cadangan Anda, ini juga dapat digunakan untuk menjaga "dotfiles" Anda (.bashrc, .vimrc, dll) di direktori home sambil menyimpan folder .git di tempat lain.
Philip
6
Jawaban paling lurus: stackoverflow.com/a/19548676/170352 (terkubur karena upvotes lama)
Brandon Bertelsen
1
Dalam kasus bahwa Anda tidak memiliki akses tulis atau tidak ingin membuat perubahan ke direktori kerja (seperti menambahkan git / dll), ini jawaban di bawah ini dengan Leo (juga terkubur oleh upvotes tua) adalah yang terbaik.
KobeJohn
1
@ Pilip, kecuali repositori dotfiles Anda juga mengandung submit Git. Git tidak mendukung submodula dalam kombinasi dengan pohon kerja eksternal.
maxschlepzig

Jawaban:

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

Ini akan melakukan apa yang Anda inginkan tetapi jelas akan menyedot ketika Anda harus menentukannya dengan setiap perintah git yang pernah Anda gunakan.

Anda dapat mengekspor GIT_WORK_TREE=.dan GIT_DIR=../backupdan Git akan mengambilnya pada setiap perintah. Itu hanya akan memungkinkan Anda untuk bekerja dalam satu repositori per shell.

Saya lebih suka menyarankan symlinking direktori .git ke tempat lain, atau membuat symlink ke direktori .git dari direktori cadangan utama Anda.

Bombe
sumber
1
Anda dapat mengarsipkan hal yang sama tanpa menentukan git-dir dan work-tree di setiap perintah dan tanpa tautan simbolis. Lihat jawaban saya.
niks
Kelemahan dari symlink adalah bahwa ia ada di dalam pohon kerja, dan jika beberapa proses lain menghapus pohon kerja, maka Anda telah kehilangan symlink
Jeff
Lebih jauh, jika OP tidak menginginkan subdirektori .git di pohon kerjanya, mengapa ia menginginkan symlink?
Jeff
@ Jeff: untuk trade-off. (Dalam kasus cadangannya, memusnahkan pohon kerjanya mungkin bukan masalah yang lebih besar baginya daripada memusnahkan dir lain (seperti repo itu sendiri).)
Sz.
1
Saya menggunakan ini bersama dengan direnv untuk catatan saya. Pohon kerja saya ada di folder di Dropbox dan folder git saya di luar. Dengan cara ini saya dapat memiliki perubahan pada semua komputer dengan mudah tetapi masih dapat memeriksa apa yang berubah dan hanya melakukan ketika sesuatu yang signifikan telah berubah. Terima kasih
Paulo Phagula
170

Anda hanya perlu memastikan bahwa repositori tahu di mana pohon kerja berada dan sebaliknya.

Untuk membiarkan repositori tahu di mana pohon kerja berada, setel nilai konfigurasi core.worktree. Untuk membiarkan pohon pekerjaan tahu di mana direktori git berada, tambahkan file bernama .git (bukan folder!) Dan tambahkan baris seperti

gitdir: /path/to/repo.git

Sejak git 1.7.5 perintah init mempelajari opsi tambahan untuk ini.

Anda dapat menginisialisasi repositori terpisah yang baru dengan

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

Ini akan menginisialisasi repositori git di direktori yang terpisah dan menambahkan file .git di direktori saat ini, yang merupakan direktori kerja repositori baru.

Sebelumnya ke 1.7.5 Anda harus menggunakan parameter yang sedikit berbeda dan menambahkan file .git sendiri.

Untuk menginisialisasi repositori terpisah, perintah berikut menautkan work-tree dengan repositori:

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

Direktori Anda saat ini adalah pohon kerja dan git akan menggunakan repositori di /path/to/repo.git. Perintah init secara otomatis akan menetapkan core.worktreenilai seperti yang ditentukan dengan --git-dirparameter.

Anda bahkan dapat menambahkan alias untuk ini:

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

Gunakan kontrol versi git pada direktori yang berfungsi hanya baca

Dengan pengetahuan di atas, Anda bahkan dapat mengatur kontrol versi git untuk direktori yang berfungsi tanpa memiliki izin menulis. Jika Anda menggunakan --git-dirsetiap perintah git atau mengeksekusi setiap perintah dari dalam repositori (alih-alih direktori kerja), Anda dapat meninggalkan file .git dan karenanya tidak perlu membuat file apa pun di dalam direktori kerja. Lihat juga jawaban Leo

niks
sumber
7
Anda dapat melakukan ini pada repo yang ada juga: pindahkan folder .git ke mana pun Anda inginkan, tambahkan file .git untuk mengarahkannya, dan kemudian Anda bisa melanjutkan menggunakan repo seperti biasanya
joachim
Perhatikan bahwa Anda hanya dapat mengeluarkan perintah git dari root repo Anda. Pergi ke subfolder membingungkannya! (Ini terjadi apakah nilai yang diberikan untuk gitdir adalah relatif atau absolut.)
joachim
2
Perbaikan untuk ini adalah untuk menentukan 'core.worktree' di file konfigurasi git yang sebenarnya, yaitu yang ada di folder yang Anda tuju di .git.
joachim
2
Ya, itulah yang saya jelaskan dalam kalimat kedua dari jawaban saya. Nilai konfigurasi core.worktreetentu saja disimpan dalam file konfigurasi folder .git, di mana file .git menunjuk ke.
niks
1
Saya menemukan ketika saya menggunakan instruksi ini repo yang dibuat menjadi repositori kosong dan memberikan kesalahan fatal: core.worktree and core.bare do not make sense. Sepertinya hanya mengubah konfigurasi sehingga tidak menyelesaikan itu.
Steven Lu
62

The --separate-git-dirpilihan untuk git init(dan git clone) dapat digunakan untuk mencapai hal ini pada versi saya git ( 1.7.11.3). Opsi memisahkan repositori git dari pohon kerja dan membuat tautan simbolis agnostik sistem berkas agnostik (dalam bentuk file bernama .git) di akar pohon kerja. Saya pikir hasilnya identik dengan jawaban niks .

git init --separate-git-dir path/to/repo.git path/to/worktree
Penggorengan
sumber
2
+1 Menggunakan opsi baris perintah untuk initdirinya sendiri terlihat lebih jelas dan bersih
goncalopp
di windows, repo.gitdibuat dengan set atribut hiden-nya. Saya kemudian mengubahnya secara manual. Apakah Anda tahu kalau ini aman?
PA.
+1 Ya git mencondongkan opsi baris perintah ini di versi 1.7.5, yang saat itu tidak tersedia (jika saya ingat benar). Saya telah memperbarui jawaban saya untuk menyarankan agar menggunakan parameter ini.
niks
25

Saya merasa lebih mudah untuk membalikkan --work-treedan --git-dirdirektori yang digunakan dalam jawaban 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
        ...

Pendekatan ini memiliki dua keunggulan:

  • Ini menghapus kebutuhan untuk memiliki opsi atau .gitfile baris perintah . Anda hanya beroperasi secara normal dari dalam root repositori.
  • Ini memungkinkan Anda untuk membuat versi sistem file bahkan jika Anda tidak memilikinya. Git hanya akan menulis ke lokasi repositori.

Satu-satunya peringatan yang saya temui adalah bahwa alih-alih menggunakan .gitignorefile, Anda mengedit info/exclude.

Anda kemudian dapat menggunakan repositori read_only_repos/foosebagai remote di repositori Anda sendiri bahkan jika file asli tidak di bawah kontrol versi.

Leo
sumber
Nah ini perintah yang persis sama. Satu-satunya perbedaan yang saya lihat adalah Anda mengganti urutan argumen --work-tree dan --git-dir. Dan tentu saja Anda tidak membuat file .git di direktori kerja karena Anda tidak memiliki akses tulis untuk itu. Namun demikian, menggunakan kontrol versi untuk direktori tanpa akses tulis ke dalamnya adalah kasus penggunaan yang bagus. :-)
niks
3
Kamu mendapatkannya. Kuncinya adalah membuat repo di luar direktori kerja.
Leo
4
Saya suka ini - ini memungkinkan saya untuk menggunakan git di direktori yang saya bagikan dengan Dropbox, tanpa peserta lain tahu bahwa git sedang digunakan.
Quentin Stafford-Fraser
19

Itu konvensional untuk menamai direktori yang merupakan repositori git yang memiliki pohon kerjanya di tempat yang tidak biasa dengan ekstensi '.git', seperti repositori kosong.

mkdir ../git_repos/myfiles.git

Jika Anda telah memberikan --work-treeopsi pada waktu init maka ini akan secara otomatis mengatur core.worktreevariabel config yang berarti bahwa git akan tahu di mana menemukan pohon yang berfungsi setelah Anda menentukan direktori git.

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

Tetapi Anda dapat mengatur variabel ini setelah fakta juga.

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

Setelah Anda selesai melakukannya, perintah add harus bekerja seperti yang diharapkan.

git --git-dir=../git_repos/myfiles.git add foo
CB Bailey
sumber
Saya telah menemukan jika Anda cd ke ../git_repos/myfiles.git pertama, daripada berada di pohon kerja nyata, 'git add foo' hanya akan bekerja, dan Anda tidak perlu menentukan --git-dir semua waktu.
Steve Folly
1
Memang benar, tetapi saya berpikir bahwa kebanyakan orang cenderung bekerja di pohon kerja mereka daripada repositori mereka. Tentu saja, jika Anda menggunakan pohon kerja yang terpisah Anda mungkin melakukan sesuatu yang 'istimewa' dan mungkin menggunakan beberapa makro untuk membantu.
CB Bailey
6

Gunakan gitdi dalam repo:

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

Mulai sekarang, Anda dapat menggunakan gitdi dalam ./backup/git_repos/myfilesdirektori, tanpa mengatur variabel lingkungan atau parameter tambahan apa pun.

To1ne
sumber
Ini sepertinya jawaban terbaik, tetapi saya mendapatkan pesan warning: core.bare and core.worktree do not make senseapakah itu berarti tidak berhasil?
hazrpg
1
Saya masih berpikir itu bekerja, tetapi mengeluh tentang telanjang saat mengatur meja kerja. Itu sebabnya saya mengatur core.bareuntuk falsesesudahnya.
To1ne
Ah! Itu lebih masuk akal sekarang. Terima kasih untuk itu.
hazrpg
1

Anda dapat membuat skrip "nodgit" (No Dot GIT) dengan semacamnya

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

Anda dapat memanggil nodgit sebagai ganti git dan itu akan mengatur variabel seperlunya dengan mencari repo git. Misalnya katakan Anda memiliki repo (telanjang) di / usr / local / gits / __ home__foo_wibbles dan Anda berada di / home / foo / wibbles / satu maka ia akan menemukan direktori kerja yang benar (/ home / foo / wibbles) dan repo .

Oh, Anda juga dapat menggunakan "shell nodgit" untuk mendapatkan shell dengan vars yang benar diatur sehingga Anda dapat menggunakan perintah git tua biasa.

Paul Hedderly
sumber
0

Dengan asumsi myfilesdirektori Anda sudah ada dan memiliki beberapa konten, dapatkah Anda hidup dengan ini:

cd ~/backup
git init
git add myfiles

The .gitdirektori akan di backup, tidak myfiles.

Abie
sumber
Walaupun itu akan memperbaiki apa yang saya inginkan, saya lebih suka hanya menyimpan folder myfiles di bawah git, dan tidak ada yang lain.
Rory
Anda dapat menyimpan pilihan file yang ingin Anda gitlacak menggunakan .gitignorefile tersebut. Jika Anda ingin menambahkan *dan !myfilesmelakukannya, hanya direktori itu yang akan dilacak. Tetapi jika Anda ingin repo terpisah untuk direktori lain, Anda akan memiliki masalah ...
To1ne
0

Saya membuat skrip yang terlihat seperti

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

Itu berlebihan untuk menggunakan --git_dir = $ GIT_DIR, tetapi mengingatkan saya bahwa saya juga dapat mengatur variabel lingkungan di luar skrip.

Contoh di atas adalah untuk melacak perubahan lokal ke file sistem cygwin.

Dapat membuat satu skrip untuk proyek besar apa pun yang membutuhkan ini - tetapi / tanpa /.git adalah penggunaan utama saya.

Di atas cukup kecil untuk membuat alias shell atau fungsi, jika Anda menghilangkan redundansi.

Jika saya cukup sering melakukan ini, saya akan menghidupkan kembali ruang kerja untuk pemetaan repositori

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

mitra modern terdekatnya adalah pemetaan atau tampilan Perforce , mendukung checkout sebagian, serta non-colokasi ruang kerja dan repo.

Krazy Glew
sumber