简介:面向Git初学者的操作指南,完整梳理从零开始使用Git上传代码到GitHub的流程。内容覆盖注册账户并创建仓库、安装msysgit客户端、配置SSH key与本地用户信息、通过add/commit/push提交上传代码,同时讲解.gitignore忽略规则与标签(tag)的管理方法,可帮助开发者解决跨平台代码同步与团队协作中的基础问题。资源包共1个文件,为docx格式文档,大小52KB,内容为图文步骤说明,便于边看边操作。已有1062人学习下载,适合刚接触版本控制或希望规范代码托管流程的开发者参考。
1. 把本地代码推到 GitHub:不只是 git push 那么简单
很多人在本地写完代码,想着“推到 GitHub 不就三行命令的事”,结果卡在 SSH 密钥报错、push 被拒、提交了一堆 bin 和 obj 中间文件,甚至把 tag 推上去了远端却看不到。这份资源把整个流程串了一遍:注册建仓库、装 msysgit 客户端、配 SSH key、git add/commit/push、.gitignore 过滤编译产物、tag 标记版本节点。适合刚入职需要往公司仓库提代码的新人,也适合本地囤了一堆项目想统一托管、却一直没理顺 Git 工作流的开发者。按这套步骤走,从空仓库到成功推送大概十分钟,关键是把配置原理也讲清了,后面出问题知道去哪查。
2. 环境准备:客户端选型与 SSH key 配置的完整链路
2.1 msysgit 和 TortoiseGit:命令行与图形界面的取舍
Git 官方在 Windows 上的发行版经历过几次更名,早年最常见的叫 msysgit,它把 Git 核心功能编译成了 Windows 原生程序,同时附带一个 Bash 模拟环境,让你能在 Windows 上敲 Unix 风格的命令。装完之后右键菜单会多出Git Init Here和Git Bash两项,前者在文件夹里初始化本地仓库,后者打开命令行终端。
如果是纯新手,我会建议先强制自己用一段时间的命令行,不要急着装 TortoiseGit。原因很简单:Git 的文档、网上的问答、CI 脚本全是命令行语法,你用熟了图形界面,遇到变体场景(比如在服务器上操作)就抓瞎。TortoiseGit 的价值在于做完每一步操作后你能直观看到文件状态图标——绿色的勾代表已提交,红色的感叹号代表有改动,这对建立“文件当前处于什么状态”的直觉很有帮助。我一般会装好 msysgit 先把核心命令跑通,再补 TortoiseGit 当辅助查看工具,两者不冲突。
装完 msysgit 后在任意文件夹右键选Git Init Here,会生成一个.git文件夹,这就是本地仓库的“黑匣子”。里面存的 HEAD、config、objects 这些文件平时不要去手工改,除非你已经知道自己在干什么。
2.2 SSH key 生成:本地密钥与 GitHub 账号的绑定
要用 SSH 协议把本地代码推送到 GitHub,得先让 GitHub 认出“你是你”。HTTP 方式每次 push 都要输账号密码,SSH 方式配一次密钥就永久免密,这才是日常开发该用的方案。生成密钥的命令:
$ ssh-keygen -t rsa -C "your_email@youremail.com"这条命令干的活是生成一对 RSA 非对称密钥:私钥留在本地(默认路径~/.ssh/id_rsa),公钥复制到 GitHub 设置页。-t rsa指定密钥类型为 RSA,-C后面的字符串是注释,一般填邮箱方便区分是哪台机器的密钥。执行后系统会问存储路径和密码,直接回车用默认路径、空密码就行——如果设置了密码,每次 push 都要输一遍,反而违背了配 SSH 的初衷。
生成完去~/.ssh目录下找id_rsa.pub,用文本编辑器打开,全部复制。然后登录 GitHub,进Account Settings->SSH Keys,点Add SSH Key,标题随便填个能辨识的名字,Key 粘贴进去保存。验证网络连通:
$ ssh -T git@github.com第一次执行会提示是否信任该主机,输入yes,看到You've successfully authenticated, but GitHub does not provide shell access就说明密钥已经被 GitHub 接受。这句话翻译成人话:GitHub 认你了,但它只给你 Git 操作权限,不给你登录 shell 的权限,这是设计使然。
2.3 提交者身份的全局配置
SSH key 解决的是“传输通道的身份认证”,但每次 commit 里记录的作者信息(用户名和邮箱)是从 Git 配置里读的。这两者是独立的两套东西,很多人混在一起,导致密钥配好了推送却被告知身份不明。
$ git config --global user.name "your_name" $ git config --global user.email "your_email@youremail.com"--global表示对当前操作系统用户全局生效,之后这台机器上所有仓库的 commit 都带这组身份信息。如果公司项目要求用公司邮箱提交,而你个人项目想用另一个邮箱,就不要用--global,在仓库目录下执行不带--global的版本,这样只对该仓库生效。Git 读配置的优先级是:仓库级 > 全局级 > 系统级,仓库目录下的.git/config会覆盖全局配置。
这段配置还牵扯一个坑:GitHub 会把 commit 里的邮箱和账号绑定的邮箱做比对,不一致时虽然能推上去,但提交不会关联到你的账号头像,在 GitHub 上看到的是一排陌生的小灰头像。白折腾一次推送,代码历史却“掉”了贡献者信息。
3. 本地仓库与远程仓库对接:从 git remote 到 push 的完整闭环
3.1 初始化仓库与第一次提交
进入项目根目录,右键选Git Bash打开命令行,或者用 Windows Terminal 进到对应路径。第一次在这个目录里跑 Git 命令之前,先确认.git文件夹是否已经存在——如果之前点过Git Init Here就跳过低级初始化,直接看git status的输出。仓库还没有初始化时执行:
$ git init这个命令会创建一个空的.git目录,里面保存着这个仓库的全部元数据。之后就可以添加文件并做第一次提交了:
$ git add README.md $ git commit -m "first commit"git add把 README.md 从工作区挪到暂存区,git commit把暂存区的快照固化到版本历史里。-m参数后面的字符串是本次提交的留言,这个习惯从第一次提交就要养好,写清楚 “做了什么、为什么做”,不要写 “update” 或 “fix”,等翻三个月前的提交记录时你会感谢自己。
如果目录里有大量文件不想一个个添加,git add -A会把所有变动都加入暂存区——注意前置条件是你已经有一个合格的.gitignore,否则编译产物会一并提交进去。这块在第 4 章细说。
3.2 关联远程仓库的三种方式与边界
本地仓库和 GitHub 上的仓库建立关联,最常见的是用git remote add命令。在 GitHub 仓库页面复制 SSH 格式的地址,形如git@github.com:yourName/yourRepo.git,然后在本地执行:
$ git remote add origin git@github.com:yourName/yourRepo.gitorigin是远程仓库的默认别名,也是业内约定俗成的名字,后面所有和这个远程仓库的交互都用这个名字指代。加了之后可以用git remote -v查看当前关联的远程地址列表,确认拼写无误。这条命令的本质是往.git/config文件里写了一段配置,所以理论上也可以直接打开.git/config手工添加:
[remote "origin"] url = git@github.com:yourName/yourRepo.git fetch = +refs/heads/*:refs/remotes/origin/*但新手不建议手工编辑,原因有两个:一是格式错了 Git 会拒绝读取配置,报出让人摸不着头脑的错误;二是 fetch 那一行是 remote-tracking 分支的映射规则,写错会导致推送分支时行为诡异。用命令添加,Git 会帮你把配置项写全。
如果发现仓库地址拼错了,用git remote rm origin删掉再重新 add 就好,本地已经提交的 commit 不受影响。远程仓库名和本地仓库名也不要求一致,你可以把本地的my-project推到远端的awesome-tool,地址手动改就行。
3.3 push 的本质:提交历史的快进与拒绝
远程关联建立后,推送命令:
$ git push origin master这条命令把本地master分支的提交历史推送到远程origin仓库的同名分支。如果远程分支不存在,Git 会自动创建;如果远程分支上已经有别人推过的提交,而你的本地分支没有包含这些提交,Git 会拒绝推送,提示non-fast-forward。
这个拒绝是保护机制。Git 的提交历史是一根单向链表,远端分支的 HEAD 必须是你本地分支 HEAD 的祖先,推送才被允许。这不是小气,是防止把别人的提交“抹掉”。遇到非快进拒绝时,先git pull --rebase把远程新提交拉到本地并把你自己的改动重放到它们后面,解决冲突后再 push。
第一次向空仓库推送时还有个隐含步骤:GitHub 新建仓库时会提示你选择初始化 README、许可证和 .gitignore。如果选了其中之一,远程仓库就有一次提交了,本地直接 push 同样会触发 non-fast-forward。所以建仓库时建议什么都不选,保持空状态,本地推上去就是“干净”的首次提交。我自己的习惯是:远程建空仓库,本地初始化再推,这样整个提交历史的起点完全由自己控制。
4. .gitignore 的实战配置:把编译产物拦在版本库之外
4.1 为什么中间文件不该进版本库
编译型语言的项目在构建过程中会产生大量中间产物——C# 项目的bin和obj目录、Python 的__pycache__、C/C++ 的.o文件、IDE 的本地配置文件。这些文件有三大毒:体积大(一个bin目录动辄几十上百 MB,塞进 Git 仓库会让 clone 变得极慢);不可预测(每次编译内容都可能变化,导致 diff 噪音巨大);不可复现(别人拉下来也不会用这些文件,它们只属于你的本地环境)。
资源里给的 C# 项目示例就很有代表性:
bin obj *.suobin和obj是编译输出目录,*.suo是 Visual Studio 2010 时代的解决方案用户选项文件,记录每个开发者个人的窗口布局、断点位置,属于纯个人配置。这三行一写,git status立马清爽,只显示源码文件的变动。
4.2 编写规则的关键细节与通用模板
.gitignore的规则有几个容易踩的坑。第一,目录名不带斜杠会匹配任意层级的同名文件;带开头斜杠如/bin则只匹配仓库根目录下的bin。第二,*是通配符,*.suo匹配所有以.suo结尾的文件,不管在哪个目录。第三,感叹号!开头表示“不忽略”,比如*.log忽略所有日志文件,但!important.log强制保留某个特定的。这个反转规则经常被误用,要记住匹配顺序:后面的规则覆盖前面的。
一个 Java 后端项目我一般会这么写:
target/ *.class *.jar !.mvn/wrapper/*.jar .idea/ *.iml logs/ *.log前三行忽略 Maven 的编译产物和打包产物,.idea/和*.iml是 IntelliJ IDEA 的工程配置,logs/和*.log忽略日志。注意!.mvn/wrapper/*.jar这一行:Maven Wrapper 的 jar 包在target被整体忽略的规则下也会被忽略,但它是项目构建必需的文件,所以要单独“救”回来。
如果项目里写好了.gitignore,但发现有些文件还是被git add -A加进去了,十有八九是之前提交时这些文件没被忽略,已经进了版本库。.gitignore只对 untracked 文件生效,对已经跟踪的文件无效。这种情形的处理:先从版本库里删掉,再在.gitignore里加规则,而且要让git add把删除操作一起提交上去。
$ git rm -r --cached bin/--cached参数表示只从 Git 跟踪列表里移除,保留本地磁盘文件,恰好就是“我不想让这个目录被 Git 管,但文件我还要用”的需求。执行后 bin 目录的状态变成 untracked,.gitignore里的规则就接管了。
5. tag 标签:一套上手就能用的版本标记方案
5.1 轻量级标签与附注标签的差异
tag 解决的核心问题是“给某个状态的代码一个可回溯的名字”。需求管理里说“发个 v2.0 版本”,代码库里对应的是某一个具体 commit 的哈希值。哈希值8a5cbc2这种字符串没人记得住,v2.0好记。tag 本质上是给 commit 起别名。
查看所有标签:
$ git tag输出按字母序排列,不是按创建时间。已经发布了几十个版本之后,列出来会很长,可以过滤:
$ git tag -l 'v1.*'通配符*匹配任意字符,这条命令只显示 v1 开头的标签。带引号的原因是防 shell 自行展开通配符,养成加引号的习惯。
创建轻量级标签:
$ git tag v1.0这个标签只是一个指向某个 commit 的引用,不带任何附加信息。创建附注标签:
$ git tag -a v1.0 -m 'first version'附注标签额外存储创建者、创建时间、注释信息,用git show v1.0能查看完整元数据。对公开发布的版本,我强烈建议用-a带注释的形式:万一发布后出了问题,至少有注释记录这次发布的动机和内容概要。
还有签名标签git tag -s v1.0 -m 'first version',用 GPG 私钥对标签做签名验证,确保标签是本人创建且内容未被篡改,适合开源项目对外发布时使用。没有 GPG 私钥就用不了,对内部项目完全没必要。
5.2 给历史 commit 打标签与推送的隐藏坑
当前代码已经在开发了,才想起来上个版本忘了打标签,不需要把代码回滚回去,直接给历史 commit 打:
$ git log --oneline $ git tag -a v1.1 8a5cbc2git log --oneline以精简格式列出提交历史——每行一个提交,左侧是短哈希,右侧是提交留言。找到版本对应的哈希值(比如8a5cbc2),在 tag 命令后追加这个哈希,标签就打到了那个提交上。配合资源里的情景:现在的工作区处于 “v2.0 开发中”,但发现 v1.1 的代码节点还没标记,这种操作刚好补上缺口。
删除标签:
$ git tag -d v1.0验证标签(有 GPG 私钥时):
$ git tag -v v1.0最关键的坑在推送环节。git push不会推送 tag,这是 Git 的默认行为——分支的提交历史会推送,标签引用不会。在 GitHub 仓库页面上死活看不到自己建的 tag,大概率就是只执行了git push没管标签。推送全部标签:
$ git push origin --tags推送到远程后再想删,比本地麻烦得多,本地git tag -d删完了远程还得发一条删除命令:
$ git push origin :refs/tags/v1.0这个语法是推送一个空引用来删除远端引用,和git branch -d的远程删除逻辑一致。先理解这个机制,后面误操作远程标签就不慌了。
6. 实战排障:SSH 超时、push 被拒、密钥失效这些坑我都踩过
6.1 SSH 连接失败:现象、排查路径与解决
现象:执行ssh -T git@github.com后长时间卡住,最终报Connection timed out;或者第一次连接时提示输入密码但不知道密码是什么。
原因:GitHub 拒绝访问一般有三层原因——本地网络和 GitHub 服务器的 TCP 连接没建立起来;SSH key 不对;SSH config 文件里写了错误的 Host 配置。最容易被忽略的是第三层:~/.ssh/config里如果有一段配置指定了错误的用户或端口,所有 SSH 连接都会被带偏,而且报错信息不是一眼能看出来的那种。
解决:先用ssh -T git@github.com -v打开详细日志模式,逐行看输出,看卡在Connecting to github.com还是Authentications that can continue。前者是网络问题,换个网络环境或检查防火墙;后者是密钥问题,确认ssh-add -l里是否有你的私钥,没有就ssh-add ~/.ssh/id_rsa加载一次。我遇到过一次很隐蔽的情况:环境变量GIT_SSH_COMMAND被某个脚本改成了指向另一个 ssh 程序,导致 key 路径解析错乱,检查这个变量把问题定位出来。
6.2 push 被拒 non-fast-forward:别硬推
现象:git push origin master报错error: failed to push some refs,提示远端包含你本地没有的提交。
原因:远程仓库在你最后一次 pull 之后有新的提交,你的本地分支历史是远程历史的分叉,直接推送会覆盖别人的提交。新手最容易犯的错误是git push -f强推——强制覆盖远端历史,如果那是团队共享分支,等于把别人的工作直接扔掉。
解决:先git fetch origin把远端最新的提交拉到本地但先不合并,然后git rebase origin/master,把自己的提交逐个重放到远端最新提交的后面。遇到文件冲突时手动解决,git add后继续git rebase --continue。整个过程不产生多余的 merge commit,历史是一条笔直的线。如果 rebase 到一半想放弃,git rebase --abort会把你拉回操作之前的状态,这是保命的后手。
6.3 .gitignore 规则没生效:检查跟踪状态
现象:.gitignore里写好了忽略规则,git add -A后git status依然显示这些文件被暂存。
原因:这些文件在写.gitignore之前已经被git add过,进入了 Git 的跟踪列表。忽略规则对已经跟踪的文件不生效,这是 Git 设计如此,不是规则写错了。
解决:git rm -r --cached 目录名把文件从跟踪列表移除但不删磁盘文件,然后git add -A提交一次,记录里的文件就删掉了,后续.gitignore规则开始生效。从那以后我每次新建项目,第一件事就是写.gitignore,文件还没写一行代码就先把这个放进去,绝不先把编译产物 add 进去再想起来补忽略规则。
6.4 GitHub 上不显示提交者身份:邮箱匹配问题
现象:代码成功推上 GitHub,但提交历史里看不到账号头像,显示的是灰色默认头像。
原因:GitHub 把 commit 里的邮箱和账号绑定的邮箱做匹配,两者不一致就不认这个提交属于该账号。常见原因是开发者有多台电脑,某台机器的git config用了另一个邮箱。
解决:git log --format='%an <%ae>'查看所有提交的作者邮箱,和 GitHub 设置里的邮箱比对。差异的修改方法不止一种,成本最低的是在 GitHub 设置里把本地 commit 用的邮箱加为自己的备用邮箱,GitHub 就会把历史提交归到账号下面。注意这个匹配是按邮箱来的,不是按用户名。如果想让历史提交的邮箱统一改掉,git filter-branch可以重写历史,但任何改写历史的方式都会导致所有仓库副本的哈希变化,有协作者时慎用。
6.5 远程 tag 和本地 tag 不同步:符号引用困惑
现象:本地删除了 tag,再执行git push origin --tags,远端 tag 还在。
原因:--tags参数的模式是把本地所有 tag 推上去,在本地已经删除的 tag 对应的远程引用不会自动删除,远程 tag 成为了“孤儿引用”。这个机制让很多人误以为推送是同步操作,但 Git 的推和拉都是单向的,不会自动做“等同化”。
解决:删除远程 tag 用显式的空引用推送git push origin :refs/tags/v1.0。如果整套 tag 全部乱了,先git fetch --prune origin让本地远端快照同步,再逐一对齐。规范的做法是推送 tag 前先和同事约定命名规则,发布后尽量不删,非要删就走走流程,对远端 tag 的修改会直接影响其他人拉取的代码版本标识。
6.6 最后:这套流程之外还想再进一步
把资源里的这套主流程跑顺之后,再往后可以研究git rebase -i交互式变基来整理提交历史,git reflog找回误删的提交,以及 pre-push 钩子在推送前自动跑测试。分支策略也值得花时间设计:团队项目里master保持稳定,功能分支开发,git merge --no-ff保留合并记录。一个小习惯很值得推荐——所有命令在提交前先git status看清楚将发生什么,再git diff看改动内容,最后才 add 和 commit。这个“两步确认”的习惯帮我拦下了不少误提交,从那以后我每次推代码都强制走一遍这套确认流程,希望帮到你。
本文还有配套的精品资源,点击获取