玩转git

442 阅读22分钟

一.Git简介

  • 1.1 Git的特点
  1. 最优的存储能力
  2. 非凡的性能
  3. 开源
  4. 很容易做备份
  5. 支持离线操作
  6. 很容易定制工作流程
  • 1.2 web端提供git服务的产品
  1. git
  2. GitHub (全球最大的开源社区)
  3. GitLab (自己提供了CI机制,一般公司内部使用)

二.安装Git

在Git官网上有详细的安装方式 起步 - 安装 Git

三.Git 配置

// 全局配置 --global
git config --global user.name 'your_name'
git config --global user.eamil 'your_eamil@domin.com'

config的三个作用域(local、global、system

git config --local  (local只针对某个仓库有效)
git config --global (global对当前用户的所有仓库有效)
git config --system (system对系统所有登录的用户有效,基本不用)

显示config的配置,加--list

git config --list --local
git config --list --global
git config --list --system

清除设置

git config --unset --local user.name
git config --unset --global user.name
git config --unset --system user.name

四、建Git仓库

两种场景

  1. 把已有的项目代码纳入Git管理
cd 进入项目所在的文件夹
git init
  1. 新建的项目直接用Git管理
cd 进入某个文件夹
git init your_project #会在当前路径下创建和项目名同名的文件夹
cd your_project
  1. 配置git config
git config --local user.name 'your_name'
git config --local user.eamil 'your_eamil@domin.com'
  1. 创建readme
touch的作用是更改一个文件或目录的时间。touch readme.MD 如果readme.MD不存在,则创建空文件readme.MD
touch readme.MD 
  1. 提交
git add readme.MD
git status 查看文件状态
git commit -m 'Add readme'
git log 查看日志

若未add,直接commit或git status则会报如下错误(untracked files)

config的设置中,local的设置的优先级要高于global

添加文件后直接commit会报错,原因是什么?

五.认识工作区和暂存区

工作目录 ---> 暂存区 ---> 版本历史

1. git add files 将文件添加到暂存区
2. git commit 

cp命令来拷贝文件

clear清理屏幕

git add -u (u代表update,代表将已经被git管理的文件一起提交到暂存区)

六.给文件重命名的简便方法git mv

readme文件重命名为readme.md

mv readme readme.md
git status

发现git提示,你 deleted: readme,并新建了 read.md

以上问题的解决方法

git add readme.md 添加
git rm readme 删除
git status

上述操作后提示,修改成功

renamed: readme -> readme.md
git reset --hard 将刚才的修改撤回

以上操作可以用一个更简单的命令解决

git mv readme readme.md

这样就用git mv一个步骤解决了变更文件名

七.通过git log查看版本演变历史

git log 查看提交的完整信息
git log --oneline 只查看commit内容
git log -n4 查看最近4个
git log -n4 --oneline 组合使用
git branch -v 查看本地有多少分支
git checkout -b testing 4323c83dcdf 基于之前的某个分支创建testing分支
git commit -am "add testing file"  将工作区的内容直接创建到版本历史库中
git branch -av 查看有多少分支
git log 查看当前分支的历史
git log --all 查看所有分支的历史
git log --all --graph 通过图像化的方式查看log
git log --oneline
git log --oneline --all
git log --oneline --all -n4
git log --oneline --all -n4 --graph
git log --oneline --all testing 指定的testing分支不起作用
git help --web log 通过web查看git的所有命令

八.用图形界面工具来查看版本历史

在命令行输入如下命令:

gitk

不过这个图形界面的清晰度有待提高

cheery-pick就是挑选某一次的commit提交放到另一个分支

尊重版权,显示 AuthorCommitter

九.了解.git目录

进入.git文件

cd .git
ls -al

  • 1.查看HEAD文件
cat HEAD
显示
ref: refs/heads/RELEASE // 显示当前所在分支RELEASE
git checkout testing    // 切换分支
cat HEAD                // 再次查看HEAD文件
ref: refs/heads/testing // 显示当前所在分支为testing
  • 2.查看config文件
cat .git/config

显示当前项目的基础配置信息

  • 3.查看refs文件夹(引用:存放tag和tag的信息)
cd refs
ls -al
显示目录文件夹
heads // 分支 drwxr-xr-x d代表文件夹
tags  // 标签(里程碑标识)

cd heads/
显示文件夹
RELEASE
testing

cat RELEASE
acedf122defesci*******
git cat-file -t acedf122defesci*******  // 查看类型 代表RELEASE的指针指向哪一个commit
commit // 显示为commit类型

git branch -av  // 查看分支进行验证

// 验证testing分支
cat testing
acedf122defesci******* // 哈希值如果足以标志唯一性则显示的较短,否则则显示的较长
  • 4.查看objects,是git文件系统的核心内容(只要任何文件的文件相同,就是唯一的blob,这样设计的好处是什么呢?)
cd objects/
ls -al 查看详情
cd 任意文件夹
ls -al
tree 是一棵树
看树的内容用
git cat-file -p acedf122defesci*******  // 查看类型
git cat-file -p fresfer3432cxrff******* // 查看到可能是css文件

十. committreebolb三个对象之间的关系

存储是git的核心技术点,git提供优良的存储能力,对版本控制系统是非常关键的。因为版本控制系统就是管理文档,但文档的变更是很频繁的,如果没有一套好的存储机制的话,库就会越来越大,性能就会越来越差。因此设计一个良好的存储机制,对版本控制系统来说非常关键!

  • Git对象彼此关系

(* 注:图片来自网络)

三者之间的关系

commit

一个commit对应一棵树tree(文件夹),代表取出某个commit,快照,通过树呈现出来。

tree

包含index.html readme styles,每个文件都是一个blob,blob和文件名没有任何关系,只要内容相同,就是唯一的blob,可以大大的节约存储空间。

blob

对不同的文件用cat命令可以进行查看

小结:

  • 1.commit中一定有一棵树
  • 2.这颗树tree里一定包含那个时间点到底包含了什么文件夹,什么文件
  • 3.这个树小有包含了各个小的树,层层展开,最后的页面节点就到了blob

十一.数一数项目中tree的个数

新建的git仓库,有且仅有一个commit,仅仅包含/doc/readme,请问内含多少个tree,多少个blob?

git init
ls-al
mkdir doc
git status // 没有变化
echo "hello,world!" > readme 创建readme并写入hello,world!
cd ../
git status
find .git/objects -type f // 空的
git add doc
git status
find .git/objects -type f // 有值,创建了blob
git commit -m "add readme"
find .git/objects -type f // 变成了4个object
git cat-file -t 083e18d   // tree
git cat-file -p 083e18d   // doc
git cat-file -t 2d832d90
git cat-file -p 2d832d90  // hello,world!
  • 1.首先,使用git log查看提交记录。
  • 2.可以看到其中的一次提交,为"5e15bae4036c9",查看commit "5e15bae4036c9" 执行命令git cat-file -p 5e15bae4036c9

执行结果表明:该提交中包含tree

  • 3.再次执行git cat-file -p 18baf1927939c1d88ab

执行结果表明:该提交中包含README.md 、package.jsonblob类型的文件和publictree类型的文件夹

从上面的例子,我们就可总结出git中committreeblob之间的关系。

十二.分离头针情况下的注意事项

git branch -av
git log
git checkout c4ccf8ffceb142 // 输出了如下信息

提示You are in 'detached HEad' state.

正处于分离头指针状态。本质就是正工作在一个没有分支的状态下。 在现在的状态下有了commit,又切到了其他分支工作,这样在分离头指针状态下写的内容,很可能被git当做垃圾清理掉。所以,你想做修改变更,一定要与某个分支挂钩。

  • 在什么状态下可以用好分离头指针呢? 假如你想尝试某个功能,发现效果不好,可随时丢弃。
git commit -am"Change style" // add + commit
git checkout RELEASE // 切换之后有如下提示

gitk --all

git branch fix_css c4ccf8ffceb142 // 根据指示信息新建分支,进行保留

git log  进行查看
  • 分离头指针的作用:
    1. 做临时性修改的时候进行尝试
    1. 修改后的内容如果要保留,可根据提示进行操作

十三.进一步理解HEADbranch

HEAD不仅可以指向某个分支,还能指向某个commit

git checkout -b fix_readme fix_css // 基于fix_css分支创建一个新的分支fix_readme,并切到新分支fix_readme

git log -n1 // (HEAD指向了新的分支)
gitk --all
cat .get/HEAD // ref: refs/heads/fix_readme

git diff 3ede435ded 490ikl9jjih  // 查看两个commit的diff
// HEAD也可以用来快速的指代
git diff HEAD HEAD~1  // 当前和父亲的比较
git diff HEAD HEAD^   // 当前和父亲的比较
git diff HEAD HEAD^^   // 当前和父亲的父亲的比较 等同于git diff HEAD HEAD~2

十四.怎么删除不需要的分支

gitk -all
git branch -d fix_readme // 删除fix_readme分支
git branch -D fix_readme // 强制删除fix_readme分支
git branch -av // 查看剩余的分支

不能在当前分支删除当前分支

十五.怎么修改最新commitmessage

git log -1 // 查看最近一次commit
git commit --amend // 即可修改此次commit信息

十六.怎么修改老旧commitmessage

变基 rebase

Rebase主要有两种场景:1.合并多次提交记录 2.合并分支

Git中整合来自不同分支的修改主要有两种方法:mergerebase

merge合并的结果是把两个分支的最新快照以及两者最近的公共祖先进行三方合并,合并的结果是生成一个新的快照(并提交)。rebase则是另一种方法,这种操作就叫做变基。你可以使用rebase命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一样。它的原理是首先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master)的最近共同祖先 C2,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向目标基底 C3, 最后以此将之前另存为临时文件的修改依序应用。 merge和rebase,这两种整合方法的最终结果没有任何区别,但是变基使得提交历史更加整洁。 你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的,但它们看上去就像是串行的一样,提交历史是一条直线没有分叉。

