Git版本管理实用指南

特别声明,本博文仅作个人日常使用Git参考之用。主要内容总结来源于:廖雪峰官网的Git教程🌻

🔎 什么是Git

Git是目前世界上最先进的分布式版本控制系统,是Linux的创建者用C开发的。GitHub网站2008上线,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub。所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。

📀 版本管理

版本库又名仓库,英文名repository,可以简单理解成一个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。使用Git作版本管理需要理解它工作的几个基本概念:工作区暂缓区版本分支。工作区就是被管理的那个目录,工作区中有一个隐藏目录.git(不属于工作区而是Git的版本库)。Git的版本库里存了很多东西,其中最重要的就是称为stage的暂缓区,还有Git为我们自动创建的第一个分支master,以及指向master分支当前版本的一个指针叫HEAD。
工作机理:Git时刻监查工作区与HEAD所指版本之间的差异,git status指令可以查看当前工作区所有文件所处的几种状态:文件未被跟踪(Untracked files)修改未放到暂存区(Changes not staged for commit)暂存区的内容未提交(Changes to be committed)工作区与版本库完全一致(Clean working tree)。工作区的修改通过git add xxx逐个放到暂存区,然后通过git commit一次性提交暂存区的所有修改到对应分支。
在这里插入图片描述

  • 安装Git
    • 在Ubuntu Linux系统安装Git指令:sudo apt-get install git。老一点的系统需要把命令改成sudo apt-get install git-core,因为以前有个软件也叫GIT(GNU Interactive Tools)。
    • 配置远程仓库账户(可选):GitHub仓库之间的传输是通过SSH加密的,所以需要配置用户参数。
      • 方式1:配置账户名。在终端输入命令:git config --global user.name accountnamegit config --global user.email youremail@example.com
      • 方式2:创建秘钥对。先在用户home目录下查看是否存在.ssh目录(一般包含id_rsaid_rsa.pub这两个文件)。如果没有,打开Shell创建SSH Key:ssh-keygen -t rsa -C "youremail@example.com",一直按回车,不需要设密码。id_rsa是私钥,不能泄露出去;id_rsa.pub是公钥,可以放心地告诉任何人。登陆GitHub,打开“Account settings”,“SSH Keys”页面点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容。
  • 创建版本库
    • 将一个目录变成Git管理的仓库,只需在目录下执行命令git init。对应目录下将会添加一个.git的目录,这个目录是Git来跟踪管理版本库的。
    • 把文件加入仓库包括两步:1. 将“文件添加”这一改动添加到暂存区git add filename;2. 从暂存区提交到版本库git commit -m 'add a new file'。注意:add可以逐个文件添加,而commit会把暂缓区的所有修改一次性提交当前的分支结点。Git也提供了快速add多个文件的命令:
      • git add .git add *:将修改操作的文件和未跟踪的新添加文件都添加到版本库的暂缓区(不包括删除操作)。
      • git add -u .-u表示将已经跟踪的文件中的修改和删除添加到暂缓区。
      • git add -A .:将所有已跟踪的文件的修改、删除、以及为跟踪的新添加文件都加到暂缓区。建议在工作区创建.gitignore文件记录那些需要忽略的文件。
    • 删除文件:对于工作区中已经add或commit的文件,如果在文件管理器中把它删掉了 (即rm filename)。这时Git将检测到这一修改操作,因为工作区和版本库不一致了。现在有两个选择:1. 确实要从版本库删除该文件,那就git rm filename并且git commit;2. 如果是不小心删错了,可以从版本库恢复到工作区git checkout -- filename
  • 管理修改
    • Git跟踪并管理的是修改(文件的增删改等状态变化,每个commit对应一个变化单元),而非文件。HEAD所指向的提交结点(commit)潜在地表示版本库中的一个版本:即从首次commit的内容到目标commit结点之间顺序修改链路的叠加结果。
    • 对比工作区文件与版本库中版本的差异:git diff HEAD -- filename
    • 当提交完了才发现漏掉了几个文件或提交信息写错了,此时可以使用git commit --amend提交命令来重新提交(终端将弹出vim编辑器用于编辑上次提交附带的说明信息)。在执行该命令前,仍需要按常规add命令添加漏掉的文件到暂缓区。如此,Git只会更新上次的commit内容而不会新增一个commit。
  • 版本切换(重点!灵活运用
    • 查看版本操作的历史记录:git log。该命令显示从首次commit到当前commit(HEAD所指)的提交日志。每次commit的版本都对应一个十六进制的commit id。在Git中,用HEAD表示当前版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^,往上100个版本写成HEAD~100
    • 版本切换命令:git reset [--soft | --mixed | --hard] [commit_id]。该命令重置HEAD到另一个commit,那么原始HEAD与重置目标commit之间的差异集合将如何处理取决于该命令的三个参数。
      • --soft表示将原始HEAD和重置目标commit之间的所有变更集都放在暂缓区中等待提交,且保持工作区内容不会变动。使用场景可能是用于合并一个更有意义的commit。
      • --hard表示将清除原始HEAD与重置目标commit之间所有的信息提交,同时将重置目标commit对应的版本完全同步到工作区,且保持工作树clean。这将撤销工作区中所有未提交的修改内容,是个有风险的操作。
      • --mixed(默认参数)表示将清除原始HEAD与重置目标commit之间所有的信息提交(包括临时add到暂缓区的内容),但工作区内容不会变动(Git检测当前HEAD与它的差异表现为unstaged或untracked)。
    • 由于版本回退,在git log维护的commit记录中(仅记录最初提交到当前HEAD所指commit之间的完整链路)可能不再包含曾经存在的某些commit结点。如果需要回到那些的版本,只能依靠git reflog打印命令记录,并从中找到对应的commit id。
    • git checkout -- filename可以将工作区的文件回到和版本库或暂缓区(若该文件已经添加到暂存区后又作了修改)一模一样的状态。显然,该命令会丢弃工作区中尚未提交到暂缓区的修改。

✂️ 分支管理

在版本回退里,每次提交Git都把它们串成一条时间线,这条时间线就是一个分支。默认情况下只有一条时间线,在Git里这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
在这里插入图片描述

【1】开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点;每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。
【2】当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上;Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!
【3】从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变;
【4】假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并;
【5】合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支。

  • 创建分支
    • 创建分支dev并切换到改分支:git checkout -b dev-b参数表示创建并切换,相当于以下两条命令:git branch devgit checkout dev。最新版本的Git提供了新的git switch master命令来切换分支,以及创建并切换到新的分支git switch -c dev
    • 列出所有分支:git branch。当前分支前面会标一个*号。
  • 合并分支
    • 切换到dev分支提交修改之后,再切回master分支是看不到刚才的修改的,因为master分支此刻的提交点并没有变。通过命令git merge dev把dev分支的工作成果合并到master分支上。
  • 删除分支
    • 合并完成后可以选择删除dev分支:git branch -d dev。注意:当分支还没有被合并,如果删除将丢失掉修改,这种情况下要强行删除,需要使用大写的-D参数git branch -D dev
  • 解决冲突
    • 当master分支和feature分支各自都分别有新的提交,就变成了分支结点图【6】。这种情况下Git无法执行“快速合并(Fast-forward)”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。当有文件存在冲突,必须手动解决冲突后再提交,或者放弃这次合并git merge --abortgit status也可以告诉我们冲突的文件。合并完成后的分支结点图如【7】。在这里插入图片描述

    • 通常合并分支时,如果可能,Git会用Fast forward模式,但这种模式下合并分支后,会丢掉分支信息。如果要强制禁用Fast forward模式,可用git merge --no-ff -m "merge with no-ff" dev(因为本次合并要创建一个新的commit,所以加上-m参数把commit描述写进去)。这样Git就会在merge时生成一个新的commit,从分支历史上就可以看出分支信息。

    • 用带参数的git log --graph --pretty=oneline --abbrev-commit也可以看到分支的合并情况。

  • 分支合并策略
    • 在实际开发中,我们应该按照几个基本原则进行分支管理。首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
    • 功能开发都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
    • 每个协同开发的成员都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。所以,团队合作的分支图看起来就像这样:在这里插入图片描述
  • Bug分支
    • 每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。但是当前feature分支只进行到一半没法提交,又临时需要去修复master分支的bug时,我们就需要用到Git提供的stash功能。git stash把当前工作现场“储藏”起来,等以后恢复现场后继续工作。此时用git status查看工作区可以发现是干净的,可以创建分支修复bug。
    • git stash list命令看看"储藏"起来的工作现场列表。恢复现场有两种方式:1. git stash apply恢复现场后stash内容不会被删除,需要显式地git stash drop来删除;2. git stash pop恢复现场的同时把stash内容也删除了。
    • 在业务场景需要的情况下,可以多次stash,恢复的时候指定stash名称即可:git stash apply stash@{0}
    • 在master分支上修复了bug后,我们要想一想dev分支是早期从master分支分出来的,所以,这个bug其实在当前dev分支上也存在。同样的bug,要在dev上修复,我们只需要把bug_commit_id这个提交所做的修改“复制”到dev分支。切换到dev分支后,执行Git提供的cherry-pick命令:git cherry-pick bug_commit_id,Git自动给dev分支做了一次新的提交。

🚀 远程仓库

Git是分布式版本控制系统,同一个Git仓库可以分布到不同的机器上。怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。实际情况往往是这样,找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。GitHub这个网站就是提供Git仓库托管服务的,只要注册一个GitHub账号,就可以免费获得Git远程仓库。

  • 本地仓库关联远程仓库(先有本地库)
    • 本地已创建的版本库可以通过命令git remote add origin git@github.com:yourname/reponame.git与Github上新建的空仓库关联。关联成功后,远程库的名字就是origin,这是Git默认的叫法,也可改成别的。
    • 把本地库master分支的所有内容同步到远程库:git push -u origin master。由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
  • 克隆远程仓库(先有远程库)
    • 远程库已经存在,用git clone git@github.com:yourname/reponame.git即可克隆一个本地库。当我们从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。
  • 解绑远程库
    • 如果添加的时候地址写错了或者就是想删除远程库,可以用git remote rm <name>命令。使用前,建议先用git remote -v查看远程库信息。此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。
  • 协同开发
    • 【推送分支】:把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支:git push origin master,Git就会把该分支推送到远程库对应的远程分支上。实际开发中,并不是一定要把本地分支往远程推送:
      • master分支是主分支,因此要时刻与远程同步;
      • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
      • feature分支是否推到远程,取决于我们是否和其他成员合作在上面开发。
      • bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
    • 【抓取分支】:默认情况下,从远程库clone后只能看到本地的master分支。如果要在dev分支上开发,就必须创建远程origin的dev分支到本地:git checkout -b dev origin/dev。在dev分支开发时,可以把dev分支push到远程:git push origin dev。在协同开发中,push的修改可能与远程仓库版本已有的修改冲突(可能其他成员已经推送了类似的修改)而导致push失败。推送存在冲突时,一般需要git pull把最新的提交从origin/dev抓取下来,然后在本地手动解决冲突合并后,再推送
    • 当本地独立创建的dev分支未与远程仓库上的dev分支(其他成员创立并推送)链接时,git pull也会失败。需先要通过指令git branch --set-upstream-to=origin/dev dev建立二者的链接。
    • 多人协作的工作模式通常是这样:
      • 首先,可以试图用git push origin 推送自己的修改;
      • 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
      • 如果合并有冲突,则解决冲突,并在本地提交;
      • 没有冲突或者解决掉冲突后,再用git push origin 推送就能成功。

以上Git使用指南只是入门级的常用技能,更高阶的使用技能请参考Git官方文档

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值