Git
英语 ▾ 主题 ▾ 最新版本 ▾ gitfaq 最后更新于 2.46.0

名称

gitfaq - 关于使用 Git 的常见问题解答

概要

gitfaq

描述

本常见问题解答中的示例假设使用标准 POSIX shell,如 bashdash,以及一个用户 A U Thor,其在托管提供商 git.example.org 上拥有帐户 author

配置

我应该在 user.name 中填写什么?

你应该填写你的个人姓名,通常使用给定名称和姓氏的形式。例如,Git 的当前维护者使用“Junio C Hamano”。这将是存储在你所做的每个提交中的名称部分。

此配置对身份验证到远程服务没有任何影响;为此,请参阅 git-config[1] 中的 credential.username

http.postBuffer 到底有什么作用?

此选项更改 Git 通过 HTTP 或 HTTPS 推送数据到远程服务器时使用的缓冲区大小。如果数据大于此大小,则处理 Git HTTP 支持的 libcurl 将使用分块传输编码,因为它事先不知道推送数据的具体大小。

除非你明确知道远程服务器或中间的代理不支持 HTTP/1.1(引入了分块传输编码)或已知在处理分块数据时存在问题,否则将此值保留为默认大小即可。这通常被(错误地)建议作为通用推送问题的解决方案,但由于几乎所有服务器和代理都至少支持 HTTP/1.1,因此提高此值通常无法解决大多数推送问题。在当今互联网上,一个不正确地支持 HTTP/1.1 和分块传输编码的服务器或代理将没有多大用处,因为它会中断大量流量。

请注意,增加此值会增加 Git 通过 HTTP 或 HTTPS 执行的每个相关推送所使用的内存,因为无论是否全部使用,都会分配整个缓冲区。因此,除非你确定需要不同的值,否则最好将其保留为默认值。

如何配置不同的编辑器?

如果你没有为 Git 特定指定编辑器,它将默认使用你使用 VISUALEDITOR 环境变量配置的编辑器,或者如果两者都没有指定,则使用系统默认值(通常为 vi)。由于有些人发现 vi 难以使用或更喜欢其他编辑器,因此可能需要更改使用的编辑器。

如果你想为大多数需要编辑器的程序配置一个通用编辑器,你可以编辑你的 shell 配置文件(例如,~/.bashrc~/.zshenv),使其包含一行将 EDITORVISUAL 环境变量设置为适当的值。例如,如果你更喜欢 nano 编辑器,则可以编写以下内容:

export VISUAL=nano

如果你想为 Git 特别配置一个编辑器,你可以设置 core.editor 配置值或 GIT_EDITOR 环境变量。你可以查看 git-var[1],了解这些选项的查询顺序。

请注意,在所有情况下,编辑器值都将传递给 shell,因此任何包含空格的参数都应进行适当的引用。此外,如果你的编辑器在调用时通常会与终端分离,则应使用一个参数来指定它不要这样做,否则 Git 将看不到任何更改。在 Windows 上解决这两个问题的配置示例将是配置 "C:\Program Files\Vim\gvim.exe" --nofork,它用引号引起来包含空格的文件名并指定 --nofork 选项以避免将进程后台化。

凭据

如何通过 HTTP 推送时指定我的凭据?

最简单的方法是通过 credential.helper 配置使用凭据助手。大多数系统都提供一个标准选项来与系统凭据管理器集成。例如,Git for Windows 提供 wincred 凭据管理器,macOS 具有 osxkeychain 凭据管理器,而具有标准桌面环境的 Unix 系统可以使用 libsecret 凭据管理器。所有这些都将凭据存储在加密存储中,以确保你的密码或令牌安全。

此外,你可以使用 store 凭据管理器,它将存储在你的主目录中的文件中,或者使用 cache 凭据管理器,它不会永久存储你的凭据,但会防止你在一段时间内被提示输入凭据。

