6

我正在使用 PHP 在 PHP 中运行 Git,exec以获取有关一组服务器仪表板的一些 Git 项目的一些信息。我遇到了一些奇怪的输出,这让我怀疑我是否误解了“工作树”是什么。

如果我使用此命令,将%ssprintf 参数替换为 Git 项目的路径:

git --work-tree=%s status

然后我得到这个输出:

On branch server-dashboard
Your branch is ahead of 'origin/server-dashboard' by 1 commit.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   .gitignore
    deleted:    README.md
    deleted:    common.php
    deleted:    conf/dev/settings.ini
    deleted:    conf/prod/settings.ini
    deleted:    conf/settings.ini.example
    deleted:    lib/HealthSettings.php
    deleted:    lib/Settings.php
    deleted:    public/assets/main.css
    deleted:    public/assets/main.js
    deleted:    public/assets/refresh.gif
    deleted:    public/assets/spinner.gif
    deleted:    public/curl-test.php
    deleted:    public/dashboard.php
    deleted:    public/iframes.php
    modified:   public/index.php
    deleted:    public/info.php
    deleted:    public/no-servers.php
    deleted:    public/sections/apache-mods.php
    deleted:    public/sections/curl-headers.php
    deleted:    public/sections/curl-self.php
    deleted:    public/sections/database.php
    deleted:    public/sections/env.php
    deleted:    public/sections/git-table.php
    deleted:    public/sections/git.php
    deleted:    public/sections/php-exts.php
    deleted:    public/sections/php-proxy.php
    deleted:    public/sections/tableau-proxy-table.php
    deleted:    public/sections/tableau-proxy.php
    deleted:    public/sections/user.php
    deleted:    public/tabs.php

git status这不是我所期望的,因为如果我在控制台上运行,这不是我得到的。

现在,如果我在 PHP 中运行此命令(再次将字符串参数交换为路径):

cd %s && git status

然后我得到正确的输出:

On branch master
Your branch is ahead of 'origin/master' by 17 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

我猜测,由于这个项目是在 master 后面 17 次提交,因此“错误”输出实际上是对这些提交发生变化的表达。但是,我原以为这两个命令是等效的。我错了吗,如果是这样,我可以无需cd先以编程方式运行 Git 命令吗?

4

2 回答 2

4

和之间存在许多实质性差异。根据附加参数和/或环境变量,可以使这些差异中的一些或全部消失。cd path; git commandgit --work-tree=path command

重要的是要意识到 Git 几乎始终必须使用三个(不仅仅是两个)关键项目。这些都是:

  • 存储库本身(名称到哈希 ID 对的存储库数据库,例如master表示提交e3331758f12da22f4103eec7efe1b5304a9be5e9或任何其他哈希 ID,加上 ID 是那个大而丑陋的哈希 ID 字符串的对象)。.git存储库通常位于工作树顶层命名的目录中。这是git 目录( $GIT_DIR)。

  • index ,它索引和缓存(因此它的两个名称index或有时cache)工作树,并充当更新文件的存储位置(因此它的第三个名称staging area)(实际上是 blob 哈希 ID 转换的路径名)当你打算构建一个新的提交时。索引主要是一个文件:.git/index. 从这个路径名可以看出,默认情况下,索引文件位于存储库中。但是,它有自己独立的控制变量$GIT_INDEX_FILE. 它只是默认$GIT_DIR/index.

  • 工作树以未压缩格式保存文件。文件通过从提交中提取到索引中,然后从索引(它们仍然被压缩并以仅 Git 格式)提取到工作树中,从而进入工作树工作树还可能包含在索引中找不到的其他文件。此类文件是未暂存的。未暂存的文件可能会或可能不会被忽略(暂存文件,即路径名出现在索引中的文件,根据定义永远不会被忽略)。

工作树通常只是当前工作目录,或者从当前工作目录通过向上走(..,然后../..,等等)找到包含.git存储库目录的第一个位置。这意味着从您登陆的任何地方开始搜索工作树。cd path; git ...

