Skip to content

git-fetch

从另一个仓库下载对象和引用

概要

git fetch [<options>] [<repository> [<refspec>...]]
git fetch [<options>] <group>
git fetch --multiple [<options>] [(<repository>|<group>)...]
git fetch --all [<options>]

描述

从一个或多个其他仓库获取分支和/或标签(统称为"引用"),以及完成其历史所需的对象。远程跟踪分支会被更新(有关控制此行为的方法,请参阅下面的 <refspec> 描述)。

默认情况下,指向正在获取的历史中的任何标签也会被获取;其效果是获取指向您感兴趣的分支的标签。此默认行为可以通过使用 --tags--no-tags 选项或配置 remote.<name>.tagOpt 来更改。通过使用显式获取标签的 refspec,您也可以获取不指向您感兴趣的分支的标签。

git fetch 可以从单个命名仓库或 URL 获取,如果给出了 <group> 并且配置文件中有 remotes.<group> 条目,则可以同时从多个仓库获取(参见 git-config(1))。

当未指定远程仓库时,默认使用 origin 远程,除非为当前分支配置了上游分支。

获取的引用名称及其指向的对象名称被写入 .git/FETCH_HEAD。此信息可被脚本或其他 git 命令使用,例如 git-pull(1)

选项

  • --all, --no-all 获取所有远程,除了设置了 remote.<name>.skipFetchAll 配置变量的远程。这会覆盖配置变量 fetch.all

  • -a, --append 将获取的引用名称和对象名称追加到 .git/FETCH_HEAD 的现有内容中。没有此选项时,.git/FETCH_HEAD 中的旧数据将被覆盖。

  • --atomic 使用原子事务更新本地引用。要么所有引用都被更新,要么在出错时没有引用被更新。

  • --depth=<depth> 将获取限制为从每个远程分支历史顶端开始的指定数量的提交。如果获取到由 git clone 使用 --depth=<depth> 选项创建的"浅层"仓库(参见 git-clone(1)),则加深或缩短历史到指定数量的提交。不会获取加深提交的标签。

  • --deepen=<depth> 类似于 --depth,只是它指定从当前浅层边界开始的提交数,而不是从每个远程分支历史的顶端开始。

  • --shallow-since=<date> 加深或缩短浅层仓库的历史以包含 <date> 之后的所有可达提交。

  • --shallow-exclude=<ref> 加深或缩短浅层仓库的历史以排除从指定远程分支或标签可达的提交。此选项可以指定多次。

  • --unshallow 如果源仓库是完整的,将浅层仓库转换为完整的仓库,删除浅层仓库施加的所有限制。 如果源仓库是浅层的,尽可能多地获取,使当前仓库与源仓库具有相同的历史。

  • --update-shallow 默认情况下,从浅层仓库获取时,git fetch 拒绝需要更新 .git/shallow 的引用。此选项更新 .git/shallow 并接受此类引用。

  • --negotiation-restrict=(<commit>|<glob>), --negotiation-tip=(<commit>|<glob>) 默认情况下,Git 会向服务器报告从所有本地引用可达的提交,以尝试减少要接收的包文件的大小。如果指定,Git 将仅报告从给定提示可达的提交。当用户知道哪个本地引用可能与正在获取的上游引用有共同提交时,这有助于加速获取。 --negotiation-restrict 是此选项的首选名称;--negotiation-tip 作为同义词被接受。 此选项可以指定多次;如果是这样,Git 将报告从任何给定提交可达的提交。 此选项的参数可以是引用名称上的 glob、引用或提交的(可能缩写的)SHA-1。指定 glob 等同于多次指定此选项,每个匹配的引用名称一次。 另请参阅 git-config(1) 中记录的 fetch.negotiationAlgorithmpush.negotiate 配置变量,以及下面的 --negotiate-only 选项。

  • --negotiation-include=(<commit>|<glob>) 确保给定提示处的提交在获取协商期间始终作为 "have" 行发送,无论协商算法选择了什么。这对于保证从特定引用可达的共同历史始终被考虑很有用,即使 --negotiation-restrict 限制了提示集或协商算法会跳过它们。 此选项可以指定多次;如果是这样,每个提交都被无条件发送。 参数可以是精确的引用名称(例如 refs/heads/release)、对象哈希或 glob 模式(例如 refs/heads/release/{asterisk})。模式语法与 --negotiation-restrict 相同。 如果使用了 --negotiation-restrict,have 集首先受该选项限制,然后增加以包含 --negotiation-include 指定的提示。 如果在命令行上未指定此选项,则使用当前远程的任何 remote.<name>.negotiationInclude 配置值。

  • --negotiate-only 不从服务器获取任何内容,而是打印我们与服务器共有的提供的 --negotiation-restrict= 参数的祖先。 这与 --recurse-submodules=(yes|on-demand) 不兼容。内部使用此选项来实现 push.negotiate 选项,参见 git-config(1)

  • --dry-run 显示将要执行的操作,而不进行任何更改。

  • --porcelain 以易于脚本解析的格式将输出打印到标准输出。有关详细信息,请参阅 git-fetch(1) 中的输出部分。 这与 --recurse-submodules=(yes|on-demand) 不兼容,并优先于 fetch.output 配置选项。

  • --filter=<filter-spec> 使用部分克隆功能并请求服务器根据给定的对象过滤器发送可达对象的子集。使用 --filter 时,提供的 <filter-spec> 用于部分获取。 如果使用 --filter=auto,过滤器规范通过组合服务器为客户端接受的 promisor 远程公布的过滤器规范自动确定(参见 gitprotocol-v2(5)git-config(1) 中的 promisor.acceptFromServer 配置选项)。 有关所有其他可用过滤器规范的详细信息,请参阅 git-rev-list(1) 中的 --filter=<filter-spec> 选项。 例如,--filter=blob:none 将过滤掉所有 blob(文件内容),直到 Git 需要它们。同样,--filter=blob:limit=<size> 将过滤掉大小至少为 <size> 的所有 blob。

  • --write-fetch-head, --no-write-fetch-head 将获取的远程引用列表直接写入 $GIT_DIR 下的 FETCH_HEAD 文件。这是默认行为。从命令行传递 --no-write-fetch-head 告诉 Git 不写入该文件。在 --dry-run 选项下,该文件永远不会被写入。

  • -f, --forcegit fetch<src>:<dst> refspec 一起使用时,它可能拒绝更新本地分支,如下面 <refspec> 部分所述。此选项覆盖该检查。

  • -k, --keep 保留下载的包。

  • --multiple 允许指定多个 <repository><group> 参数。不能指定 <refspec>

  • --auto-maintenance, --no-auto-maintenance, --auto-gc, --no-auto-gc 在结束时运行 git maintenance run --auto 以在需要时执行自动仓库维护。默认启用。

  • --write-commit-graph, --no-write-commit-graph 获取后写入 commit-graph。这覆盖配置设置 fetch.writeCommitGraph

  • --prefetch 修改配置的 refspec 以将所有引用放入 refs/prefetch/ 命名空间。参见 git-maintenance(1) 中的 prefetch 任务。

  • -p, --prune 获取前,删除远程上不再存在的任何远程跟踪引用。如果标签仅因为默认标签自动跟踪或由于 --tags 选项而被获取,则标签不受修剪影响。但是,如果标签由于显式 refspec(在命令行上或在远程配置中,例如远程使用 --mirror 选项克隆)而被获取,则它们也受修剪影响。提供 --prune-tags 是提供标签 refspec 的简写。 有关更多详细信息,请参阅下面的修剪部分。

  • -P, --prune-tags 如果启用了 --prune,则在获取前删除远程上不再存在的任何本地标签。此选项应更谨慎使用,与 --prune 不同,它将删除已创建的任何本地引用(本地标签)。此选项是提供显式标签 refspec 与 --prune 的简写,有关其讨论请参阅其文档。 有关更多详细信息,请参阅下面的修剪部分。

  • -n, --no-tags 默认情况下,从远程仓库下载的对象指向的标签会被获取并本地存储。此选项禁用此自动标签跟踪。远程的默认行为可以通过 remote.<name>.tagOpt 设置指定。参见 git-config(1)

  • --refetch 代替与服务器协商以避免传输已存在于本地的提交和关联对象,此选项像新的克隆一样获取所有对象。当过滤器定义已更改时,使用此选项从配置或使用 --filter= 重新应用部分克隆过滤器。获取后的自动维护将执行对象数据库包合并以删除任何重复对象。

  • --refmap=<refspec> 获取命令行列出的引用时,使用指定的 refspec(可以给出多次)将引用映射到远程跟踪分支,而不是远程仓库的 remote.<name>.fetch 配置变量的值。向 --refmap 选项提供空的 <refspec> 会导致 Git 忽略配置的 refspec 并完全依赖作为命令行参数提供的 refspec。有关详细信息,请参阅"配置的远程跟踪分支"部分。

  • -t, --tags 从远程获取所有标签(即将远程标签 refs/tags/* 获取到同名的本地标签),此外还获取其他方式会获取的任何内容。单独使用此选项不会使标签受修剪影响,即使使用了 --prune(但如果标签也是显式 refspec 的目标,则标签可能会被修剪;参见 --prune)。

  • --recurse-submodules[=(yes|on-demand|no)] 控制是否以及在什么条件下也应获取子模块的新提交。递归通过子模块时,git fetch 始终尝试获取"已更改"的子模块,即具有新获取的超级项目提交引用但本地子模块克隆中缺失的提交的子模块。只要本地存在更改的子模块(例如在 $GIT_DIR/modules/ 中,参见 gitsubmodules(7)),就可以获取它;如果上游添加了新子模块,则在克隆之前无法获取该子模块(例如通过 git submodule update)。 设置为 on-demand 时,仅获取已更改的子模块。设置为 yes 时,获取所有已填充的子模块以及既未填充又已更改的子模块。设置为 no 时,从不获取子模块。 未指定时,使用 fetch.recurseSubmodules 的值(如果已设置,参见 git-config(1)),未设置时默认为 on-demand。使用此选项时不带任何值时,默认为 yes

  • -j <n>, --jobs=<n> 将所有形式的获取并行化,一次最多 <n> 个作业。 值为 0 将使用一些合理的默认值。 如果指定了 --multiple 选项,不同的远程将并行获取。如果获取多个子模块,它们将并行获取。要独立控制它们,请使用配置设置 fetch.parallelsubmodule.fetchJobs(参见 git-config(1))。 通常,并行递归和多远程获取会更快。默认情况下,获取是顺序执行的,而不是并行的。

  • --no-recurse-submodules 禁用子模块的递归获取(这与使用 --recurse-submodules=no 选项效果相同)。

  • --set-upstream 如果远程获取成功,添加上游(跟踪)引用,供无参数的 git-pull(1) 和其他命令使用。有关更多信息,请参阅 git-config(1) 中的 branch.<name>.mergebranch.<name>.remote

  • --submodule-prefix=<path> 在信息性消息(如 "Fetching submodule foo")中打印的路径前加上 <path>。此选项在递归子模块时内部使用。

  • --recurse-submodules-default=(yes|on-demand) 此选项内部用于临时为 --recurse-submodules 选项提供非负默认值。配置获取的子模块递归的所有其他方法(例如 gitmodules(5)git-config(1) 中的设置)覆盖此选项,直接指定 --[no-]recurse-submodules 也是如此。

  • -u, --update-head-ok 默认情况下,git fetch 拒绝更新对应于当前分支的头。此标志禁用该检查。这纯粹是 git pullgit fetch 通信的内部使用,除非您正在实现自己的 Porcelain,否则不应使用它。

  • --upload-pack <upload-pack> 给定后,当要获取的仓库由 git fetch-pack 处理时,--exec=<upload-pack> 被传递给命令以指定在另一端运行的命令的非默认路径。

  • -q, --quiet--quiet 传递给 git-fetch-pack 并静音任何其他内部使用的 git 命令。进度不会报告到标准错误流。

  • -v, --verbose 详细模式。

  • --progress 默认情况下,当标准错误流连接到终端时,进度状态会报告到标准错误流,除非指定了 -q。此标志强制显示进度状态,即使标准错误流未定向到终端。

  • -o <option>, --server-option=<option> 使用协议版本 2 通信时将给定字符串传输到服务器。给定字符串不得包含 NULLF 字符。服务器对服务器选项(包括未知选项)的处理取决于服务器。当给出多个 --server-option=<option> 时,它们按命令行上列出的顺序全部发送到另一端。当命令行上未给出 --server-option=<option> 时,使用配置变量 remote.<name>.serverOption 的值。

  • --show-forced-updates 默认情况下,git 在获取期间检查分支是否被强制更新。这可以通过 fetch.showForcedUpdates 禁用,但 --show-forced-updates 选项保证此检查发生。参见 git-config(1)

  • --no-show-forced-updates 默认情况下,git 在获取期间检查分支是否被强制更新。传递 --no-show-forced-updates 或将 fetch.showForcedUpdates 设置为 false 以出于性能原因跳过此检查。如果在 git-pull 期间使用,--ff-only 选项仍将在尝试快进更新之前检查强制更新。参见 git-config(1)

  • -4, --ipv4 仅使用 IPv4 地址,忽略 IPv6 地址。

  • -6, --ipv6 仅使用 IPv6 地址,忽略 IPv4 地址。

  • <repository> 作为获取或拉取操作源的"远程"仓库。此参数可以是 URL(参见下面的 GIT URLS 部分)或远程的名称(参见下面的 REMOTES 部分)。

  • <group> 引用仓库列表的名称,作为配置文件中 remotes.<group> 的值(参见 git-config(1))。

  • <refspec> 指定要获取哪些引用以及更新哪些本地引用。当命令行上未出现 <refspec> 时,要获取的引用从 remote.<repository>.fetch 变量中读取(参见下面的配置的远程跟踪分支)。 <refspec> 参数的格式是可选的加号 +,后跟源 <src>,后跟冒号 :,后跟目标 <dst>。当 <dst> 为空时可以省略冒号。_<src> 通常是引用或带有单个 * 的 glob 模式,用于匹配一组引用,但它也可以是完整拼写的十六进制对象名称。 <refspec> 可以在其 <src> 中包含 * 以指示简单模式匹配。这样的 refspec 充当匹配具有该模式的任何引用的 glob。模式 <refspec><src><dst> 中都必须有一个且仅有一个 *。它将通过用源中匹配的内容替换 * 将引用映射到目标。 如果 refspec 以 ^ 为前缀,它将被解释为否定 refspec。此类 refspec 不是指定要获取哪些引用或更新哪些本地引用,而是指定要排除的引用。如果引用至少匹配一个正 refspec 且不匹配任何否定 refspec,则认为它匹配。否定 refspec 可用于限制模式 refspec 的范围,使其不包含特定引用。否定 refspec 本身可以是模式 refspec。但是,它们只能包含 <src> 且不指定 <dst>。也不支持完整拼写的十六进制对象名称。 tag <tag>refs/tags/<tag>:refs/tags/<tag> 含义相同;它请求获取直到给定标签的所有内容。 匹配 <src> 的远程引用被获取,如果 <dst> 不是空字符串,则尝试更新匹配它的本地引用。 是否允许在没有 --force 的情况下进行该更新取决于获取到的引用命名空间、获取的对象类型以及更新是否被视为快进。通常,获取适用与推送相同的规则,有关这些规则是什么,请参阅 git-push(1)<refspec>... 部分。特定于 git fetch 的规则例外情况如下所述。 直到 Git 版本 2.20,与使用 git-push(1) 推送时不同,对 refs/tags/* 的任何更新都会在 refspec 中没有 +(或 --force)的情况下被接受。获取时,我们不加区分地认为来自远程的所有标签更新都是强制获取。从 Git 版本 2.20 开始,获取以更新 refs/tags/* 的工作方式与推送时相同。即在 refspec 中没有 +(或 --force)的情况下,任何更新都会被拒绝。 与使用 git-push(1) 推送时不同,refs/{tags,heads}/* 之外的任何更新都会在 refspec 中没有 +(或 --force)的情况下被接受,无论是将树对象交换为 blob,还是将提交交换为没有前一个提交作为祖先的另一个提交等。 与使用 git-push(1) 推送时不同,没有配置可以修改这些规则,也没有类似于 pre-receive 钩子的 pre-fetch 钩子。 与使用 git-push(1) 推送时一样,上面描述的关于不允许作为更新的所有规则都可以通过在 refspec 前添加可选的 +(或使用 --force 命令行选项)来覆盖。唯一的例外是,无论多少强制都不会使 refs/heads/* 命名空间接受非提交对象。 注意:当您要获取的远程分支已知会被定期回绕和变基时,预计其新提示将不是其先前提示(如您上次获取时存储在远程跟踪分支中的)的后代。您会想要使用 + 号来指示此类分支将需要非快进更新。没有办法确定或声明分支将以这种行为在仓库中可用;拉取用户只需知道这是分支的预期使用模式。

  • --stdin 除了作为参数提供的 refspec 外,还从 stdin 读取 refspec,每行一个。不支持 "tag <name>" 格式。

GIT URLS

通常,URL 包含有关传输协议、远程服务器地址和仓库路径的信息。根据传输协议,其中一些信息可能不存在。

Git 支持 ssh、git、http 和 https 协议(此外,ftp 和 ftps 可用于获取,但效率低下且已弃用;请勿使用它们)。

原生传输(即 git:// URL)不进行身份验证,在不安全的网络上应谨慎使用。

以下语法可与它们一起使用:

  • ssh://[<user>@]<host>[:<port>]/<path-to-git-repo>
  • git://<host>[:<port>]/<path-to-git-repo>
  • http[s]://<host>[:<port>]/<path-to-git-repo>
  • ftp[s]://<host>[:<port>]/<path-to-git-repo>

替代的类 scp 语法也可用于 ssh 协议:

  • [<user>@]<host>:<path-to-git-repo>

仅当第一个冒号之前没有斜杠时才识别此语法。这有助于区分包含冒号的本地路径。例如,本地路径 foo:bar 可以指定为绝对路径或 ./foo:bar 以避免被误解为 ssh url。

ssh 和 git 协议还支持 ~<username> 扩展:

  • ssh://[<user>@]<host>[:<port>]/~<user>/<path-to-git-repo>
  • git://<host>[:<port>]/~<user>/<path-to-git-repo>
  • [<user>@]<host>:~<user>/<path-to-git-repo>

对于本地仓库,Git 也原生支持以下语法:

  • /path/to/repo.git/
  • file:///path/to/repo.git/

这两种语法基本等效,只是前者在克隆时隐含 --local 选项。有关详细信息,请参阅 git-clone(1)

git clonegit fetchgit pull(但不包括 git push)也会接受合适的 bundle 文件。参见 git-bundle(1)

当 Git 不知道如何处理某个传输协议时,它会尝试使用 remote-<transport> 远程助手(如果存在)。要显式请求远程助手,可以使用以下语法:

  • <transport>::<address>

其中 <address> 可以是路径、服务器和路径,或特定远程助手识别的任意类似 URL 的字符串。有关详细信息,请参阅 gitremote-helpers(7)

如果有大量类似命名的远程仓库,并且您想为它们使用不同的格式(以便您使用的 URL 被重写为有效的 URL),您可以创建以下形式的配置部分:

[url "<actual-url-base>"]
    insteadOf = <other-url-base>

例如,使用此配置:

[url "git://git.host.xz/"]
    insteadOf = host.xz:/path/to/
    insteadOf = work:

像 "work:repo.git" 或 "host.xz:/path/to/repo.git" 这样的 URL 将在任何接受 URL 的上下文中被重写为 "git://git.host.xz/repo.git"。

如果您只想为推送重写 URL,可以创建以下形式的配置部分:

[url "<actual-url-base>"]
    pushInsteadOf = <other-url-base>

例如,使用此配置:

[url "ssh://example.org/"]
    pushInsteadOf = git://example.org/

像 "git://example.org/path/to/repo.git" 这样的 URL 将为推送重写为 "ssh://example.org/path/to/repo.git",但拉取仍将使用原始 URL。

REMOTES

以下之一的名称可以用作 <repository> 参数的 URL 替代:

  • Git 配置文件中的远程:$GIT_DIR/config
  • $GIT_DIR/remotes 目录中的文件,或
  • $GIT_DIR/branches 目录中的文件

所有这些还允许您从命令行省略 refspec,因为它们各自包含 git 将默认使用的 refspec。

配置文件中的命名远程

您可以选择提供先前使用 git-remote(1)git-config(1) 或手动编辑 $GIT_DIR/config 文件配置的远程名称。此远程的 URL 将用于访问仓库。当您在命令行上未提供 refspec 时,此远程的 refspec 将默认使用。配置文件中的条目如下所示:

[remote "<name>"]
    url = <URL>
    pushurl = <pushurl>
    push = <refspec>
    fetch = <refspec>

<pushurl> 仅用于推送。它是可选的,默认为 <URL>。推送到远程会影响所有已定义的 pushurl 或所有已定义的 url(如果未定义 pushurl)。但是,如果定义了多个 url,获取只会从第一个定义的 url 获取。

$GIT_DIR/remotes 中的命名文件

您可以选择提供 $GIT_DIR/remotes 中文件的名称。此文件中的 URL 将用于访问仓库。当您在命令行上未提供 refspec 时,此文件中的 refspec 将作为默认值使用。此文件应具有以下格式:

URL: <上述 URL 格式之一>
Push: <refspec>
Pull: <refspec>

Push: 行由 git push 使用,Pull: 行由 git pullgit fetch 使用。可以指定多个 Push:Pull: 行以进行额外的分支映射。

$GIT_DIR/branches 中的命名文件

您可以选择提供 $GIT_DIR/branches 中文件的名称。此文件中的 URL 将用于访问仓库。此文件应具有以下格式:

<URL>#<head>

<URL> 是必需的;#<head> 是可选的。

根据操作,如果您未在命令行上提供 refspec,git 将使用以下 refspec 之一。<branch> 是此文件在 $GIT_DIR/branches 中的名称,<head> 默认为 master

git fetch 使用:

refs/heads/<head>:refs/heads/<branch>

git push 使用:

HEAD:refs/heads/<head>

上游分支

Git 中的分支可以选择性地拥有上游远程分支。Git 默认使用上游分支进行远程操作,例如:

  • 它是无参数的 git pullgit fetch 的默认值。
  • 它是无参数的 git push 的默认值,有一些例外。例如,您可以使用 branch.<name>.pushRemote 选项推送到与拉取不同的远程,并且默认情况下使用 push.default=simple,您配置的上游分支必须具有相同的名称。
  • 各种命令,包括 git checkoutgit status,将向您显示自您从上游分支分叉以来当前分支和上游分支添加了多少提交,例如"您的分支和 'origin/main' 已经分歧,分别有 2 个和 3 个不同的提交"。

上游存储在 .git/config 中的 "remote" 和 "merge" 字段中。例如,如果 main 的上游是 origin/main

[branch "main"]
   remote = origin
   merge = refs/heads/main

您可以使用 git push --set-upstream <remote> <branch> 显式设置上游分支,但 Git 通常会自动为您设置上游,例如:

  • 当您克隆仓库时,Git 将自动为默认分支设置上游。
  • 如果您设置了 push.autoSetupRemote 配置选项,git push 将在您第一次推送分支时自动设置上游。
  • 使用 git checkout <branch> 检出远程跟踪分支将自动创建具有该名称的本地分支并将上游设置为远程分支。

注意:上游分支有时被称为"跟踪信息",如"设置分支的跟踪信息"。

配置的远程跟踪分支

您经常通过定期和重复地从同一远程仓库获取来与之交互。为了跟踪此类远程仓库的进度,git fetch 允许您配置 remote.<repository>.fetch 配置变量。

通常此类变量可能如下所示:

[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*

此配置以两种方式使用:

  • 当运行 git fetch 而未在命令行上指定要获取的分支和/或标签时(例如 git fetch origingit fetch),remote.<repository>.fetch 值用作 refspec——它们指定要获取哪些引用以及更新哪些本地引用。上面的示例将获取 origin 中存在的所有分支(即匹配值左侧 refs/heads/* 的任何引用),并更新 refs/remotes/origin/* 层次结构中的相应远程跟踪分支。

  • 当运行 git fetch 并在命令行上指定了要获取的显式分支和/或标签时(例如 git fetch origin master),命令行上给出的 <refspec> 决定要获取什么(例如示例中的 master,它是 master: 的简写,这意味着"获取 master 分支但我未在命令行上显式说明更新哪个远程跟踪分支"),示例命令将仅获取 master 分支。remote.<repository>.fetch 值决定更新哪个远程跟踪分支(如果有)。以这种方式使用时,remote.<repository>.fetch 值在决定获取什么方面没有任何效果(即当命令行列出 refspec 时,值不用作 refspec);它们仅通过充当映射来决定获取的引用存储在哪里。

remote.<repository>.fetch 值的后一种用途可以通过在命令行上给出 --refmap=<refspec> 参数来覆盖。

修剪

Git 有一个保留数据的默认处置,除非显式丢弃;这也延伸到保留对远程上已删除这些分支的分支的本地引用。

如果任其积累,这些过时的引用可能会使大型且繁忙的仓库(具有大量分支变动)的性能变差,并使命令(如 git branch -a --contains <commit>)的输出不必要地冗长,以及影响任何其他处理完整已知引用集的内容。

这些远程跟踪引用可以通过以下任一方式一次性删除:

# 获取时
$ git fetch --prune <name>

# 仅修剪,不获取
$ git remote prune <name>

要在正常工作流中修剪引用而无需记住运行该命令,请在配置中全局设置 fetch.prune 或按远程设置 remote.<name>.prune。参见 git-config(1)

事情变得棘手和更具体的地方在于。修剪功能实际上不关心分支,而是根据远程的 refspec 修剪本地 <--> 远程引用(参见上面的 <refspec> 和配置的远程跟踪分支)。

因此,如果远程的 refspec 包含例如 refs/tags/*:refs/tags/*,或者您手动运行例如 git fetch --prune <name> "refs/tags/*:refs/tags/*",被删除的将不是过时的远程跟踪分支,而是远程上不存在的任何本地标签。

这可能不是您期望的,即您想修剪远程 <name>,但也想从中显式获取标签,因此当您从中获取时,您会删除所有本地标签,其中大部分可能最初不是来自 <name> 远程。

因此,当与此类 refspec(如 refs/tags/*:refs/tags/*)或任何其他可能将多个远程的引用映射到同一本地命名空间的 refspec 一起使用时请小心。

由于同时保持与远程上的分支和标签同步是常见用例,可以提供 --prune-tags 选项与 --prune 一起以修剪远程上不存在的本地标签,并强制更新那些不同的标签。标签修剪也可以通过配置中的 fetch.pruneTagsremote.<name>.pruneTags 启用。参见 git-config(1)

--prune-tags 选项等同于在远程的 refspec 中声明 refs/tags/*:refs/tags/*。这可能导致一些看似奇怪的交互:

# 这两个都获取标签
$ git fetch --no-tags origin 'refs/tags/*:refs/tags/*'
$ git fetch --no-tags --prune-tags origin

当提供 --prune-tags 而不提供 --prune 或其配置版本时不会报错的原因是为了配置版本的灵活性,并维护命令行标志所做的事情与配置版本所做的事情之间的 1=1 映射。

~/.gitconfig 中配置 fetch.pruneTags=true 以在运行 git fetch --prune 时修剪标签是合理的,而不会使每次不带 --prunegit fetch 调用成为错误。

使用 --prune-tags 修剪标签在获取 URL 而不是命名远程时也有效。以下都将修剪在 origin 上找不到的标签:

$ git fetch origin --prune --prune-tags
$ git fetch origin --prune 'refs/tags/*:refs/tags/*'
$ git fetch <url-of-origin> --prune --prune-tags
$ git fetch <url-of-origin> --prune 'refs/tags/*:refs/tags/*'

输出

"git fetch" 的输出取决于所使用的传输方法;本节描述通过 Git 协议(本地或通过 ssh)和智能 HTTP 协议获取时的输出。

获取的状态以表格形式输出,每行代表单个引用的状态。每行的格式为:

 <flag> <summary> <from> -> <to> [<reason>]

使用 --porcelain 时,输出格式旨在可由机器解析。与人类可读的输出格式相比,它因此打印到标准输出而不是标准错误。每行的格式为:

<flag> <old-object-id> <new-object-id> <local-reference>

仅当使用 --verbose 选项时才显示最新引用的状态。

在紧凑输出模式下(由配置变量 fetch.output 指定),如果整个 <from><to> 在另一个字符串中被找到,它将在另一个字符串中被替换为 *。例如,master -> origin/master 变为 master -> origin/*

  • flag - 指示引用状态的单个字符:

    • (空格):成功获取的快进
    • +:成功的强制更新
    • -:成功修剪的引用
    • t:成功的标签更新
    • *:成功获取的新引用
    • !:被拒绝或更新失败的引用
    • =:最新且不需要获取的引用
  • summary - 对于成功获取的引用,摘要以适合用作 git log 参数的形式显示引用的旧值和新值(在大多数情况下为 <old>..<new>,对于强制非快进更新为 <old>...<new>)。

  • from - 正在获取的远程引用的名称,减去其 refs/<type>/ 前缀。在删除的情况下,远程引用的名称为 "(none)"。

  • to - 正在更新的本地引用的名称,减去其 refs/<type>/ 前缀。

  • reason - 人类可读的解释。对于成功获取的引用,不需要解释。对于失败的引用,描述失败原因。

示例

  • 更新远程跟踪分支:

    $ git fetch origin

    上面的命令从远程 refs/heads/ 命名空间复制所有分支并将其存储到本地 refs/remotes/origin/ 命名空间,除非使用 remote.<repository>.fetch 选项指定了非默认 refspec。

  • 显式使用 refspec:

    $ git fetch origin +seen:seen maint:tmp

    这通过从远程仓库分别获取分支 seenmaint 来更新(或在必要时创建)本地仓库中的分支 seentmpseen 分支将被更新,即使它不是快进,因为它以加号为前缀;tmp 则不会。

  • 查看远程的分支,而不在本地仓库中配置远程:

    $ git fetch git://git.kernel.org/pub/scm/git/git.git maint
    $ git log FETCH_HEAD

    第一个命令从 git://git.kernel.org/pub/scm/git/git.git 仓库获取 maint 分支,第二个命令使用 FETCH_HEAD 通过 git-log(1) 检查该分支。获取的对象最终将被 git 的内置清理机制删除(参见 git-gc(1))。

安全性

获取和推送协议并非旨在防止一方从另一个仓库窃取非预期共享的数据。如果您有需要保护免受恶意对等方侵害的私有数据,最好的选择是将其存储在另一个仓库中。这适用于客户端和服务器。特别是,服务器上的命名空间对读取访问控制无效;您应该只授予您信任的客户端对整个仓库的读取访问权限。

已知的攻击向量如下:

  1. 受害者发送 "have" 行,公布其拥有的对象 ID,这些对象并非明确打算共享,但如果对等方也拥有它们,可以用来优化传输。攻击者选择要窃取的对象 ID X 并发送对 X 的引用,但不需要发送 X 的内容,因为受害者已经拥有它。现在受害者相信攻击者拥有 X,并稍后将 X 的内容发送回攻击者。(这种攻击对客户端对服务器执行最为直接,通过在客户端有权访问的命名空间中创建对 X 的引用然后获取它。服务器对客户端执行的最可能方式是将 X "合并"到公共分支中,并希望用户在此分支上进行额外工作并在不注意合并的情况下将其推回服务器。)

  2. 与 #1 一样,攻击者选择要窃取的对象 ID X。受害者发送攻击者已拥有的对象 Y,攻击者虚假声称拥有 X 而不拥有 Y,因此受害者发送 Y 作为相对于 X 的增量。增量向攻击者揭示了 X 中与 Y 相似的区域。

配置

本节中此行以下的内容是从 git-config(1) 文档中选择性包含的。内容与其中的相同:

  • fetch.recurseSubmodules - 此选项控制 git fetch(和 git pull 中的底层获取)是否递归获取到已填充的子模块中。此选项可以设置为布尔值或 on-demand。设置为布尔值会更改获取和拉取的行为,设置为 true 时无条件递归到子模块,设置为 false 时根本不递归。设置为 on-demand 时,仅当其超级项目检索到更新子模块引用的提交时才递归到已填充的子模块中。默认为 on-demand,或如果设置了 submodule.recurse 则为其值。

  • fetch.fsckObjects - 如果设置为 true,git-fetch-pack 将检查所有获取的对象。有关检查内容请参见 transfer.fsckObjects。默认为 false。如果未设置,则使用 transfer.fsckObjects 的值。

  • fetch.fsck.<msg-id> - 作用类似于 fsck.<msg-id>,但由 git-fetch-pack(1) 使用而不是 git-fsck(1)。有关详细信息,请参阅 fsck.<msg-id> 文档。

  • fetch.fsck.skipList - 作用类似于 fsck.skipList,但由 git-fetch-pack(1) 使用而不是 git-fsck(1)。有关详细信息,请参阅 fsck.skipList 文档。

  • fetch.unpackLimit - 如果通过 Git 原生传输获取的对象数量低于此限制,则对象将被解包为松散对象文件。但是,如果接收的对象数量等于或超过此限制,则接收到的包将作为包存储,在添加任何缺失的增量基数之后。存储来自推送的包可以使推送操作完成得更快,特别是在慢速文件系统上。如果未设置,则使用 transfer.unpackLimit 的值。

  • fetch.prune - 如果为 true,获取将自动表现为命令行上给出了 --prune 选项。另请参阅 remote.<name>.prunegit-fetch(1) 的修剪部分。

  • fetch.pruneTags - 如果为 true,获取在修剪时将自动表现为提供了 refs/tags/*:refs/tags/* refspec(如果尚未设置)。这允许设置此选项和 fetch.prune 以维护与上游引用的 1=1 映射。另请参阅 remote.<name>.pruneTagsgit-fetch(1) 的修剪部分。

  • fetch.all - 如果为 true,获取将尝试更新所有可用的远程。此行为可以通过传递 --no-all 或显式指定一个或多个要获取的远程来覆盖。默认为 false

  • fetch.output - 控制引用更新状态的打印方式。有效值为 fullcompact。默认值为 full。有关详细信息,请参阅 git-fetch(1) 中的输出部分。

  • fetch.negotiationAlgorithm - 控制在协商服务器要发送的包文件内容时如何发送有关本地仓库中提交的信息。设置为 consecutive 以使用遍历连续提交检查每个提交的算法。设置为 skipping 以使用跳过提交以尝试更快收敛的算法,但可能导致比必要更大的包文件;或设置为 noop 以完全不发送任何信息,这几乎肯定会导致比必要更大的包文件,但会跳过协商步骤。设置为 default 以覆盖先前的设置并使用默认行为。默认通常是 consecutive,但如果 feature.experimentaltrue,则默认为 skipping。未知值将导致 git fetch 报错。 另请参阅 git-fetch(1)--negotiate-only--negotiation-restrict 选项。

  • fetch.showForcedUpdates - 设置为 false 以在 git-fetch(1)git-pull(1) 命令中启用 --no-show-forced-updates。默认为 true

  • fetch.parallel - 指定一次并行运行的最大获取操作数(子模块,或当 git-fetch(1)--multiple 选项生效时的远程)。 值为 0 将给出一些合理的默认值。如果未设置,默认为 1。 对于子模块,此设置可以使用 submodule.fetchJobs 配置设置覆盖。

  • fetch.writeCommitGraph - 设置为 true 以在每次 git fetch 命令从远程下载包文件后写入 commit-graph。使用 --split 选项,大多数执行将在现有 commit-graph 文件之上创建非常小的 commit-graph 文件。偶尔,这些文件会合并,写入可能需要更长时间。拥有更新的 commit-graph 文件有助于许多 Git 命令的性能,包括 git merge-basegit push -fgit log --graph。默认为 false

  • fetch.bundleURI - 此值存储用于在从 origin Git 服务器执行增量获取之前从 bundle URI 下载 Git 对象数据的 URI。这类似于 git-clone(1)--bundle-uri 选项的行为。如果提供的 bundle URI 包含为增量获取组织的 bundle 列表,git clone --bundle-uri 将设置 fetch.bundleURI 值。 如果您修改此值并且您的仓库有 fetch.bundleCreationToken 值,则在从新 bundle URI 获取之前删除该 fetch.bundleCreationToken 值。

  • fetch.bundleCreationToken - 当使用 fetch.bundleURI 从使用 "creationToken" 启发式的 bundle 列表增量获取时,此配置值存储已下载 bundle 的最大 creationToken 值。此值用于防止将来下载 bundle,如果公布的 creationToken 不严格大于此值。 创建令牌值由提供特定 bundle URI 的提供者选择。如果您在 fetch.bundleURI 修改 URI,请确保在获取之前删除 fetch.bundleCreationToken 的值。

已知问题

使用 --recurse-submodules 只能获取本地存在的子模块中的新提交(例如在 $GIT_DIR/modules/ 中)。如果上游添加了新子模块,则在克隆之前无法获取该子模块(例如通过 git submodule update)。预计将在未来的 Git 版本中修复此问题。

另请参阅

git-pull(1)

Git

git(1) 套件的一部分

基于 CC BY-NC-SA 3.0 许可发布