首先来简单介绍下Git,Git是一款分布式版本控制工具,需要存储大量的文件内容数据,其中Git objects发挥了关键的作用。Git objects基本存储了Git的一切,多种类型的对象和对象之间的引用共同构成了Git版本控制的核心。
Git objects的存储特点如下:
Git Objects会松散存储在仓库的.git/objects/目录下
Git Objects采用key-value的形式进行存储
不同的Object,Git会为其生成唯一的key
key的格式是SHA1形式(表现为40位的16进制字符串)或者SHA-256
Git Objects包含了4种类型,其中:
Blob对象用于存储文件内容;
Tree对象用于进行目录索引,它保存了某个目录下的一个完整视图;
Commit对象负责存储提交的元数据(作者、说明、父提交等),同时还引用一个根目录的索引视图;
Tag对象一般用于标记发行版等提交时刻及相关信息。
这里通过一个简单实例做个演示,首先通过git init test 初始化一个仓库并进入仓库中,创建一个README.md文件,然后执行git add README.md添加到暂存区,再用git commit -s 提交代码,在test下新建src和docs目录存放代码main.c和文档project.txt,再将src和docs使用git add和git commit完成提交,最后执行git tag -a -m "Release V1.0" v1.0 记录tag为1.0版本。我们分别将两次提交作为commitA 和 commitB,那么两次提交中Git Objects的组织关系如下图:
我们刚刚提到Git对象是用SHA1或者SHA-256格式存储,由于其表现形式不方便使用,所以需要用到Git 引用。它通常存储在仓库的.git/refs/目录下,Git支持4种默认的Builtin reference:
Branches:分支的引用。存储位置:.git/refs/heads/
Tags:标签的引用。存储位置:.git/refs/tags/
Remotes:与远端协作时使用。存储位置:.git/refs/remotes
Symbolic:符号引用 HEAD。存储位置:.git
这里的符号引用,即为指向引用的引用,HEAD是一种特殊的符号引用,它指向你当前所在分支的引用,你可以通过git symbolic-ref HEAD来查看符号引用 HEAD的相关信息。
底层内容能够更好的帮助我们了解一款软件,下面阿巩尽量白话介绍基础并引入常用命令。
01 创建仓库
“仓库”是指与项目相关的所有文件,包括源代码、工程文件、资源文件和一些配置信息,它可以是本地的仓库,也可以是保存在远端服务器上的仓库。仓库之间可以相互同步,你可以把本地代码同步到远程服务器上。
获取本地Git仓库:
进入目录:$ cd /Users/user/my_project
初始化git仓库:$ git init
创建一个名为 .git 的子目录
跟踪项目文件:$ git add .
初始提交git仓库:$ git commit -m 'initial project version'
从远端服务器获取已存在的Git仓库:
$ git clone https://github.com/libgit2/libgit2
自定义本地仓库的名字:
$ git clone https://github.com/libgit2/libgit2 mylibgit
02 更新仓库
检查当前文件状态:
$ git status
$ git status -s(简洁输出)
?? 未跟踪的文件
A 新添加到暂存区
M 修改过的文件
查看具体哪些行发生变化
查看尚未暂存的文件:
$ git diff
查看已暂存的将要添加到下次提交里的内容:
$ git diff --staged
提交更新
提交暂存区:
$ git commit
命令+消息:$ git commit -m "...“
不使用暂存区:
$ git commit -a -m “...” 不需要git add步骤
如果想更改一次提交
$ git commit --amend
从暂存区移除文件
未暂存清单中删除
$ git rm
暂存区移除
$ git rm --cached README
文件保留在磁盘,但是并不想让 Git 继续跟踪
移动文件
$ git mv file_from file_to
查看提交历史
$ git log
撤销操作
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
最终你只会有一个提交——第二次提交将代替第一次提交的结果。
撤消对文件的修改:
放弃修改: $ git checkout --
... Git 会用最近提交的版本覆盖掉
取消暂存的文件
$ git reset HEAD ...
也可以跳过暂存区域直接从仓库取出文件或者直接提交代码。
使用远程仓库
git clone 默认设置本地 master 分支跟踪克隆的远程仓库的 master 分支
$ git clone https://github.com/schacon/ticgit
查看使用的远程仓库
$ git remote -v
添加远程仓库
$ git remote add pb https://github.com/paulboone/ticgit
使用字符串 pb 来代替整个 URL
拉取远程仓库有但本地没有的信息
$ git fetch
只下载数据到本地仓库,需要另外进行合并
配置了跟踪远程分支
自动抓取后台合并到当前分支:$ git pull
推送到远程仓库
$ git push origin master
查看某个远程仓库
$ git remote show origin
打标签记录发布节点 v1.0 、 v2.0 等等)
列出标签
$ git tag
$ git tag -l "v1.8.5*"
创建标签
保存版本信息—————附注标签
$ git tag -a v1.4 -m "my version 1.4"
带-a,-m为添加到标签内的信息
不保存,只用作标识—————轻量标签
$ git tag v1.4-lw
直接提供标签名即可
查看标签
$ git show v1.4-lw
后期打标签
查看提交历史
$ git log --pretty=oneline
在指定版本上打标签
$ git tag -a v1.2 9fceb02
由于git push 命令并不会传送标签到远程仓库服务器上需要额外推送标签
$ git push origin v1.5
$ git push origin --tags 批量推送
删除标签
$ git push origin --delete
Git分支
在进行提交操作时,Git会保存一个提交对象,该提交对象会包含一个指向暂存内容快照及作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。
分支创建
$ git branch testing 在当前所在的提交对象上创建一个指针
查看各个分支当前所指的对象
$ git log --oneline --decorate
分支切换
$ git checkout testing
$ git checkout master
1、使 HEAD 指回 master 分支
2、恢复master 分支所指向的快照
分支的新建与合并流程
1、新建并切换
$ git checkout -b hotfix
2、提交修改
$ git commit -a -m 'fixed the broken email address'
3、切换到master分支
$ git checkout master
4、将hotfix合并回master分支部署到线上
$ git merge hotfix
5、删除hotfix分区继续在自己的分支工作
删分支:$ git branch -d hotfix
切回分支:$ git checkout iss53
提交修改:$ git commit -a -m 'finished the new footer [issue 53]'
查看分支信息
查看分支
$ git branch
分支前的 * 字符:它代表现在检出的那一个分支
查看每一个分支的最后一次提交
$ git branch -v
查看哪些分支已经合并到当前分支
$ git branch --merged
查看所有包含未合并工作的分支
$ git branch --no-merged
远程分支操作
推送
$ git push
$ git push origin serverfix:awesomebranch 来将本地的 serverfix 分支推送到远程仓库上的 awesomebranch 分支。
拉取
$ git fetch origin
合并
$ git merge origin/serverfix
建立跟踪
$ git checkout -b serverfix origin/serverfix
如果检查的分支不存在,使用快捷方式跟踪
$ git checkout --track origin/serverfix
删除远程分支
$ git push origin --delete serverfix
只删除指针,可恢复
变基
变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。
使用 rebase 命令将提交到某一分支上的所有修改都移至另一分支上
$ git rebase --onto master server client
“取出 client 分支,找出它从 server 分支分歧之后的补丁, 然后把这些补丁在 master 分支上重放一遍,让 client 看起来像直接基于 master 修改一样”
$ git checkout master
$ git merge client
$ git rebase master server
$ git checkout master
$ git merge server
$ git branch -d client
$ git branch -d server
五种提高代码提交原子性的基本操作
1、把工作区里代码改动的一部分转变为提交
$ git add -p
$ git diff --cached 命令检查
2、对当前提交进行拆分
当前提交,指的是当前分支的 HEAD 指向的提交。
1、git log 查看历史
2、git show 确认提交内容改动
3、git branch temp 命令产生一个临时分支 temp,指向当前 HEAD
便于恢复
git reset --hard temp
4、git reset HEAD^ 命令
当前分支指向目标提交 HEAD^
5、git add -p分块提交
3、修改当前提交
修改conmmit message
git commit --amend 命令
修改的是文件内容
1、使用 git add、git rm 等命令把改动添加到暂存区
2、git commit --amend修改信息
3、输入 Commit Message 保存退出即可
4、交换多个提交的先后顺序
1、git branch temp 产生一个临时分支
2、git log --oneline --graph 来确认当前提交历史
3、git rebase -i origin/master
4、使用pick命令把A、B重新放到origin/master上
pick 7b6ea30 Add a new endpoint to return timestamp
pick b517154 Change magic port number to variable
5、修改非当前提交
1、运行 git rebase -i origin/master
2、把原来的“pick b517154”的一行改为“edit b517154”(b517154 是提交 A 的 SHA1)
告知 Git rebase 命令,在应用了 b517154 之后,暂停后续的 rebase 操作,
直到我手动运行 git rebase --continue 通知它继续运行
3、git log --oneline --graph --all,确认当前 HEAD 已经指向了我想要修改的提交 A。
4、修改文件,之后用 git add < 文件名 >,然后再运行 git commit --amend。
5、git rebase --continue,完成 git rebase -i 的后续操作
在 A 之上再应用提交 B,并把 HEAD 重新指向了 B,从而完成了对历史提交 A 的修改。
多分支工作流具体步骤
假设现在有这样一个业务场景:我首先开发需求 C,并把它的提交 C1 发送到质量检查中心;然后开始开发需求 D,等到 C1 通过质量检查之后,我立即将其推送到远程共享代码仓中去。
这个流程分为以下四个阶段:
阶段 1:开发需求 C
使用 git checkout -b feature-c origin/master 产生本地分支 feature-c,并跟踪 origin/master,然后进行 C 的开发,产生提交 C1。
阶段 2:开发需求 D
使用 git checkout -b feature-d origin/master 产生一个分支 feature-d 并跟踪 origin/master,然后进行 D的开发,产生提交 D1。
阶段 3:推送提交 C1 到远端代码仓共享分支
使用git checkout feature-c 把分支切换回分支 feature-c,执行git fetch和git rebase origin/master来确保我的分支上有最新的远程共享分支代码;然后执行git push推送 C1。
阶段 4:继续开发 D1
执行git checkout feature-d 把分支切换分支 feature-d,执行git fetch和git rebase origin/master,使用git log --oneline --graph feature-c feature-d来查看提交状态。
欢迎你与我交流,催更扯淡一条龙~
参考:
https://git-scm.com/
极客时间《研发效率破局之道》
阿里云《Git教程》