日常开发中把代码交给版本控制系统管理几乎已经成了职业底线。而“代码托管在GitHub”这件事更是绝大多数开发者的日常刚需本地仓库备份、多人协作、持续集成、代码审查全都依赖这一层基础建设。最近工作室来了几个新同事用的都是Ubuntu 18.04的机器在联调时发现好几个人连基本的git托管流程都没走顺要么SSH密钥没配好要么本地分支和远程对不上来回折腾浪费了不少时间。正好借这个机会把Ubuntu 18.04下从零开始用git把代码托管到GitHub的完整流程梳理一遍。这篇东西覆盖了环境准备、SSH认证配置、本地仓库与远程关联、日常协作分支操作以及高频报错的排查思路适合刚接触版本控制的同学也适合那些在Linux环境下配置过几次但总是半懂不懂的开发者。看完自己动手走一遍基本能告别“代码无处安放”的尴尬。1. 为什么要把代码托管到GitHub很多新手最初接触git是从“本地记个版本”开始的改一下代码commit一下至少能回滚。但本地版本管理有一个天然缺陷——换机器、换硬盘、团队协作的时候所有历史记录都带不走。GitHub这类托管平台解决的不光是备份问题它把整个版本库放到远端让多台机器、多个成员可以基于同一个仓库进行协作同时提供Issue、Pull Request、Actions等配套能力把代码托管从单纯的存储扩展成了完整的协作闭环。1.1 版本控制的核心价值git的核心能力在于三点快照式版本记录、分支切换的轻量化、分布式仓库的完整性。快照式记录意味着每次commit都是整个项目状态的完整保存而不是差异补丁。这和SVN的增量存储思路有本质区别。git把每个版本的完整内容都存下来所以撤销、回滚、比较历史版本都非常直接。分支之所以切换得快也是因为每个分支本质上只是指向某个commit的指针切换分支只需要改变指针引用不需要重新铺文件。分布式则是git的设计灵魂。每一个克隆下来的仓库都是完整独立的包含着全部分支、全部历史。只要有一台机器上的仓库完好整个项目就能恢复。GitHub在整个体系里扮演的是“中心枢纽”的角色它不垄断数据但提供了统一的同步入口、权限控制和协作流程。1.2 GitHub在协作流程中的角色GitHub的价值不只是“把代码放上去”。实际开发中它承接了几个关键工作流代码审查通过Pull Request机制成员提交的代码需要经过review才能合并到主分支这个流程可追溯、可讨论。问题跟踪Issue模块用来记录bug、功能需求和commit关联后能实现“哪个提交解决了哪个问题”的追踪。自动化集成GitHub Actions可以监听push、pull_request等事件自动跑测试、构建、部署把CI/CD直接嵌进托管环节。多端同步办公室台式机、家里笔记本、服务器环境都能通过git clone / pull保持代码一致这是纯本地版本控制做不到的。我当时之所以把工作室所有项目都迁到GitHub上就是有一次同事的U盘坏了写了两周的模块没提交到仓库全没了。从那之后立了一条规矩任何项目第一件事先建远程仓库本地分支推进的同时保持定期push。后面踩过的坑、总结的经验就是这篇博文的内容基础。2. Ubuntu 18.04环境准备与Git安装很多人的Ubuntu机器上其实已经自带了git或者装过旧版本。但如果是刚装好的系统第一步一定是确认git环境是否可用。Ubuntu 18.04自带的软件源里git版本不算新好在git这个工具核心命令非常稳定源里版本完全够用。2.1 安装与版本确认先检查系统里是否已经有gitgit --version如果提示找不到命令直接通过apt安装sudo apt update sudo apt install git -y注意这里有个容易被忽略的点apt update和apt install要连着执行。有些新装的系统软件源索引是空的跳过update直接install大概率报错“Unable to locate package”。安装完成后再次确认版本git --versionUbuntu 18.04的源里装出来一般在2.17.x左右。这个版本应对日常操作足够了。如果你对版本有洁癖想用新版可以用官方PPA安装但我的建议是除非有特殊需求否则不要折腾原因后面会在常见问题里解释。2.2 配置用户信息git的每一次commit都会记录作者信息。这个信息不是登录用户名而是git配置的用户名和邮箱。新机器第一次提交前必须设置否则commit会失败或者产生一串奇怪的默认值。配置分两个层级git config --global user.name your_name git config --global user.email your_emailexample.com--global表示全局生效写入的是~/.gitconfig文件对所有仓库有效。如果只想对当前仓库生效去掉--global即可写入的是.git/config文件。为什么邮箱必须配置因为GitHub就是通过邮箱关联commit和账号的。你在GitHub上看到的绿色贡献格子就是靠committer邮箱匹配到账号。如果邮箱没配置好或者和GitHub账号邮箱不一致提交记录就不会关联到你的账号上这会让你的贡献统计出现“缺失”的诡异情况。我当时就踩过这个坑机器上配了一个旧邮箱提交了好几天代码后发现自己GitHub主页上绿色格子一片灰排查半天才发现邮箱不匹配。后来统一用GitHub账号里的主邮箱重新配置新提交才正常关联。所以配置完顺手验证一下git config --global user.name git config --global user.email2.3 代理与网络访问的准备GitHub在某些网络环境下访问速度不太理想尤其是clone大仓库或者push大文件时。Ubuntu 18.04系统层面如果你的网络环境需要代理才能稳定访问外网建议在shell环境里设置代理变量export http_proxyhttp://proxy-server:port export https_proxyhttp://proxy-server:port但这属于临时生效重启终端就没了。长期使用可以把这两行追加到~/.bashrc末尾echo export http_proxyhttp://proxy-server:port ~/.bashrc echo export https_proxyhttp://proxy-server:port ~/.bashrc source ~/.bashrc需要说明的是git自身也有代理配置但走系统环境变量会更省心覆盖HTTP和HTTPS操作。我自己实测下来只要代理配置正确clone大仓库的速度提升非常明显。如果网络环境完全没问题这一段可以跳过。3. SSH密钥配置连接GitHub的安全通道把代码push到GitHub之前得先解决身份认证问题。GitHub支持两种认证方式HTTPS和SSH前者需要每次输入账号密码或使用token后者通过密钥对自动认证签名一致就能推送不用重复输密码。在日常开发中SSH是绝大多数开发者的选择原因很简单——省事且安全。3.1 为什么选择SSH而非HTTPSHTTPS方式本身没有错早期也大量使用。但它有两个痛点一是每次push都要输入凭据虽然git可以缓存或使用credential helper但配置麻烦二是密码或token在传输中被截获的风险始终存在。SSH密钥对是公钥加密体系的典型应用私钥留在本地权限600公钥上传到GitHub连接时GitHub用公钥加密挑战数据本地的私钥能正确解密身份就验证通过。全程不需要传输密码安全性和便利性兼顾。3.2 生成SSH密钥对检查是否已有现成的密钥ls -la ~/.ssh/如果看到id_rsa和id_rsa.pub两个文件说明之前生成过。没有的话执行ssh-keygen -t rsa -b 4096 -C your_emailexample.com参数解释-t rsa指定密钥类型为RSA-b 4096指定密钥长度为4096位安全性足够-C是备注信息通常填邮箱方便在GitHub上识别运行时它会询问保存路径和passphrase。保存路径直接回车用默认的~/.ssh/id_rsa即可。passphrase是私钥的额外保护密码我建议不要设置。虽然设置了更安全但每次SSH连接都要输一遍passphrase之后用git push也会频繁提醒会直接把自动化的方便性抹平。如果你对安全性有更高要求可以设置日常开发推荐留空。生成成功后查看公钥内容cat ~/.ssh/id_rsa.pub3.3 将公钥添加到GitHub账户登录GitHub网页端进入头像下方的Settings在左侧菜单找到SSH and GPG keys点击New SSH key按钮。Title可以填“Ubuntu 18.04 desktop”之类的标识方便知道这是哪台机器的密钥。Key文本框粘贴刚才cat命令输出的整个公钥内容注意包括开头的ssh-rsa一直到末尾的邮箱备注全都要复制完整。最后点Add SSH key保存。这里有一个重要提示公钥内容是以ssh-rsa开头的长串字符复制时一定要注意不能漏字符、不能多空格。如果公钥格式不对添加时网页端会直接提示格式错误。3.4 测试SSH连接配置完成后先测试是否连通ssh -T gitgithub.com第一次连接会看到一条确认提示询问是否信任GitHub的主机密钥输入yes回车。如果配置正确会返回一段类似“Hi username! Youve successfully authenticated...”的消息。看到这句话说明SSH通道已经完全打通。如果这里卡住了排查思路后面会在常见问题部分展开。有一个高频原因是~/.ssh目录权限不对私钥文件权限过宽会导致SSH拒绝使用。执行下面的命令修正chmod 700 ~/.ssh chmod 600 ~/.ssh/id_rsa顺带一提有时候多台设备共用同一个GitHub账号每台机器都应该单独生成密钥并添加到账号下。不用担心多密钥冲突GitHub允许添加多个公钥标识清楚即可这也是为什么添加公钥时Title要写清楚的原因。4. 本地仓库与GitHub远程仓库关联SSH通道通了之后核心操作就登场了把本地项目推送到GitHub远程仓库。很多人卡在这一步因为涉及几个容易混淆的概念远程仓库别名origin、分支名main/master、以及本地分支和远程分支的追踪关系。下面把整个流程拆开讲。4.1 在GitHub上创建远程仓库打开GitHub首页点击右上角加号图标选择New repository。Repository name就是仓库名建议和本地项目名保持一致方便管理。Description填一句话说明即可。可见性选Private私有还是Public公开取决于项目性质个人代码建议私有分享项目可以公开。我的建议是创建时不要勾选“Add a README file”“Add .gitignore”“Choose a license”这些初始化选项。原因很简单这些文件如果在远程初始化了本地仓库push时就需要先pull融合多一步操作。我习惯先创建空仓库所有初始化文件都在本地准备然后直接推上去。这个习惯在多设备协作时尤其好使能避免很多仓库历史不一致的麻烦。创建完成后页面会显示一个仓库地址选SSH格式形如gitgithub.com:username/repo-name.git这就是后面要关联到本地的远程地址。4.2 本地仓库初始化与首次推送假设本地已经有一个项目目录进入目录cd ~/projects/your-project git initgit init会在当前目录生成一个.git隐藏目录这是整个版本库的核心。然后用git add把项目文件加入暂存区git add ..表示把所有文件加入但如果有敏感文件比如配置文件里的密码、密钥等不要这么干。后面专门讲.gitignore的用法。提交到本地仓库git commit -m Initial commit这里说一个非常关键的点git的add和commit是两个不同阶段。add把文件放进暂存区commit才真正生成一个历史快照。很多人一开始不理解总是改完代码忘了add直接commit结果发现提交的内容和预期不符。简单类比暂存区类似超市购物车你往车里放东西是add结账清单是commit——只有结账清单上的东西才会成为版本记录。关联远程仓库git remote add origin gitgithub.com:username/repo-name.git这里的origin只是一个别名代表后面的远程地址。用remote -v可以查看当前的远程配置。首次推送时需要将本地master或main分支与远程分支建立关联并推送。Ubuntu 18.04系统上git init的默认分支名通常是master而GitHub新建仓库默认主分支已经改为main。两种叫法本身没有对错关键是本地和远程要对应上。如果你本地是master分支想推送到远程main最简单的做法是推送前先把本地的分支改名git branch -m main然后执行首次推送git push -u origin main-u参数全称是--set-upstream它会将本地main分支和远程的main分支建立追踪关系。建立之后后续推送就直接用git push省略参数。4.3 分支重命名与默认分支设置这里要稍微展开说一下main和master的问题。多年前GitHub默认分支叫master后来为了让命名更具包容性默认改成了main。很多老教程、老项目还是master新创建的仓库则是main。如果你本地仓库是旧的master分支而GitHub新仓库默认main推的时候会得到两种结果推送master分支到远程maingit push origin master:main但这种方式只是把内容推上去了远程分支的历史实际上没有建立正确关联后续容易乱。更好的方案是本地分支直接改名git branch -m master main git push -u origin main这样本地和远程的分支名统一为main追踪关系稳定。如果推送成功后还想把远程旧的master分支删掉可以用git push origin --delete master但在团队协作的仓库里不要轻易删远程分支除非确认所有人都已经迁移到新分支。4.4 从远程克隆已有仓库如果是新机器上接手已有项目不需要本地init。直接git clone gitgithub.com:username/repo-name.gitclone操作会把远程仓库完整复制到本地包括所有分支和历史。默认情况下的分支会自动关联develop分支或其它分支需要手动切换。进入目录后cd repo-name git branch -a这个命令能列出所有本地和远程分支。如果需要开发某个远程分支git checkout -b feature-x origin/feature-x这条命令从远程分支创建本地分支并自动建立追踪关系。5. 日常开发中的Git协作与分支管理远程仓库建好只是第一步真正体现git能力的是日常开发中多个分支并行、成员协同推进的节奏。这里把我在实际使用中积累的分支操作、冲突处理、提交规范一次性讲透。5.1 分支模型与主分支保护个人项目和团队项目的分支策略差异很大。单打独斗时直接在main分支上提交也没多大问题。但团队协作时我强烈建议遵循一套简单的分支模型main分支始终是稳定可用状态不应该直接提交代码feature/xxx分支每个新功能或修复从main切出来独立开发功能完成后通过Pull Request合并回main这套模型的好处是main永远保持可发布状态任何成员的分支出了问题都不会污染主分支。关于Pull RequestGitHub网页端的New pull request可以从一个分支合并到另一个分支提交后可以review代码、留言讨论通过后再点击Merge pull request完成合并。这是多人协作的核心流程比直接在本地merge再push更能保留审查历史。GitHub上还能给main分支设置branch protection rules强制要求Pull Request审查通过才能合并这属于团队治理范畴个人仓库看需求开启。5.2 .gitignore与敏感文件防护这是新手最容易忽略的环节。没有.gitignore会把一堆不该提交的文件塞进仓库编译产物、依赖包、本地配置文件、IDE设置、密钥文件等。时间长了仓库会越来越臃肿克隆速度变慢甚至导致敏感信息泄漏。项目根目录创建.gitignore文件写入需要忽略的规则。给出一个后端项目的常见模板# 依赖目录 node_modules/ vendor/ # 编译产物 dist/ build/ *.class *.jar # 环境配置 .env .env.local config/database.yml # 日志 *.log # 系统文件 .DS_Store Thumbs.db # IDE配置 .idea/ .vscode/关键点在于.gitignore只对尚未被git跟踪的文件生效。如果你之前已经用git add .把某些文件提交进去了再写进.gitignore不会让它自动被忽略需要用命令移除git rm -r --cached .env git commit -m Remove env file from tracking--cached参数的意思是只从git索引中移除保留本地文件。这是处理误提交的一个很实用的操作。另外真正敏感的数据数据库密码、API密钥、私钥文件不应该以任何形式出现在仓库里哪怕有.gitignore也不行。正确做法是使用环境变量或单独的配置文件让.gitignore把配置文件排除掉。这个意识要在项目开始时就建立否则一旦密钥提交上去即使之后删除在git历史里还是能找到需要改密钥才能根除。5.3 日常同步与提交规范日常开发流程像我自己的工作习惯是开发前先git pull origin main拉取最新主分支然后切到自己的功能分支。写完代码按模块分批次add和commit不要攒一大堆文件一次性commit。每个commit只做一件事信息写清楚这是commit message的意义所在。关于commit信息建议遵循约定俗成的格式type(scope): subject常用的type包括feat新功能、fix修复、docs文档、style格式调整、refactor重构、test测试等。举例feat(user-service): add login api fix(order): resolve null pointer when order is empty这种写法在回看历史时能快速定位改动类型和影响范围配合git log --oneline查看提交历史信息密度非常高。好的commit信息三五年后回来看依然能看懂当时的意图。5.4 合并与冲突解决功能分支开发完成需要合入主分支。常规操作是先切回main分支拉取最新的远程更新然后merge功能分支git checkout main git pull origin main git merge feature/xxx如果两个分支改动了同一个文件的同一段代码git无法自动决定保留哪份内容就会产生冲突。这时候git status会显示both modified状态打开冲突文件能看到形如 HEAD 当前分支的内容 合并进来的内容 feature/xxx手动处理的方式是保留你需要的内容删掉、、这三行标记保存文件后执行git add 冲突文件名 git commit -m merge: resolve conflicts between main and feature/xxx解决冲突尽量不要用编辑器自带的盲选功能一定要理解两个版本各自的意思再决定取舍。我现在处理冲突的原则是不确定的代码先找对应的同事确认绝不自己拍脑袋决定删掉谁的逻辑。冲突解决本身不可怕可怕的是解决冲突时把别人的正确逻辑误删了。6. 常见问题与排查技巧实录折腾git和GitHub对接谁都会遇到各种报错。这里整理了我实际使用中频率最高的一批问题每条都带上排查思路和解决方案可以当作速查表用。6.1 SSH连接类问题症状一ssh -T gitgithub.com提示Permission denied (publickey)。这通常意味着SSH没有找到可用的私钥。按顺序排查确认私钥文件存在且权限正确ls -la ~/.ssh/id_rsa权限应为-rw-------确认公钥已经添加到了GitHub账号可以重新cat公钥内容到GitHub设置里对比一下确认SSH agent里加载了密钥执行ssh-add ~/.ssh/id_rsa这里有个细节如果之前设置过passphrase每次重启机器后SSH agent不会自动加载密钥需要手动ssh-add。如果你发现自己重启后突然push失败大概率是这个问题。症状二ssh -T gitgithub.com连接超时或卡住不动。这种情况在国内使用GitHub时偶尔遇到通常不是密钥问题而是网络连不上GitHub的22端口。排查先试一下ssh -T -p 443 gitssh.github.comGitHub提供了443端口的SSH通道作为备用。如果这个能通就改一下本地SSH配置文件cd ~/.ssh touch config编辑config文件Host github.com Hostname ssh.github.com Port 443 User git改完后重新测试ssh -T gitgithub.com。这个方法是我解决SSH断连问题时最常用的一招原理是绕过可能被防火墙封锁的22端口改用标准的HTTPS端口跑SSH协议兼容性非常好。6.2 推送拉取类问题症状三git push报错error: failed to push some refs。这个报错的根本原因通常是远程仓库有本地没有的提交。解决思路是先把远程更新拉下来合并后再推送git pull origin main git push origin main如果pull时报错fatal: refusing to merge unrelated histories说明本地和远程的仓库没有共同的历史起点最常见的原因是本地init后直接用git pull而远程仓库初始化时勾选了README等文件。解决方案git pull origin main --allow-unrelated-histories合并成功后就可以正常推送了。这个--allow-unrelated-histories参数比较冷门但是遇到这个场景就是必须的。症状四git push提示The requested URL returned error: 403。通常发生在使用HTTPS方式推送时用户名密码错误或没有推送到自己仓库的权限。如果你在用HTTPS方式建议直接转SSH方式从源头解决。如果必须用HTTPS注意GitHub从某段时间起不再接受账户密码认证需要使用Personal Access Token作为密码。症状五git pull之后本地文件丢失。这个大多数时候不是“丢失”而是切换分支导致的文件状态变化。先检查当前分支git branch git status如果你之前在某个功能分支上有未提交的改动直接切到main分支会导致工作区文件改变看起来像文件被删了。解决办法是回到原分支找回改动git checkout 原分支名如果是commit了但找不到可以用git reflog查看所有分支的引用变化记录找到对应的commit哈希后恢复。reflog是git里最有用的恢复工具之一在新手培训时我一定会重点讲到。6.3 提交记录类问题症状六提交后发现commit信息写错了。如果还没push可以直接修改最近一次的commit信息git commit --amend -m correct message如果已经push了修改后需要强推git push --force-with-lease origin main注意--force-with-lease在团队协作场景下比--force安全因为它会检查远程分支是否被其他人更新过避免误覆盖别人的提交。这个细节是对新人特别值得强调的安全操作。症状七提交时自动转换行尾导致整个文件都变成修改状态。在Linux上编辑Windows传过来的代码文件比较常见git默认的行为可能会把CRLF转成LF导致diff一看整个文件全变了。此时可以在仓库根目录配置.gitattributes* textauto *.sh text eollf *.bat text eolcrlf比较系统性的做法是在.gitattributes里明确各类文件的换行符规则。这个文件应该在项目初始化时就定义好团队统一遵守可以少掉无数莫名其妙的diff问题。6.4 其他容易卡住的细节有几个实际操作里的小坑一并说透。第一个是.git目录误删。有人觉得.git是个隐藏垃圾目录想清理删了就完蛋——整个仓库历史和远程关联全都没了。虽然可以用git init重新初始化再关联远程但本地所有commit历史会丢失相当于从1开始重新累积。真的误删了唯一恢复路径是看看有没有其他设备的克隆仓库。第二个是git add了大文件导致push失败。GitHub限制单文件不能超过100MB超过会直接拒绝。如果仓库里不小心提交了大文件需要从历史中彻底移除这个操作比较麻烦建议从一开始就用.gitignore把大文件目录排除掉。第三个是多个GitHub账号的SSH配置。如果在同一台机器上有个人账号和工作账号需要创建多个密钥对并在config文件里用Host别名区分。基本模板Host github-personal HostName github.com User git IdentityFile ~/.ssh/id_rsa_personal Host github-work HostName github.com User git IdentityFile ~/.ssh/id_rsa_work然后远程仓库地址里的gitgithub.com替换成对应的Host别名。这个方案我在多账号场景下实测过能稳定区分身份不会出现两个账号的提交串号。7. 关于Ubuntu 18.04系统的额外说明Ubuntu 18.04本身是个很稳的老版本系统网上能搜到的大量教程也都是基于这个版本写的。但有几个版本相关的细节容易被忽略值得单独提出来。系统的软件源里git版本偏低这是好事也是坏事好事是命令稳定老机器跑起来毫无压力坏事是缺少一些新特性比如某些稀疏检出、部分路径跟踪相关的功能要新版git才有。如果你不需要这些特性安心用系统自带的版本就好。工作室里有台机器一直跑Ubuntu 18.04git版本2.17日常用了两年没有任何问题。另外Ubuntu 18.04自带的OpenSSL和CA证书都比较旧偶尔会遇到HTTPS证书校验失败的情况。如果git clone时报证书错误先更新CA证书sudo apt install ca-certificates再不行可以临时设置GIT_SSL_NO_VERIFYtrue绕过校验但这个只是应急手段不建议长期使用。如果考虑升级Ubuntu 18.04已经过了官方支持期从安全角度长远看建议规划迁移到更新的LTS版本。但业务系统如果已经稳定运行短时间内不想动那这篇博文里的git配置方案依然完全适用——git本身跨系统、跨版本兼容性极好配置思路在哪里都一样。我个人在实际操作中的体会是代码托管流程走顺之后整个开发的效率和安全感都会上一个台阶。你不需要再担心U盘坏掉、电脑丢失、改错不可逆这些事只管专注写代码剩下的交给git和GitHub这层基础设施就行。希望这篇基于Ubuntu 18.04的完整流程能帮你把这条路一次走通少走我当年那些弯路。