跳转至

Git 工具:隐私、工作流和 Troubleshooting

权限模型

本地的 git 程序并不关注使用的「远程账号」,而只关心当前仓库的 user.nameuser.email 配置,这两项配置决定了 git commit 记录的作者信息,GitHub 使用邮箱来归属统计贡献;远程账号只在与远程仓库进行 push pull clone 等交互时用于鉴权,使用 HTTPS 或者 SSH 凭据。

因此,对于「工作仓库用工作账号 commit,个人仓库用个人账号 commit」的需求,只需要做仓库级别的配置即可。

但是,对于 push 操作,GitHub 会记录执行 push 的账号,在 GitHub 的审计日志、活动记录、PR 关联等可见。clonepull 在某些情况下也会在审计日志中留下痕迹。

GitHub 提供三种鉴权方式:

  1. HTTPS:这种方式要求提供 GitHub 账号名和在 GitHub 中生成的 token。在陌生设备上临时执行 clone 等操作时较为便利,但由于 token 存在有效期限制,并且生成和保管都比较麻烦,不建议长期使用这种方式。
  2. SSH:使用本地操作系统配置的 SSH 私钥,与 GitHub 上配置的 SSH 公钥配对进行鉴权。
  3. GitHub CLI:为 Git 配置和接管 HTTPS 凭据的工具。通过 OAuth 授权获得 token 后自动交由 Git 的 HTTPS 凭据系统,相当于自动化 1 的过程。

这三种方式都可以实现「手动控制仓库使用哪一个身份进行鉴权」。

  1. HTTPS:对每个仓库手动操作,在 clone push pull 的时候都会询问账号名和 token。
  2. SSH:在本地生成两对 SSH 密钥,并为它们分别设置 SSH Host 别名。在 GitHub > Settings > SSH and GPG keys > Add new SSH Key 中添加刚刚生成的密钥对中,以 .pub 结尾的公钥的文件内容。在 clone 的时候修改链接中的 git@github.com:...git@别名:...(使得上游仓库链接变成 git@别名:...,对于存在的仓库可以用 git remote set-url origin ... 修改)。
    # 生成两对密钥
    ssh-keygen -t ed25519 -C "注释" -f ~/.ssh/id_ed25519_github_work
    ssh-keygen -t ed25519 -C "注释" -f ~/.ssh/id_ed25519_github_personal
    
    # 在 ~/.ssh/config 中配置别名
    Host github-work
        HostName github.com
        User git
        IdentityFile ~/.ssh/id_ed25519_github_work
    
    Host github-personal
        HostName github.com
        User git
        IdentityFile ~/.ssh/id_ed25519_github_personal
    
    # clone
    git clone git@github-work:...       # 工作仓库
    git clone git@github-personal:...   # 个人仓库
    
    如果不假修改而直接使用 git@github.com:... 链接,会导致 SSH 尝试匹配密钥对,产生错误认证。
  3. GitHub CLI:通过 brew install gh 安装(或其他包管理器,在其他操作系统上);使用 gh auth login 登录,登录时选择在 HTTPS 协议;使用 gh auth status 查看当前登录状态;使用 gh auth switch 切换账号。 clone push pull 的时候会自动使用 GitHub CLI 的凭据。
    $ gh auth status
    github.com
       Logged in to github.com account work_account (keyring)
      - Active account: true
      - Git operations protocol: https
      - Token: gho_************************************
      - Token scopes: 'gist', 'read:org', 'repo', 'workflow'
    
       Logged in to github.com account personal_account (keyring)
      - Active account: false
      - Git operations protocol: https
      - Token: gho_************************************
      - Token scopes: 'gist', 'read:org', 'repo', 'workflow'
    

基础操作

从远程仓库克隆代码 git clone <url>;查看所有远程分支 git branch -a 创建本地分支以跟踪同名的远程分支 git checkout --track origin/<branch_name>

创建新的分支 git branch <branch_name>

切换到分支 git checkout <branch_name>

创建新的分支并切换到此分支 git checkout -b <branch_name>