变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。

总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

git rebase -i 27de432fe(要修改的父级的commit哈希)  将pick改为r 对修改后的文件进行保存。自动弹出另一个界面。此时即可修改此次commit文案

要修改的父级的commit哈希

r进行修改,r代表reward,只修改commit信息,出现如下信息 退出后,自动跳转到新的界面。

修改后保存退出,提示如下信息,修改成功

git log // 你会发现修改commit之后,hash值也发生了修改

git log -n5 --graph

可以对任何一次commit进行修改

变基:基于某个基础进行变动

  • 使用rebase合并分支步骤
  1. 从master分支切出dev分支,进行开发
git:(mater)  git checkout -b feature1

  1. 此时,你的同事完成了一次hotfix,并合并入了master分支,此时master已经领先于你的feature1分支了:

3. 此时,我们想要把master分支的改动同步到feature分支,我们常用的为merge,执行:

git:(feature1)  git merge master

合并之后的结果如上图,执行:

git:(feature1)  git log

就会在记录里发现一些merge信息,但是我们觉得这样污染了commit记录,想要保持一份干净的 commit,此时,git rebase就排上用场了。

  1. 先回退到同事 hotfix 后合并 master 的步骤:

  1. 使用 rebase
git:(feature1)  git rebase master

这里rebase做了什么呢?

