我可以将.git 文件夹存储在我想要跟踪的文件之外吗?

我有一个不寻常的想法,使用 git 作为备份系统。假设我有一个目录。备份/myfiles,我想使用 git 对其进行备份。为了保持干净,我不想有一个。Git 目录,所以我想我可以创建。备份/git _ repos/myfiles。通过查看 Git 文档,我试着这样做:

$ 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

您可以看到我得到的错误消息。我做错了什么?

56788 次浏览

假设您的 myfiles目录已经存在并且包含一些内容,您是否可以接受以下方式:

cd ~/backup
git init
git add myfiles

.git目录将位于 backup中,而不是 myfiles中。

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

这将做你想做的事情,但是当你必须使用每个 git 命令来指定它的时候,显然会很糟糕。

您可以导出 GIT_WORK_TREE=.GIT_DIR=../backup,Git 将在每个命令中提取它们。但是,这只能让您轻松地在每个 shell 的单个存储库中工作。

我宁愿建议。Git 目录到其他地方,或者创建到。来自主备份目录的 git 目录。

通常将目录命名为 git 存储库,该存储库的工作树位于一个不寻常的位置,其中包含一个’。Git 的扩展,很像一个空的存储库。

mkdir ../git_repos/myfiles.git

如果您在 init 时提供了 --work-tree选项,那么这将自动设置 core.worktree配置变量,这意味着一旦您指定了 git 目录,git 将知道在哪里可以找到工作树。

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

但是您也可以在事后设置这个变量。

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

完成此操作后,add 命令应该能够按预期工作。

git --git-dir=../git_repos/myfiles.git add foo

您可以创建一个“ nodgit”脚本(No Dot GIT)

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

您可以调用 nodgit 代替 git,它将根据需要通过查找 git repo 来设置变量。例如,假设你在/usr/local/gits/_ _ home _ foo _ wibbles 中有一个(裸)回购,而你在/home/foo/wibbles/one 中,那么它就会找到正确的工作目录(/home/foo/wibbles)和回购。

哦,您还可以使用“ nodgit shell”来获得一个设置了正确 vars 的 shell,这样您就可以使用普通的老 git 命令了。

在回购协议中使用 git:

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

从现在开始,您可以在 ./backup/git_repos/myfiles目录中使用 git,而不需要设置任何环境变量或其他参数。

您只需要确保存储库知道工作树的位置,反之亦然。

要让存储库知道工作树的位置,请设置配置值 core.worktree。要让工作树知道它的 git 目录在哪里,添加一个名为。Git (不是文件夹!)然后加一行

gitdir: /path/to/repo.git

自从 git1.7.5以来,init 命令为此学习了一个额外的选项。

您可以使用以下方法初始化一个新的单独存储库

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

这将在单独的目录中初始化 git 存储库,并添加。工作目录中的 git 文件,这是新存储库的工作目录。

在1.7.5 之前,您必须使用稍微不同的参数并自己添加. git 文件。

为了初始化一个单独的存储库,以下命令将工作树与存储库链接起来:

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

你的工作目录将是工作树,git 将使用 /path/to/repo.git上的存储库。Init 命令将自动设置用 --git-dir参数指定的 core.worktree值。

你甚至可以为此添加一个别名:

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

在只读工作目录上使用 git 版本控制

有了以上知识,你甚至可以在没有写权限的情况下为工作目录设置 git 版本控制。如果对每个 git 命令都使用 --git-dir,或者从存储库中执行每个命令(而不是工作目录) ,则可以省略。Git 文件,因此不需要在工作目录中创建任何文件。参见 狮子座回答

git init(和 git clone)的 --separate-git-dir选项可以用来在我的 git 版本(1.7.11.3)上实现这一点。该选项将 git 存储库与工作树分离,并在工作树的根中创建一个与文件系统无关的 git 符号链接(以文件 .git的形式)。我想结果和 Niks 的回答是一样的。

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

我发现逆转 Niks 的回答是:中使用的 --work-tree--git-dir目录更简单

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

这种方法有两个优点:

  • 它消除了使用任何命令行选项或 .git文件的需要。您只需在存储库的根目录中正常操作即可。
  • 它允许您对文件系统进行版本控制,即使您并不拥有该文件系统。Git 只会写入存储库位置。

我遇到的唯一警告是,不使用 .gitignore文件,而是编辑 info/exclude

然后,即使原始文件不受版本控制,您也可以在自己的存储库中将存储库 read_only_repos/foo作为远程使用。

我创建的脚本看起来像

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

使用—— GIT _ dir = $GIT _ DIR 是多余的,但是提醒我,我也可以在脚本之外设置环境变量。

上面的示例用于跟踪对 cygwin 系统文件的本地更改。

可以为任何需要它的主要项目制作一个这样的脚本-但是/没有/。Git 是我的主要用途。

如果消除冗余,上面的代码足够小,可以创建一个 shell 别名或函数。

如果我这样做的次数足够多,我将恢复工作空间到

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

其最接近的现代对应物是 迫使映射或视图,支持部分签出以及工作空间和回购的非同位。

为了澄清下面两个选项之间的区别,您可以告诉您的 git 存储库跟踪其他地方的文件(core.worktree) ,或者您可以在主工作树中放置一个面包屑,它指向其他地方的 git 存储库文件夹(--separate-git-dir)。core.worktree选项不需要面包屑文件,并且自从第一次发布以来就是 git 的一个特性。第二个选项比较新,需要一个面包屑文件,但不需要更改存储库配置文件。

选项1: core.worktree

在要跟踪的路径之外初始化一个非裸库,并将 core.worktree设置为要跟踪的路径。您可以使用终端命令来设置这个值,或者直接编辑存储库配置文件来添加:

worktree = <path to files to backup>

不要使存储库文件夹成为此路径的子文件夹; 这将是递归的。您可以尝试这样做,并简单地忽略存储库文件夹,但我认为 git 不会允许这种情况。

在这种情况下,您可以转到 backup/git_repos/运行 init命令,并使用 --git-dir=./myfiles选项覆盖默认的存储库文件夹名称。命令应该是这样的:

cd backup/git_repos
git init --git-dir=./myfiles
git config core.worktree backup/myfiles

注意1: 这不同于较新的 附加条款工作树 Git 功能。这是所有非裸库使用的主要工作树。这些额外的工作树将面包屑用于 git 存储库文件夹的子文件夹,非常类似于下面的选项2。

注意2: 我最近测试了很多用于窗口的 Git GUI,只有 Git 扩展支持使用 core.worktree 移动主工作树。

不支持 core.worktree 的 GUI

SourceTree,Fork,Tower,GitKraken,GitHub Desktop,GitAhead,SmartGit * 和 Git-Cola。在使用 core.worktree 时,您需要坚持使用终端。

* SmartGit 将此特性与选项2冲突,并要求提供 .git文件。这对于 core.worktree 不是必需的。

备选案文2: 分开-git-dir

使用 --separate-git-dir=<path to hold repository data>在要备份的路径上初始化存储库。这将使用指定的路径来保存存储库数据,并在初始化位置创建一个 .git文件,其中包含如下一行:

gitdir: <path to hold repository data>

对于您来说,命令应该是这样的:

cd backup/myfiles
git init --separate-git-dir=backup/git_repos/myfiles/

backup/myfiles/中的 .git文件将包含 gitdir: backup/git_repos/myfiles/

现在操作 git 将 .git文件的位置视为存储库的位置。