查看上游仓库(远程地址) git remote -v

修改上游仓库(远程地址) git remote set-url origin <url>

将本地的分支 a 推送到远程的分支 b git push origin a:b

重命名当前分支 git branch -m <new_name>

将未提交的改动推入暂存栈 git stash;为此添加说明 git stash push -m "message"

从暂存栈中取回改动 git stash pop

将修改加入暂存区 git add <file>

提交暂存区中的修改 git commit;只添加简短说明时 git commit -m "message"

推送到远程仓库:git push

拉取远程仓库的更改并合并到当前分支:git pull

工作流:git clone -> git add -> git commit -> git pull -> 处理冲突 -> git commit 提交冲突处理,如果不能自动处理冲突的话 -> git push。这个方法的好处是可以存档一份本地跑通的修改,在合并出错时可以回档。

或者:git clone -> git stash -> git pull -> git stash pop -> 处理冲突 -> git add -> git commit -> git push。这个方法的好处是当远程和本地都对某个文件做了修改,不会产生的合并提交。

Troubleshooting

设置代理

使用下面的命令设置 git 的 http 代理,去掉 --global 参数只对当前仓库生效(下同)。

git config --global http.proxy <proxy_url>

也可以指定只对某些地址使用代理(例如https://github.com)。

git config --global http.https://github.com.proxy <proxy_url>

git 的代理配置不存在 https.proxy 选项,因此不必设置,文档:https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpproxy

如果 git 的代理设置并没有按预期运行,可以用 git config --list 查看是否有冲突的配置,使用 git config --unset xxx 取消冲突配置。

git config --global --list

可以使用下面的命令查询某一项配置:

git config --global --get http.proxy

也支持使用正则表达式进行配置查询:

git config --global --get-regexp ^.*proxy.*$

手动指定仓库域名的 DNS 解析

碰到以下问题:

$ git push
fatal: unable to access 'https://git.xxx.edu.cn/yyy/zzz.git/': Could not resolve host: git.xxx.edu.cn

假设没有管理员权限,并且已知域名 git.xxx.edu.cn 的 IP 地址为 10.1.2.3,使用 git 配置的 insteadOf 选项添加 URL 重写规则,使得每当 git 尝试访问 git.xxx.edu.cn 时,它会自动替换为 IP 地址。

git config --local url."http://10.1.2.3/".insteadOf "http://git.xxx.edu.cn/"
git config --local url."https://10.1.2.3/".insteadOf "https://git.xxx.edu.cn/"
git config --local url."ssh://git@10.1.2.3/".insteadOf "ssh://git@git.xxx.edu.cn/"

如果遇到 SSL 证书验证错误,可能是因为服务器的 SSL 证书是颁发给域名的而不是颁发给IP地址的。解决方案是为特定域名添加例外(需要确保该服务器是安全的):

git config --local http.https://10.1.2.3/.sslVerify false

重命名或删除被跟踪的文件

当需要重命名或删除被跟踪的文件时,使用 git mvgit rm,而不是直接使用 mvrm

递归地移动或删除(以移动为例)一个目录下的所有文件:git mv old_dir/* new_dir/

只提交部分修改

使用 git add 将想要提交更改的文件添加到暂存区,然后再用 git commit 提交即可。在 vscode 中,可以通过直接点击文件后面的「暂存更改」按钮来将文件的修改添加到暂存区。这两个方式是等价的。

如果只想提交某个文件的部分修改,可以使用 git add -p,可以在随后唤起的交互模式中选择要添加到暂存区的修改。

修改 commit 记录的描述

使用 git commit --amend 即可修改上一次 commit 记录的描述。

如果需要修改倒数第 n 个 commit 记录的描述,则需要通过 git rebase -i 先变基到 HEAD~(n+1),然后在打开的文本编辑器内找到你想要修改的 commit,将行首的 pick 改为 reword,这将再次打开一个文本编辑器对选定的 commit 描述进行修改。最后,通过 git rebase --continue 回到 HEAD,这可能会造成远程仓库的历史记录与本地不一致。