
1. 项目概述为什么你需要真正理解git clonegit clone大概是每个开发者入职第一天就会碰到的命令。但我见过太多人在这个命令上栽跟头要么clone到一半断线重来要么拉下来的代码根本没法跑要么在配置SSH认证时被一串报错劝退。这些问题的根源都一样——只知道git clone 仓库地址这种写法却不知道这个命令背后究竟做了什么。简单说git clone的本质是从远程仓库复制一份完整的仓库历史到本地。它不是简单地“下载文件”而是把远程仓库的所有提交记录、所有分支、所有标签连同git的元数据一起打包拉到你机器上。这也是git和SVN最大的区别——每台本地机器都是一份完整的仓库副本即使远程仓库挂掉了你本地依然拥有全部历史。这篇内容既适合刚摸到git边缘的新手也适合那些已经会用git但从未深究过clone细节的同学。我会从clone的完整命令形态、认证配置、实操流程、效率优化、报错排查五个方面把我踩过的坑和验证过的方案一次讲清楚。在动手之前先建立整体认知clone最常用的场景有三类。第一类是直接复制别人的开源项目到本地学习或二次开发第二类是在公司内部拉取项目代码参与协作第三类是CI/CD流水线中自动拉取代码构建产物。这三类场景对clone的需求侧重点完全不同第一类关心易用性第二类关心认证权限第三类关心速度和稳定性。后面所有章节的讨论都是围绕这三个诉求展开的。2. 核心细节解析clone命令的完整形态与参数拆解2.1 从最简单的用法到完整命令形态git clone 仓库地址是最基础的写法但完整形态要复杂得多。我手动敲过最完整的一条clone命令是这个样子的git clone --depth 1 --branch main --single-branch --recurse-submodules --shallow-submodules gitgithub.com:octocat/Hello-World.git my-project拆开看这条命令要求git只拉取最近1条提交记录、只拉取main分支、只拉取该分支历史同时把子模块也初始化并拉取最终把代码放到my-project目录下。在日常开发中这些参数不一定全用得上但在特定场景下它们能救命。先看最基础的用法。git clone 仓库地址会在当前目录下创建一个和仓库名同名的目录。如果你不想用默认目录名就在地址后面追加目录名参数。这个目录名的位置比较反直觉因为很多人以为目录名要写在地址前面——不需要担心记错顺序git clone 地址 目录先地址后目录固定顺序。地址本身支持两种协议HTTPS和SSH。HTTPS地址形如https://github.com/octocat/Hello-World.gitSSH地址形如gitgithub.com:octocat/Hello-World.git。两者的区别我会在第三章详细展开这里先记住一个结论在不需要输入密码的场景下SSH更顺手在防火墙严格的公司内网HTTPS更省事。2.2 关键参数的使用场景与代价--depth这个参数的作用是只拉取指定数量的提交历史。--depth 1意味着只拉取最新一条提交仓库会瘦身很多。我clone过一些动辄几百MB历史的仓库用--depth 1之后体积直接降到几十MB。代价是你失去了完整的历史记录——git log里只能看到一条提交git blame也无法追溯旧版本代码是谁改的如果你需要基于旧标签做排查浅克隆会给后续工作带来麻烦。--branch指定你clone后默认检出的分支。不写这个参数时git会检出远程仓库的默认分支通常是main或者master。这个参数的价值在于如果一个仓库的分支特别多你明确知道自己只需要某个功能分支那么指定分支后git只拉取该分支的提交历史效率高不少。--single-branch只拉取指定分支的提交历史。默认情况下git clone会把远程所有分支的引用全部下载下来git branch -r可以看到所有远程分支。加上--single-branch后远程分支列表里只有一个你要的分支。这个参数有两个应用场景一是仓库分支数量特别多几十甚至上百个分支时减少无用引用能省时间二是配合--depth做浅克隆时必须加上--single-branch否则git会把所有分支的浅提交都拉一次代价等于没说。--recurse-submodules如果你的仓库里有子模块submodule不加这个参数的话clone下来的代码里子模块目录是空的。我在实际项目中遇到过很多次同事拉完代码发现某个目录是空的跑起来直接报模块找不到最后发现是子模块没初始化。加上--recurse-submodules参数后git会自动执行子模块的初始化和拉取一步到位。如果项目深度使用子模块建议配合--shallow-submodules一起使用它会让子模块也执行浅克隆进一步节省时间。# 完整子模块拉取 git clone --recurse-submodules --shallow-submodules 仓库地址--bare与**--mirror**这两个参数不常见但很关键。--bare会拉取一个裸仓库没有工作目录只有.git内容用于服务器端部署和代码托管。--mirror则是裸仓库的完整镜像会把远程的所有引用包括远端分支、标签、远端服务器上自己维护的引用全部复制一份。这两个参数日常开发中用不到但在搭建代码托管服务器或者做仓库迁移时是核心工具。下表是这些参数的速查参数作用典型场景代价--depth浅克隆只保留N条历史大仓库快速拉取、CI构建缺失完整历史记录--branch检出指定分支明确需要某个分支的代码无--single-branch只拉取一个分支的历史分支数量极多的仓库无法查看其他分支--recurse-submodules初始化并拉取子模块有submodule的仓库额外拉取时间--mirror完整镜像远程仓库仓库迁移、备份体积巨大、不含工作区2.3 一个容易忽略的细节clone后的remote配置clone完成之后git会在本地仓库的remote配置里自动添加一个名为origin的远程仓库地址。这个origin几乎成了所有人在日常协作中使用的主远程。有一个很容易踩坑的知识点git remote -v只能看到fetch和push两条地址记录而这两条记录默认是同一个地址。如果仓库迁移了地址很多人会直接删掉origin然后重新添加其实不用这么做——一条git remote set-url origin 新地址就解决问题。另外我建议在clone完成的第一时间查看一下.git/config文件确认当前remote配置是否符合预期。这个文件记录了clone时使用的基础参数如果将来出现和远程仓库对不上的奇怪行为优先检查这里。3. 远程认证方式解析与SSH配置实操3.1 HTTPS与SSH的选择逻辑很多人第一次clone公开仓库用的都是HTTPS地址因为不用配置任何认证信息输入仓库地址就能拉。但如果拉的是私有仓库HTTPS会面临两个问题每次push都要输入账号密码虽然macOS的钥匙串能记忆但在Linux服务器和CI环境里没那么方便以及部分企业内网服务对HTTPS的大文件传输支持不佳。我的建议是日常开发环境优先配置SSH密钥。SSH方式做好一次配置之后所有基于SSH协议的仓库操作都不再需要输入密码体验接近无感。服务端通过你本机生成的一对密钥公钥私钥来识别你的身份私钥保存在本地公钥配置在git服务端。HTTPS在一种场景下依然值得选择你需要在公用电脑或者临时环境上拉一次代码不方便持久保存私钥文件时HTTPS配合凭据管理器能更快地完成任务。另外某些企业内网的防火墙会屏蔽SSH的22端口这种情况下HTTPS也是更务实的选择。3.2 实战一行命令生成SSH密钥并完成配置SSH密钥生成使用的工具是ssh-keygen。打开终端执行ssh-keygen -t ed25519 -C your_emailexample.com这里有两个关键选择-t ed25519表示使用Ed25519算法生成密钥这是目前兼顾安全和性能的主流选择-C后面的注释建议写你的邮箱或者姓名纯粹用于标识。执行后会问你保存路径和密码短语。保存路径默认是~/.ssh/id_ed25519直接回车用默认值即可。密码短语passphrase建议设置一个虽然每次用密钥时都要输一次密码但这相当于给私钥加了第二道锁——即使私钥文件被人复制走了没有密码也无法使用。生成完毕后需要把公钥内容复制到git服务端。查看公钥内容cat ~/.ssh/id_ed25519.pub复制输出的整行内容以ssh-ed25519开头然后打开GitHub的Settings SSH and GPG keys页面点击New SSH key把内容粘贴进去保存。其他git平台GitLab、Gitea的操作路径类似都在个人设置的安全中心里。3.3 SSH认证失败的排查顺序配置完SSH密钥之后先做一次连接测试确认认证链路畅通ssh -T gitgithub.com如果一切正常会看到类似“Hi username! Youve successfully authenticated”的提示。如果你使用的git服务不是默认的22端口比如公司内网用2222端口则需要在测试和后续clone命令中显式指定端口ssh -T -p 2222 gitgitserver.company.com git clone ssh://gitgitserver.company.com:2222/group/project.gitSSH认证失败是最常见的git问题之一几乎每个人都遇到过。下面是我整理的排查顺序按这个顺序查基本不会漏确认公钥真的添加到了服务端很多人以为粘贴了公钥实际粘贴时多了换行或者空格导致服务端校验不过。重新打开cat ~/.ssh/id_ed25519.pub完整复制逐字节对比。确认本机正在使用正确的私钥如果本机有多个密钥对ssh可能选错。这时在~/.ssh/config文件里为指定域名绑定密钥文件Host github.com HostName github.com User git IdentityFile ~/.ssh/id_ed25519_company确认终端环境变量没问题有时GIT_SSH_COMMAND环境变量被设置了奇怪的参数干扰了ssh连接。执行env | grep GIT_SSH检查有异常就unset GIT_SSH_COMMAND。确认服务端地址可访问ping一下域名或者用ssh -T看报错是超时还是Permission denied。超时是网络问题Permission denied才是认证问题。我在实际操作中还发现过一种隐蔽情况公钥配置正确、网络畅通但ssh连接仍然报错最后发现是Windows用户目录下的.ssh文件夹权限设置过于开放ssh直接拒绝使用其中的密钥文件。处理方式是右键密钥文件属性在安全选项卡中将其他用户的访问权限全部移除只保留当前用户。4. 实操过程从clone到正常工作的完整流程4.1 标准操作链路我自己在正式项目中遵循的操作链路是确认环境、配置认证、clone、核对分支、建立工作分支、启动开发。每一步都有需要注意的细节。第一步确认git版本。不同版本的git对clone命令的支持有差异尤其--depth和--recurse-submodules这类参数在旧版本上表现不够稳定。git --version低于2.0的版本建议先升级目前主流git版本都在2.3x以上新特性支持完整。第二步确认认证配置。无论是HTTPS还是SSH先跑一次无实际操作的安全验证避免clone到一半报权限错误。# SSH方式 ssh -T gitgithub.com # HTTPS方式以GitHub为例 git ls-remote https://github.com/your-username/some-repo.gitgit ls-remote这个命令很实用它只列出远程仓库的引用而不下载内容。如果这个命令能正常返回远程分支列表说明认证和网络都没有问题接下来clone几乎不会在权限上卡壳。第三步执行clone。根据这次拉取代码的目的选择参数组合。场景A学习开源项目需要完整历史和研究所有分支git clone https://github.com/octocat/Hello-World.git场景B快速在本地运行项目不关心历史git clone --depth 1 https://github.com/octocat/Hello-World.git场景C参与公司项目需要基于功能分支开发git clone --branch feature/login gitgithub.com:company/project.git4.2 clone后的分支管理和子模块处理很多新手clone完代码后直接在当前分支上改东西这是危险操作。clone出来默认检出的分支是远程默认分支而参与协作的正确姿势是在远程已有分支的基础上建立自己的本地跟踪分支或者基于当前分支创建新的功能分支。查看本地和远程的所有分支git branch -a本地分支前面没有前缀远程分支以remotes/origin/开头。比如remotes/origin/develop表示远程有一个develop分支。要在本地检出这个远程分支git checkout -b develop origin/develop这条命令的意思是新建一个本地分支develop并让它跟踪远程的origin/develop分支。之后执行git pull和git push时git会自动在develop和origin/develop之间推拉代码无需额外指定分支名。如果习惯用git fetch而不是git pull拉取远程分支的方式有一点不同git fetch origin git checkout -b develop origin/develop本质上和上面一条命令一样但先fetch再checkout的方式能让你在checkout之前先看到远程有哪些新分支。子模块方面即使clone时加了--recurse-submodules子模块的远程地址也可能发生变化导致拉取失败。遇到这种情况在仓库根目录执行git submodule sync git submodule update --init --recursivesubmodule sync会把子模块的remote地址重新对齐到父仓库中记录的状态这一步能解决大多数子模块地址失效导致的拉取问题。4.3 在IDE中配置远程仓库源开发者大部分时间在IDE中工作以VSCode为例配置git远程仓库是一个非常常见的需求。VSCode左侧菜单的源代码管理面板已经内置了git能力但有时候你打开一个本地项目发现git功能不起作用十有八九是当前目录不是一个git仓库。解决办法是打开终端执行git init或者如果你把项目目录删掉了直接重新clone到本地再打开。在VSCode中查看和修改远程仓库地址的操作路径是命令面板CtrlShiftP输入Git: Remote选择添加或者修改远程仓库。实测下来VSCode对git的支持基本覆盖了日常需求包括分支切换、暂存、提交、推送唯一体验较差的是冲突解决遇到复杂的merge冲突还是启动终端更顺手。一条实操心得VSCode配置远程仓库时首选SSH地址而不是HTTPS地址因为VSCode的认证机制对SSH密钥利用得很好开箱即用HTTPS地址偶尔会出现凭据无法自动识别的问题。具体做法是先在终端里把SSH密钥配置好然后在VSCode里添加远程时直接粘贴SSH地址基本一次成功。5. 大仓库与克隆效率浅克隆、稀疏检出与镜像仓库5.1 为什么你的clone总是慢很多人以为clone慢纯粹是网络问题。以我的经验看网络只是其中一个因素更多时候是仓库本身太大——不是代码量大而是提交历史积累得太多了。一个五六年历史的大型项目光.git目录就能撑到几个GB即使网络带宽充裕也要花大量时间传输这些历史数据。常规解法是浅克隆和单分支克隆。在章节2中我已经介绍过这两个参数这里给出更具体的效率对比。我曾经对同一个大型仓库做过实验克隆方式传输体积耗时完整克隆3.2GB约18分钟--depth 1240MB约2分钟--depth 1 --single-branch180MB约1分30秒稀疏检出首次拉取同完整克隆但工作区只保留指定目录拉取快工作区瘦身结论很明确如果只需要最新代码跑起来--depth 1 --single-branch是几乎完美的组合。但它有代价我在章节2.2中提醒过历史追溯的问题。这里再补充一个常见补救方案需要完整历史时在浅克隆基础上执行git fetch --unshallowgit会从远程补齐所有提交历史相当于从浅克隆升级为完整克隆。唯一的问题是如果仓库历史极大这个补全过程依然耗时只是起码你不会在最开始卡住。5.2 稀疏检出只拿需要的目录大仓库的另一种常见形态是仓库里目录众多但你实际只用到其中两三个目录。比如一个大型monorepo仓库里包含着前端、后端、文档、脚本等几十个目录你只想看后端代码。常规clone会把所有目录的工作区文件都检出来占据大量磁盘空间。稀疏检出sparse checkout能解决这个问题。操作流程是先做一个不带工作区文件的克隆然后配置只检出指定目录。# 第1步不检出文件只拉取元数据 git clone --no-checkout 仓库地址 cd 仓库目录 # 第2步启用稀疏检出模式 git sparse-checkout init --cone # 第3步指定需要检出的目录 git sparse-checkout set backend/src backend/test # 第4步检出当前分支的文件 git checkout这套流程执行完毕后本地工作区只包含backend/src和backend/test两个目录。切换到其他分支时稀疏检出规则会一直生效不会因为切换分支就把全部代码拉下来。需要追加目录时把set换成addgit sparse-checkout add frontend/src这个功能在Git 2.25及以上版本中表现稳定是我在大型monorepo场景下强烈推荐的做法。唯一要注意的是某些构建工具或脚本会扫描整个仓库目录并期望所有文件都存在稀疏检出后这些工具可能会报错需要先评估项目构建是否能在缺失文件的前提下工作。5.3 断点续传与多服务器拉取clone到一半断线是很多人的噩梦因为git clone没有真正的断点续传功能。我见过最无奈的场景是拉取一个1GB的仓库进度到800MB时断线重新执行clone又从头开始。目前没有完美解决这个问题的办法但有降低痛苦的三条经验先把浅克隆作为预热先--depth 1快速拿到完整代码结构之后如果需要历史数据再在本地作git fetch --unshallow补历史。即使fetch中断git的传输是支持分片断点恢复的因为fetch的传输对象是基于对象的增量推送比从头clone要可靠得多。使用HTTP的传输加速如果你的git服务支持HTTP/2可以尝试将传输协议切换为智能HTTPgit config --global http.version HTTP/1.1实际上部分网络环境下HTTP/1.1比HTTP/2更稳定HTTP/2的多路复用对长连接要求更高这个配置根据实际代理网络环境来调整。调整postBufferclone大仓库时报RPC failed; curl 56或者early EOF错误时绝大多数是HTTP缓冲区太小导致传输中断。调整缓冲区大小解决git config --global http.postBuffer 524288000这个配置把HTTP传输缓冲区调整到500MB实测解决了很多大仓库clone失败的问题。5.4 镜像克隆与仓库备份如果目的是做仓库备份或者服务迁移普通的clone是低效的因为它会丢掉很多远端引用信息。正确的姿势是裸克隆配合镜像参数。git clone --mirror 旧仓库地址 目标目录 cd 目标目录 git remote set-url origin 新仓库地址 git push --mirror origin这套操作的精髓在于--mirror它会把旧仓库的所有内容包括所有分支、标签、远端分支引用全部复制到本地然后再通过push --mirror完整推送到新地址。迁移后新旧仓库的状态几乎完全一致。需要提醒的是--mirror克隆出来的仓库没有工作目录不要在它里面做代码开发它只是一个数据传输的介质。做完推送后这个本地目录就可以删除了。6. 高频报错速查与排查思路git报错的提示信息往往不够友好很多英文错误信息对经验不足的人就是天书。下面这张表收集了我在实际环境和社区里最常见的几类报错附带解决方案建议截图保存随时对照。报错信息可能原因解决方案Permission denied (publickey)SSH密钥未添加或本地私钥错误重查公钥是否添加正确检查~/.ssh/config的密钥绑定Authentication failedHTTPS密码错误或凭据过期更新凭据或改用SSH方式remote: Repository not found仓库不存在或没有访问权限确认仓库地址拼写确认有该仓库的权限fatal: unable to access网络不通或TLS证书拦截检查网络内网代理环境下配置http.proxyRPC failed; curl 56HTTP缓冲区太小或网络中断调大http.postBuffer更换网络unable to read tree本地文件损坏或index损坏执行git reset --hard HEAD或删除本地仓库重新cloneearly EOF网络中断导致传输不完整配置postBuffer后重试改用浅克隆warning: remote HEAD refers to nonexistent ref服务端默认分支失效不影响clone可在服务端重设默认分支6.1 企业内网环境下的代理配置很多人在公司内网clone开源项目时失败报错信息五花八门其实根源都是代理。git支持为不同协议配置代理# 为所有git请求配置HTTP代理 git config --global http.proxy http://127.0.0.1:7890 git config --global https.proxy http://127.0.0.1:7890 # 为SSH请求配置代理 git config --global http.https://github.com.proxy http://127.0.0.1:7890如果你需要访问的git服务在代理之外比如企业内部git仓库不需要走代理取消代理只针对某些域名的办法是git config --global --unset http.proxy git config --global https.proxy 这里有一个很关键的避坑经验当http.proxy设置为空字符串时有些git版本会报错正确的做法是用--unset删除配置而不是git config --global http.proxy 。我遇到过同事把这个配置写成了空字符串导致所有git请求都尝试连接空代理直接全部失败。6.2 仓库迁移后的地址更新问题项目仓库从旧服务器迁移到新服务器后clone产生的origin地址还是旧地址。很多人重新clone一遍项目来更新地址其实不用这么折腾。# 查看当前origin地址 git remote -v # 修改origin地址 git remote set-url origin 新地址 # 验证 git remote -v如果是本地仓库不是通过clone创建的而是用git init创建的那么它可能没有任何远程地址。添加远程地址用git remote add origin 新地址这里有个细节值得注意git remote set-url和git remote add是两个不同的操作。set-url要求远程名已经存在add要求远程名不存在。搞混的结果要么是error: remote origin already exists要么是error: could not find remote origin。报错后不要慌用git remote -v看清当前状态再决定用哪条命令。6.3 clone后代码跑不起的经典排查路径代码clone成功了但跑不起来这种情况在团队协作中太常见了。我的排查顺序是查看README的安装步骤大多数项目会在README里说明依赖安装方式确认你执行过这些前置步骤。检查Node/Python/Java版本项目对运行时版本有要求版本不一致会出现各种诡异错误。检查环境变量例如需要DATABASE_URL、API_KEY这类配置变量项目通常提供.env.example模板复制为.env后填入实际值。检查子模块是否完整目录为空或缺失就是子模块没有拉取在根目录执行git submodule update --init --recursive。检查依赖安装是否完成前端项目跑npm installPython项目跑pip install -r requirements.txt后端Java项目跑mvn clean package。我自己有过一次印象很深的经历clone一个Python项目后反复报找不到一个第三方库pip install执行了还是报错最后发现是项目要求Python 3.8但我本地默认用的Python 3.7pip装到了另一个版本的解释器上。所以遇到莫名其妙的依赖问题先确认当前命令行解释器版本。6.4 解决clone的SSL证书验证报错在内网环境里很多公司自建git平台用的SSL证书不是权威CA签发的git的默认行为会校验HTTPS证书并拒绝连接报错通常是SSL certificate problem。临时绕过证书校验的办法不推荐长期使用git clone -c http.sslVerifyfalse 仓库地址但这会让每次clone都带上额外参数很麻烦。如果公司内部git服务就是用的自签名证书可以在git配置中针对该地址关闭校验git config --global http.https://gitserver.company.com.sslVerify false记住这里的域名需要和报错信息中的域名严格一致否则配置不会生效。关闭SSL校验有安全风险仅限内网可信环境使用。7. 个人经验补充与更深一层的思考7.1 正确配置git环境的三个必选项在新环境里第一时间配置的git全局参数git config --global user.name 你的名字 git config --global user.email 你的邮箱 git config --global init.defaultBranch main前两项决定提交记录中的作者信息很多新手第一次提交后发现看不到自己的名字就是因为没配这两个值。第三项决定git init创建的默认分支名Git 2.28版本之后支持这个配置建议直接设为main避免夹在master和main之间的混乱。git的全局配置文件在~/.gitconfig修改完之后可以通过git config --list查看所有生效配置。如果发现某个配置在全局和仓库级冲突仓库级的.git/config有更高优先级。7.2 clone后的第一个习惯先看分支再看状态我的个人习惯是clone完成后不急着改代码先执行三个命令git branch -a git log --oneline -5 git statusgit branch -a让你了解仓库有哪些分支git log --oneline -5让你知道当前代码处于什么进度git status确认工作区干净没有残留文件。这套组合拳打完你对这个仓库的理解就已经超过了多数clone完就跑的新手。尤其在参与多人协作项目时这个习惯能帮你避免一个常见事故默认检出的分支可能是别人开发了一半的状态带着未提交的半成品代码。如果你不看git log和git status就闷头改代码最后提交时会发现混入了别人的修改。7.3 分支合并前的重要检查clone之后拉分支是常规操作但分支合并前有一个必查项基于哪个分支创建的本地分支。这是一个隐蔽但是后果严重的坑你从主分支拉出一个功能分支但主分支落后于远程主分支你开发完要合并代码时发现冲突一大堆。正确做法是在分支开发前先确认基础分支是新的。git fetch origin git log --oneline origin/main -1 git log --oneline main -1如果本地main的提交记录落后于origin/main先git rebase origin/main或者git merge origin/main把基础追平再开始开发。这个动作能为后续省去大量冲突处理时间。我在实际项目里还见过一种情况有人clone仓库后在主分支上做了大量修改然后想推送到远程被拒绝原因是远程main分支已经被别人推进了多个提交。此时正确做法是把本地修改切到新分支再合并起来处理。7.4 记住clone永远是一个元数据操作最后说一个容易被忽视的认知。很多人把git clone理解为“下载代码”这在概念层面是偏了。更准确的理解是“下载仓库元数据并检出一份工作区副本”。元数据包括提交对象、树对象、blob对象、引用等工作区文件只是这些对象的物化结果。这解释了很多现象为什么浅克隆会让仓库体积大减因为减少了历史对象为什么稀疏检出能节省磁盘空间因为工作区不物化所有文件为什么--mirror克隆没有工作区因为它只有元数据。带着这个认知去看git的更多操作比如fetch、pull、push、merge你会发现它们都是围绕元数据在工作。当你理解了git存储模型的这一层遇到问题就不容易慌因为你能判断哪些操作会真正改变远程状态哪些操作只影响本地存储。我在实践中的体会是git的很多疑难问题本质上都是对这套存储模型理解不够深导致的。花半小时把对象模型理清楚后面能省下数不清的排查时间。所以如果你正在学习git除了记住命令和参数值得花点时间看看Git Book中关于Git Internals的几个章节。