你也可以在提示时输入你的密码。虽然可以在 URL 中放置密码(必须进行百分比编码),但这并不安全,并且可能导致意外泄露凭据,因此不建议这样做。

如何从环境变量中读取密码或令牌?

credential.helper 配置选项还可以接受一个任意 shell 命令,该命令在标准输出上生成凭据协议。例如,在将凭据传递到容器时,这很有用。

可以通过在选项值开头添加感叹号来指定此类 shell 命令。如果你的密码或令牌存储在 GIT_TOKEN 中,则可以运行以下命令来设置你的凭据助手:

$ git config credential.helper \
	'!f() { echo username=author; echo "password=$GIT_TOKEN"; };f'
如何更改我保存在凭据管理器中的密码或令牌?

通常,如果密码或令牌无效,Git 将将其删除并提示输入新的密码或令牌。但是,有时并非总是如此。要更改密码或令牌,你可以删除现有凭据,然后 Git 将提示输入新的凭据。要删除凭据,请使用类似以下的语法(替换你的用户名和主机名):

$ echo url=https://[email protected] | git credential reject
如何使用 HTTP 使用同一托管提供商的多个帐户?

通常,区分这些帐户的最简单方法是在 URL 中使用用户名。例如,如果你在 git.example.org 上拥有帐户 authorcommitter,则可以使用 URL https://[email protected]/org1/project1.githttps://[email protected]/org2/project2.git。这样,当你使用凭据助手时,它会自动尝试查找你的帐户的正确凭据。如果你已经设置了远程仓库,则可以使用 git remote set-url origin https://[email protected]/org1/project1.git 等命令更改 URL(有关详细信息,请参阅 git-remote[1])。

如何使用 SSH 使用同一托管提供商的多个帐户?

对于大多数支持 SSH 的托管提供商而言,单个密钥对唯一地标识一个用户。因此,要使用多个帐户,需要为每个帐户创建一个密钥对。如果你使用的是相当现代的 OpenSSH 版本,则可以使用 ssh-keygen -t ed25519 -f ~/.ssh/id_committer 等命令创建新的密钥对。然后,你可以将公钥(在本例中为 ~/.ssh/id_committer.pub;请注意 .pub)注册到托管提供商。

大多数托管提供商使用单个 SSH 帐户进行推送;也就是说,所有用户都推送到 git 帐户(例如,[email protected])。如果你的提供商也是如此,则可以在 SSH 中设置多个别名,以明确使用哪个密钥对。例如,你可以在 ~/.ssh/config 中编写如下内容,并替换正确的私钥文件:

# This is the account for author on git.example.org.
Host example_author
	HostName git.example.org
	User git
	# This is the key pair registered for author with git.example.org.
	IdentityFile ~/.ssh/id_author
	IdentitiesOnly yes
# This is the account for committer on git.example.org.
Host example_committer
	HostName git.example.org
	User git
	# This is the key pair registered for committer with git.example.org.
	IdentityFile ~/.ssh/id_committer
	IdentitiesOnly yes

然后,你可以将你的推送 URL 调整为使用 git@example_authorgit@example_committer 代替 [email protected](例如,git remote set-url git@example_author:org1/project1.git)。

传输

如何跨系统同步工作区?

首先,决定是否需要这样做。Git 在你使用典型的 git pushgit fetch 命令推送或拉取工作时效果最佳,并且并非设计用于跨系统共享工作区。这可能存在风险,并且在某些情况下会导致存储库损坏或数据丢失。

通常,这样做会导致git status需要重新读取工作树中的每个文件。此外,Git 的安全模型不允许在不受信任的用户之间共享工作树,因此,只有在工作树仅由单个用户跨所有机器使用时,同步工作树才安全。

重要的是,不要使用云同步服务来同步 Git 存储库的任何部分,因为这会导致损坏,例如丢失对象、更改或添加文件、损坏的引用以及各种其他问题。这些服务倾向于持续地逐个文件同步,并且不理解 Git 存储库的结构。如果它们在存储库更新过程中同步存储库,则尤其糟糕,因为这很可能导致不完整或部分更新,从而导致数据丢失。