如果没有覆盖,找到工作树并因此找到.git目录,Git 现在知道在哪里$GIT_DIR以及在哪里可以找到索引文件。但是,如果您提供覆盖,使用或通过设置环境变量,Git 将在那里查找工作树,并在当前目录(或然后等等)中查找存储库目录。git --work-tree=path$GIT_WORK_TREE..../..

如果您提供覆盖或设置环境变量,Git 将在那里查找存储库目录,而不管工作树的任何设置或缺少设置。--git-dir=path$GIT_DIR

(注意:--git-dir并且--work-tree实际上是通过让git前端设置环境变量来实现的。因此,如果您同时设置了两个,则 flag 参数会在 Git 命令期间覆盖环境设置,包括 Git 本身运行的任何子进程。)

如果您通过环境提供$GIT_INDEX_FILE覆盖,Git 将在那里查找索引文件,无论$GIT_DIR.

这些设置中的任何一个都可以是绝对路径——从类Unix/系统开始,或者在更简单的系统上使用驱动器号——或相对路径。绝对路径覆盖当前工作目录,而相对路径从当前工作目录开始。

因此,任何这些参数或环境变量的确切内容都非常重要。例如,运行:

cd $HOME/foo; GIT_INDEX_FILE=$HOME/index git --git-dir=sub/.git --work-tree=/tmp ...

将导致 Git 在 中查找存储库$HOME/foo/sub/.git、在 中查找索引文件$HOME/index以及在/tmp中查找工作树。

除此之外,前端git命令还允许一个-C参数或多个-C参数。这些中的每一个都使 Git 执行cd到提供的路径。因此,上述内容在很大程度上等同于:

GIT_INDEX_FILE=$HOME/index git -C $HOME/foo --git-dir=sub/.git --work-tree=/tmp ...

除了上述命令终止后,您的 shell / 命令解释器将保留在运行命令之前的任何工作目录中。(我相信,这些细节在 Windows 上略有不同,因为 Windows 对“当前目录”与“当前驱动器号”或类似的东西做出了一些非常奇怪的假设。)


在您的特定情况下 - 运行 - 请git status注意进行git status两个单独的比较:

  • 首先,它将当前提交(通过 找到git diff)与. 这里的任何不同之处都是为 commit 上演的$GIT_DIR/HEAD$GIT_INDEX_FILE
  • 然后它将git diff索引文件的内容与$GIT_WORK_TREE. 这里的任何不同之处都不是为 commit 上演的

领先和/或落后计数来自较早的步骤,其中git status使用当前分支(再次来自$GIT_DIR/HEAD)。通常,此HEAD文件是一个符号引用,包含当前分支的名称。然后 Git 可以找到该分支的上游设置(git rev-parse --symbolic-full-name $branch@{upstream}或多或少,尽管--abbrev-ref更适合人类):master通常具有refs/remotes/origin/master作为其上游。然后,Git 读取从 中提取的提交图,$GIT_DIR以发现前面和/或后面有多少提交master是 vs origin/master


以上不是全部。查阅git 前端命令文档以查找可用于覆盖特定项目的更完整的环境变量列表。例如,GIT_ALTERNATE_OBJECT_DIRECTORIES可用于使 Git 在存储库本身之外查找其他对象存储位置,同时GIT_CEILING_DIRECTORIES可用于限制 Git 在查看..、、../..等时执行的路径遍历量。

于 2018-07-24T17:26:15.460 回答
1

另一个答案使讨论环境变量变得复杂,在运行 php 服务器脚本时,这些环境变量在实践中永远不会设置,并且它没有明确回答哪个选项可以解决问题。咱们试试吧:

$ cd
$ ls -ad .git
ls: cannot access '.git': No such file or directory

$ env | grep -i git | wc -l
0

$ git --work-tree=$HOME/myrepo describe --always
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

$ git --work-tree=$HOME/myrepo --git-dir=$HOME/myrepo/.git describe --always
d5cd316

$ git --git-dir=$HOME/myrepo/.git describe --always
d5cd316

$ git --git-dir=$HOME/myrepo describe --always
fatal: not a git repository: '/home/redacted/myrepo'

-->--git-dir就足够了,但它需要/.git后缀。

于 2020-09-27T15:21:30.673 回答