首先,git 会把 feature1 分支里面的每个 commit 取消掉;
其次,把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下;
然后,把 feature1 分支更新到最新的 master 分支;
最后,把上面保存的 patch 文件应用到 feature1 分支上;

commit记录可以看出,feature1分支是基于hotfix合并后的masterfeature1便成为了最领先的分支,而且没有mergecommit记录,看起来舒服多了。

  1. rebase的过程中,可能会出现冲突conflict。此时,git会停止rebase并会让你去解决冲突。在解决冲突后,用git add去更新这些内容。注意:add之后不要执行git commit,需要执行git rebase --continue
git rebase --continue

这样git会继续执行余下的patch补丁文件了。

  1. 在任何情况下,我们都可以用--abort命令参数来终止rebase的行动,并且分支会回到rebase开始前的状态。
git rebase --abort

附:merge到一半如何取消合并

git merge --abort

附:解决冲突git mergegit rebase中的ourstheirs

git mergegit rebase的区别:git rebase不会产生额外的commit。在实际项目中,不同的公司倾向的使用方法不一样,rebase能使commit是一条线,相对较为清晰,而merge则能完整的记录所有的操作,相对更加完整。

解决冲突:merge的时候,用git checkout --theirs留下传入的,用git checkout --ours留下当前的。 rebase的时候,用git checkout --ours留下传入的,用git checkout --theirs留下当前的。 在merge时,ours 指代的是当前分支,theirs 代表需要被合并的分支。而在 rebase 过程中,ours 指向了修改参考分支,theirs 却是当前分支。 mergerebase对于ourstheirs的定义是完全相反的。