可能发生的损坏类型的一个示例是关于引用状态的冲突,例如,双方最终在对方没有的某个分支上拥有不同的提交。这可能导致重要的对象变得无引用,并可能被git gc修剪,从而导致数据丢失。

因此,最好使用正常的推送和拉取机制将您的工作推送到另一个系统或中央服务器。但是,这并不总是保留重要的数据,例如暂存区,因此有些人更喜欢在系统之间共享工作树。

如果您这样做,建议的方法是使用rsync -a --delete-after(理想情况下使用加密连接,例如使用ssh)在存储库的根目录上。执行此操作时,您应确保以下几点

  • 如果您有其他工作树或单独的 Git 目录,则必须与主工作树和存储库同时同步它们。

  • 您对目标目录成为源目录的精确副本感到满意,*删除其中已有的任何数据*。

  • 存储库(包括所有工作树和 Git 目录)在传输期间处于静止状态(即,在其上没有进行任何类型的操作,包括后台操作,例如git gc以及由您的编辑器调用的操作)。

    请注意,即使按照这些建议,以这种方式同步也存在一些风险,因为它绕过了 Git 对存储库的正常完整性检查,因此建议进行备份。您可能还希望在同步后执行git fsck以验证目标系统上数据的完整性。

常见问题

我在上次提交中犯了一个错误。如何更改它?

您可以对工作树进行相应的更改,根据需要运行git add <file>git rm <file>将其暂存,然后运行git commit --amend。您的更改将包含在提交中,并且系统会提示您再次编辑提交消息;如果您希望逐字使用原始消息,则可以另外对git commit使用--no-edit选项,或者在编辑器打开时保存并退出。

我进行了一项有错误的更改,并且它已被包含在主分支中。我应该如何撤消它?

处理此问题的通常方法是使用git revert。这保留了原始更改已进行且是宝贵贡献的历史记录,但也引入了撤消这些更改的新提交,因为原始更改存在问题。revert 的提交消息指示已 revert 的提交,通常会对其进行编辑以包含 revert 原因的解释。

如何忽略对已跟踪文件的更改?

Git 没有提供执行此操作的方法。原因是,如果 Git 需要覆盖此文件(例如在检出期间),它不知道对文件的更改是否宝贵且应保留,或者它们是否无关紧要并且可以安全地销毁。因此,它必须采取安全途径并始终保留它们。

尝试使用git update-index的某些功能(即assume-unchanged和skip-worktree位)很诱人,但这些功能不适用于此目的,不应以这种方式使用。

如果您的目标是修改配置文件,则通常可以将一个文件检入存储库,该文件是模板或一组默认值,然后可以将其复制并在旁边进行修改。通常会忽略第二个修改后的文件,以防止意外提交它。

我要求 Git 忽略各种文件,但它们仍然被跟踪

gitignore文件确保 Git 未跟踪的某些文件保持未跟踪状态。但是,有时某些文件可能在添加到.gitignore之前已被跟踪,因此它们仍然被跟踪。要取消跟踪和忽略文件/模式,请使用git rm --cached <file/pattern>并将与<file>匹配的模式添加到.gitignore中。有关详细信息,请参阅gitignore[5]

如何知道是否要执行 fetch 或 pull?

fetch 会存储远程存储库中最新更改的副本,而不会修改工作树或当前分支。然后,您可以随时检查、合并、在之上重新设置基准或忽略上游更改。pull 由 fetch 组成,然后立即进行合并或重新设置基准。请参阅git-pull[1]

我可以在 Git 中使用代理吗?

是的,Git 支持使用代理。Git 遵循 Unix 上常用的标准http_proxyhttps_proxyno_proxy环境变量,并且还可以使用http.proxy和类似的选项配置 HTTPS(请参阅git-config[1])。http.proxy和相关选项可以根据每个 URL 模式进行自定义。此外,Git 理论上可以与网络上存在的透明代理正常工作。

