Git clone 到底是什么?

本文介绍了 Git clone 的几种方式,还有一些有用的参数

Git clone 到底是什么?
Photo by Praveen Thirumurugan / Unsplash

我们经常使用 git clone,但大多数情况下,我们都是从一个中心化的远程仓库,比如 GitHub,GitLab 等等。但实际上,这一条命令有更简单的含义。

HTTPS

最常见的方式。

git clone https://github.com/torvalds/linux.git

SSH

通过这种方式 clone,需要在 Github 上传 SSH 公钥,才能通过鉴权。

git clone [email protected]:torvalds/linux.git

但是事实上,这里并不一定得是 github,这里其实可以是任何一个你有权通过 ssh 访问的主机,例如:

git clone [email protected]:test/.git

假设 user 的家目录里面有个 test 目录,这个目录是一个 git repo,那么这条命令就会把 user~/test clone 下来。

看看,上面两个命令有多像,所以实际上,当我们使用 SSH clone 的时候,就是通过 SSH 登录上了 github.com 这台主机。

💡
通过这种方式 clone 应该是鲜为人知,我是从下面这个视频里面知道的。
You Don’t Know Network Programming
Streamed Live on Twitch: https://twitch.tv/tsodingEnable Subtitles for Twitch ChatMore Tore Episodes: https://www.youtube.com/playlist?list=PLpM-Dvs8t0Vb6rfY…
[中英熟肉] 你不懂网络编程 | Tsoding Daily
title: You Don't Know Network Programming url: https://www.youtube.com/watch?v=JRTLSxGf_6w channel: Tsoding Daily upload_timestamp: 2024-12-03T14:38:07Z

如果你尝试使用 ssh 直接登录 github,会有下面的提示:

$ ssh [email protected]
PTY allocation request failed on channel 0
Hi <USER_NAME>! You've successfully authenticated, but GitHub does not provide shell access.
Connection to ssh.github.com closed.

本地路径

既然能通过 ssh clone,想必也可以从本地 clone 了。

git clone /path/to/repo/.git

这样就可以从本地 clone 下来。

更神奇的是,居然也可以 push,只不过会报这个错:

$ git push  
Enumerating objects: 5, done.  
Counting objects: 100% (5/5), done.  
Writing objects: 100% (3/3), 257 bytes | 257.00 KiB/s, done.  
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0  
remote: error: refusing to update checked out branch: refs/heads/main  
remote: error: By default, updating the current branch in a non-bare repository  
remote: is denied, because it will make the index and work tree inconsistent  
remote: with what you pushed, and will require 'git reset --hard' to match  
remote: the work tree to HEAD.  
remote:  
remote: You can set the 'receive.denyCurrentBranch' configuration variable  
remote: to 'ignore' or 'warn' in the remote repository to allow pushing into  
remote: its current branch; however, this is not recommended unless you  
remote: arranged to update its work tree to match what you pushed in some  
remote: other way.  
remote:  
remote: To squelch this message and still keep the default behaviour, set  
remote: 'receive.denyCurrentBranch' configuration variable to 'refuse'.  
To /path/to/repo/.git
! [remote rejected] main -> main (branch is currently checked out)  
error: failed to push some refs to '/path/to/repo/.git'

这个错误是因为:正在向一个非裸仓库(non-bare repository)的当前检出分支(checked-out branch)进行推送。

如果确实有需求这样做,有两种方法:

  1. 创建裸仓库
    1. 可以把当前仓库 clone 一份 git clone --bare . ../test.git
    2. 也可以在创建仓库的时候就直接使用裸仓库:git init --bare /path/to/central-repo.git
  2. 不要往当前检出分支推送
    1. 可以在 remote 里运行 git checkout --detach,分离头指针
    2. 也可以切换到其他分支git switch -c temp-branch
    3. 也可以在 push 的时候改名 git push origin main:new-branch (把本地的 main 推送到 origin 的 new-branch 上)

总结

所以说,remote 并不一定得是一个中心化的仓库,它其实可以是任何一个位置的仓库,双方是对等的。像 git clone, git push, git pull 等等只需要两台(甚至一台)安装有 git 客户端的主机就可以了。

有用的 Git Clone 参数

这里列举一些常用的 git clone 参数。

  1. 设置上游的别名。当存在多个上游的时候,可以用这个办法设置别名。

    # 将上游仓库命名为 upstream 而不是 origin
    git clone -o upstream https://github.com/otheruser/original-repo.git
    
  2. 设置目标目录,而不是默认的以仓库名作为目录名。

    # 将仓库克隆到 my-project 文件夹,而不是默认的 repo 文件夹
    git clone https://github.com/user/repo.git my-project
    
  3. 控制 clone 的深度。如果只是想把代码下载下来看看,可以使用 --depth=1

    # 只克隆最近一次提交,历史记录为空
    git clone --depth=1 https://github.com/vuejs/vue.git
    # 按照时间筛选
    git clone --shallow-since="2024-01-01" https://github.com/user/repo.git
    # 如果想要更多历史,可以这样补救
    git fetch --depth=10
    # 或者这样直接获取所有的历史
    git fetch --unshallow
    
  4. 只克隆一个分支。可以和上面的深度结合起来。

    # 只克隆 main 分支的最新代码
    git clone --single-branch --branch main --depth=1 https://github.com/user/repo.git
    # 如果需要其他分支,也可以用下面的命令补充。
    git fetch origin feature-x:feature-x