十七.把连续的多个commit整理成1个

squash

合并最近的4次提交记录:

git rebase -i HEAD~3

这时候,会自动进入vi编辑模式:

s cacc52da add: qrcode
s f072ef48 update: indexeddb hack
s 4e84901a feat: add indexedDB floder
s 8f33126c feat: add test2.js

# Rebase 5f2452b2..8f33126c onto 5f2452b2 (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#

并提示了如下几个命令

p, pick = use commit
r, reword = use commit, but edit the commit message
e, edit = use commit, but stop for amending
s, squash = use commit, but meld into previous commit
f, fixup = like “squash”, but discard this commit’s log message
x, exec = run command (the rest of the line) using shell
d, drop = remove commit

将你需要合并的提交的记录前缀改为s

s cacc52da add: qrcode
s f072ef48 update: indexeddb hack
s 4e84901a feat: add indexedDB floder
p 8f33126c feat: add test2.js

如果保存的时候,你碰到了这个错误:

error: cannot 'squash' without a previous commit // 注意不要合并先前提交的东西,也就是已经提交远程分支的纪录。

如果你异常退出了 vi 窗口,不要紧张:

git rebase --edit-todo

这时候会一直处在这个编辑的模式里,我们可以回去继续编辑,修改完保存一下:

git rebase --continue  

查看结果

git log // 两次提交合并成了一次,减少了无用的提交信息。
git rebase -i r435dr34oi4od // 多个变更的父commit的hash值

将连续的几个commit合到一个commit中

十八.把间隔的几个commit整理成一个

比如git中有两个关于readme的修改,但是是不连续的,如何将这两个commit合并为同一个?

git rebase -i 43s4dreji34fc3 // 父级commit

将两个要合并的commit放在一起,使用s命令

git rebase --continue // 将commit合并

git status
 
gitk --all // 用树形结构查看,出现了两个没有祖先的commit

将刚提交的commit撤回

git reset --soft HEAD^ // 撤回上一次的提交,并保留代码

HEAD^的意思是上一个版本,也可以写成HEAD~1

如果你进行了2次commit,想都撤回,可以使用HEAD~2

--mixed --soft --hard这几个参数的区别:

  • --mixed不删除工作空间改动代码,撤销commit,撤销git add . ,这个为默认参数,git reset --mixed HEAD^ 和 git reset HEAD^ 效果是一样的。
  • --soft不删除工作空间改动代码,撤销commit,不撤销git add .
  • --hard删除工作空间改动代码,撤销commit,撤销git add . ,注意完成这个操作后,就恢复到了上一次的commit状态。