对于 SSH,Git 可以使用 OpenSSH 的ProxyCommand支持代理。常用的工具包括netcatsocat。但是,必须将它们配置为在标准输入上看到 EOF 时不退出,这通常意味着netcat需要-q,而socat需要类似-t 10的超时。这是必需的,因为 Git SSH 服务器知道不再有请求的方式是标准输入上的 EOF,但当这种情况发生时,服务器可能尚未处理最终请求,因此在此时断开连接会中断该请求。

使用 HTTP 代理的~/.ssh/config中的示例配置条目可能如下所示

Host git.example.org
    User git
    ProxyCommand socat -t 10 - PROXY:proxy.example.org:%h:%p,proxyport=8080

请注意,在所有情况下,为了使 Git 正确工作,代理必须完全透明。代理不能以任何方式修改、篡改或缓冲连接,否则 Git 几乎肯定无法工作。请注意,许多代理(包括许多 TLS 中间盒、Windows 防病毒和防火墙程序(Windows Defender 和 Windows 防火墙除外)以及过滤代理)无法满足此标准,因此最终会导致 Git 出现故障。由于有许多问题报告以及它们糟糕的安全历史,我们建议不要使用这些类型的软件和设备。

合并和重新设置基准

使用 squash 合并多次合并长期存在的分支时可能出现哪些问题?

通常,使用 squash 合并多次合并两个分支时可能会出现各种问题。这些问题可能包括在git log输出、GUI 或使用...表示法表示范围时看到额外的提交,以及可能需要反复重新解决冲突。

当 Git 在两个分支之间执行普通合并时,它会考虑三个点:这两个分支和一个第三个提交(称为*合并基准*),该提交通常是提交的共同祖先。合并的结果是合并基准与每个头部之间更改的总和。当您使用常规合并提交合并两个分支时,这会导致一个新的提交,该提交最终会在再次合并时成为合并基准,因为现在有一个新的共同祖先。Git 不必考虑合并基准之前发生的更改,因此您不必重新解决之前解决的任何冲突。

当您执行 squash 合并时,不会创建合并提交;相反,来自一侧的更改将作为常规提交应用到另一侧。这意味着这些分支的合并基准不会改变,因此当 Git 去执行下一个合并时,它会考虑上次考虑的所有更改加上新的更改。这意味着任何冲突可能都需要重新解决。类似地,在git diffgit log或 GUI 中使用...表示法的任何内容都将导致显示自原始合并基准以来的所有更改。

因此,如果您想重复合并两个长期存在的分支,最好始终使用常规合并提交。

如果我在两个分支上进行了更改,但在其中一个分支上 revert 了它,为什么合并这些分支会包含该更改?

默认情况下,当 Git 执行合并时,它使用名为ort策略的策略,该策略执行一个高级的三向合并。在这种情况下,当 Git 执行合并时,它会考虑三个点:两个头部和一个第三个点(称为*合并基准*),该点通常是这些提交的共同祖先。Git 根本不会考虑这些分支上发生的提交的历史记录或单个提交。

因此,如果双方都有更改,而其中一方 revert 了该更改,则结果是包含该更改。这是因为代码在一侧发生了更改,而另一侧没有净更改,在这种情况下,Git 会采用该更改。

如果这对您来说是一个问题,您可以改用 rebase,将带有 revert 的分支重新设置基准到另一个分支。在这种情况下,rebase 会 revert 该更改,因为 rebase 会应用每个单独的提交,包括 revert。请注意,rebase 会重写历史记录,因此您应避免重新设置已发布分支的基准,除非您确定您对这样做感到满意。有关更多详细信息,请参阅git-rebase[1]中的 NOTES 部分。

钩子

如何使用钩子来阻止用户进行某些更改?