commit注释写错了,可以执行下面的代码:

git commit --amend

此时会进入默认vim编辑器,修改注释完毕后保存就OK了。

十九.怎么比较暂存区和HEAD所含文件的差异

将暂存区的文件和当前分支最近的一次commit做比较

此时已经执行过git add,文件提示已变为绿色,此时需要--cached命令来查看暂存区的diff

git diff --cached // 查看暂存区

二十.怎么比较工作区和暂存区所含文件的差异

bare repository裸仓库,即.git下的内容

git diff -- index.html

git diff默认比较工作区和暂存区的区别

二十一.如何让暂存区恢复成和HEAD的一样

使用情景:将文件放入到暂存区之后,在工作区又有了一次修改,感觉比暂存区的要好,此时就想把暂存区的删除掉,恢复成和HEAD的一样。

git reset HEAD // 将所有的暂存区的文件取消掉,回到了工作区
git diff --cached // 验证暂存区与HEAD是否一致

二十二.如何让工作区的文件恢复成和暂存区一样

git add index.html
git diff --cached // 暂存区
git status

// 根据提示
git checkout -- index.html

变更工作区用git checkout ,变更暂存区用git reset.

二十三.怎样取消暂存区部分文件的更改

git reset HEAD -- index.html // 将暂存区的文件撤回到工作区

二十四.消除最近几次提交

git branch -av
git log
git reset --hard 5def35df890d // 回到某次提交之前,之前的将会被删除

回到你指定的commit的hash值的时候,之前的将会被删除

二十五.查看不同提交的指定文件的差异

git log -n8 --all --graph              // 查看最近8次的提交,所有分支的情况
git diff temp master                   // 查看temp和master所有的区别
git diff temp master -- index.html     // 查看temp和master中index.html的区别
git branch -av
git diff 5df3fd1 03defx53 -- index.html // 用hash的方式查看

此种查看方式都是查看最新的一次提交

二十六.正确删除文件的方法

rm readme             // 工作区删除
git rm readme         // 暂存区删除
git reset --hard HEAD // 恢复到了和头指针相同的状态

其实只需要下面这一条命令即可:

git rm readme         // git rm + 具体文件,直接将删除的文件放在暂存区,就不需要在工作路径下删除文件了

二十七.开发中临时加塞了紧急任务怎么处理

git stash             // 存放起来
git stash pop         // 释放出最新的stash
git stash list        // 查看stash信息,是否还存在
git stash apply       // 1.把之前存放的文件释放出来,2.stash中的信息还存在
git reset --hard HEAD // 使其与头指针相同
  • popapply的区别:
    1. 执行pop之后会丢掉之前的信息
    1. 执行apply之后之前的信息还存在于堆栈之中

二十八.如何指定不需要Git管理的文件

.gitignore文件名不能修改,否则将会失效

# gitignore
HEAD
.cache
v*
alias

# For testing
test/bak
.urchin.log
.urchin_stdout
test/**/test_output

node_modules/
npm-debug.log

.DS_Store
current
/default-packages

# Only apps should have lockfiles
npm-shrinkwrap.json
package-lock.json
yarn.lock

ls -al
vi .gitignore
rm -rf doc // 删除doc文件夹

二十九.如何将Git仓库备份到本地

  • http/https需要用户名和密码的方式
  • ssh 需要公私钥的方式

哑协议与智能协议:

  • 直观区别: 哑协议传输进度不可见;智能协议传输可见。
  • 传输速度: 智能协议比哑协议传输速度快。

备份特点:可多点备份

git push   // 将本地的变更push到远端
git fetch  // 将远端的变更直接拉到本地

git clone --bare https://****** // 不带工作区的裸仓库

git push 远端名称

三十.注册一个GitHub账号

官网https://github.com/

三十一.配置公私钥

将本地和远程进行连接

在git中找到help,搜索ssh

Adding a new SSH key to your GitHub account

cd ~/.ssh

若已经存在,需要将本地的ssh进行备份

存在一对公私钥的文件

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

再次查看文件夹: id_rsa为私钥,id_rsa.pub为公钥。

cat id_rsa.pub // 查看公钥,如下图,需要将下面的内容粘贴到远端的github账户

需要将上面的内容粘贴到远端的github账户

可以有多个ssh key

用ssh协议的好处: push时不需要用户名和密码了,可以智能识别

三十二.在GitHub上创建个人仓库

Description:可以让别人快速的搜到你的项目

Add .gitignore

Add a license MIT License开源协议

三十三.把本地仓库同步到GitHub

git remote -v

git remote add github ****** // 新增远端站点

fetch:从远端拉最新的代码

fast-forward

git pull // 1. git fetch 2.git merge
git fetch origin ***** // 将远端分支拉到本地,还未与本地的分支产生关联
git branch -v  // 查看本地分支
git branch -va // 查看远端分支
git merge github/master // fatal: refusing to merge unrelated histories
让两个历史不想关的树merge到一起
git help
相同的分支非fast forward
git merge --allow-unrelated-histories github/master // 基于远端生成了一个新的commit

git push origin master
git remote -v 查看远程版本库信息
git remote add githup <url> 添加githup远程版本库
git fetch githup 拉取远程版本库
git merge -h 查看合并帮助信息
git merge --allow-unrelated-histories githup/master 合并githup上的master分支(两分支不是父子关系,所以合并需要添加 --allow-unrelated-histories)
git push githup 推送同步到githup仓库
在不用merge用rebase的情况下怎么操作
1)先把远端的分支 fetch到本地,然后,再执行 rebase 。
2)直接 git pull --rebase 。

fast forward到底是

  • 本地分支往远端分支做push,如果远端分支不是本地分支的祖先,那它俩就不是 fast forward 了。反之,它俩就是fast forward的关系。

三十四.不同人修改了不同的文件该如何处理

两个人修改了不同的文件

git add -u
git commit -m ""
git push
git branch -av
git fetch // 将远端的分支拉到本地
git checkout -b feature/add_commands remotes/add_commands
git pull // note about fast-forwards 要变成fast-forwards的关系
git fetch // [ahead 1, behind 1] 远端有一个commit比本地的新
git merge // merge 远端分支
git push

标签管理

git tag                      // 查看所有标签
git tag name                 // 创建标签
git tag -a name -m "comment" // 指定提交信息
git tag -d name              // 删除标签
git push origin name         // 标签发布

三十五.撤销或回退已经addcommitpush的提交

命令

reset是重置的意思。多观察一下git reset这个命令。

git reset  【commit ID】
git revert 【commit ID】 // revert可以保留完整的历史记录。对于和其他人协同的项目,使用git rever是最好的。
git reset 146bd5*****27da72  html/commons/index.html 通过hash直接将某个文件回退到
git checkout html/commons/index.html 回退之后,只是回退到了暂存区,此时还需要用checkout命令将本地的文件删除掉,再进行commit等操作,这样本地文件就与远程一致了
git push --force

commit之后撤回:

git reset --soft HEAD^ (HEAD~1)
撤销多个commit HEAD~n // 可进行尝试

参数含义:
--mixed: 不删除工作空间改动代码,撤销commit,并且撤销git add .
--soft:  不删除工作空间改动代码,撤销commit,不撤销git add .
--hard:  删除工作空间改动代码,撤销commit,撤销git add .

commit注释写错,改注释

git commit --amend

三十六.Git忽略规则.gitignore不生效

在项目开发过程中个,一般都会添加 .gitignore 文件,规则很简单,但有时会发现,规则不生效。

原因是 .gitignore 只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。

那么解决方法就是先把本地缓存删除(改变成未track状态),然后再提交。

git rm -r --cached .

git add .

git commit -m 'update .gitignore'

持续更新...