进行这些更改的唯一安全位置是在远程存储库(即 Git 服务器)上,通常在pre-receive钩子或持续集成 (CI) 系统中。这些是在其中可以有效执行策略的位置。

通常会尝试使用pre-commit钩子(或对于提交消息,使用commit-msg钩子)来检查这些内容,如果您是单独的开发人员并且希望工具帮助您,这很好。但是,在开发人员机器上使用钩子作为策略控制是无效的,因为用户可以使用--no-verify绕过这些钩子而不会被发现(以及其他各种方法)。Git 假设用户控制着他们的本地存储库,并且不会尝试阻止或告发用户。

此外,一些高级用户发现pre-commit钩子会妨碍使用临时提交来暂存正在进行的工作或创建修复提交的工作流程,因此最好将这些类型的检查推送到服务器。

跨平台问题

我在 Windows 上,我的文本文件被检测为二进制文件。

当您将文本文件存储为 UTF-8 时,Git 的工作效果最佳。许多 Windows 程序支持 UTF-8,但有些程序不支持,只使用小端 UTF-16 格式,Git 会将其检测为二进制文件。如果您无法在程序中使用 UTF-8,则可以指定一个工作树编码,该编码指示应使用哪个编码检出文件,同时仍然在存储库中将这些文件存储为 UTF-8。这允许像git-diff[1]这样的工具按预期工作,同时仍然允许您的工具工作。

为此,您可以使用working-tree-encoding属性指定gitattributes[5]模式。例如,以下模式将所有 C 文件设置为使用 UTF-16LE-BOM,这在 Windows 上是一种常见的编码

*.c	working-tree-encoding=UTF-16LE-BOM

您需要运行git add --renormalize才能使此生效。请注意,如果您在跨平台使用的项目上进行这些更改,则可能需要在每个用户的配置文件中或$GIT_DIR/info/attributes中的配置文件中进行更改,因为在存储库中的.gitattributes文件中进行更改将应用于存储库的所有用户。

有关规范化换行符的信息,请参阅以下条目,并参阅gitattributes[5]以获取有关属性文件的更多信息。

我在 Windows 上,git diff 显示我的文件末尾有^M

默认情况下,Git 期望文件以 Unix 换行符存储。因此,Windows 换行符的一部分回车符 (^M) 会显示出来,因为它被认为是尾随空格。Git 默认情况下仅在新行而不是现有行上显示尾随空格。

您可以使用 Unix 换行符在存储库中存储文件,并将其自动转换为平台的换行符。为此,请将配置选项core.eol设置为native,并参阅关于推荐存储设置的问题,了解如何将文件配置为文本或二进制文件。

如果您不想从换行符中删除回车符,也可以使用core.whitespace设置控制此行为。

为什么我有一个总是被修改的文件?

在内部,Git 始终将文件名存储为字节序列,并且不执行任何编码或大小写折叠。但是,Windows 和 macOS 默认情况下都会对文件名执行大小写折叠。因此,可能会出现多个文件名仅大小写不同的文件或目录。Git 可以很好地处理这种情况,但文件系统只能存储其中一个文件,因此当 Git 读取另一个文件以查看其内容时,它会显示已修改。

最好删除其中一个文件,以便您只有一个文件。您可以在干净的工作树上使用以下命令执行此操作(假设有两个文件AFile.txtafile.txt

$ git rm --cached AFile.txt
$ git commit -m 'Remove files conflicting in case'
$ git checkout .

这避免了接触磁盘,但删除了额外的文件。您的项目可能更喜欢采用命名约定,例如全小写名称,以避免此问题再次发生;可以使用pre-receive钩子或作为持续集成 (CI) 系统的一部分来检查此类约定。

如果您的系统上使用了涂抹或清理过滤器,但先前提交的文件未运行涂抹或清理过滤器,则在任何平台上也可能发生永久修改的文件。要解决此问题,请在干净的工作树上运行以下命令

$ git add --renormalize .

GIT

git[1]套件的一部分

scroll-to-top