Git笔记
一、概述
Git直接记录快照,而非差异比较,这样Git只关注修改的文件,没有修改的文件,Git不再重新存储该文件,而只是保留一个链接指向之前存储的文件,Git对待数据更像是个快照流。
Git很多情况使用哈希值,即Git数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
1.1三种状态
- 已提交 committed:数据已经安全的保存到本地数据库中了
- 已修改 modified:修改了文件,但还没有保存到数据库中
- 已暂存 staged:对一个已修改文件的当前保本做了标记,使之包含在下次提交的快照中。
1.2基本的 Git 工作流程如下:
- 在工作目录中修改文件。
- 暂存文件,将文件的快照放入暂存区域。
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
大型项目项目管理的一种思路:多人项目,本地在提交时往往不推荐使用pull 来拉去远端仓库中最新的代码到本地仓库中更新,原因是如果远端仓库的代码被其他人更改过,再pull时会出现冲突的情况(例如本地修改A.java,远端也有人将修改过的A.java上传到远端仓库中,这样两个A.java就不是一个,所以会产生了冲突),这样还是需要一步一步解决冲突,最佳做法是,再建一个新文件夹将远端仓库代码pull到本地,通过第三方的比差分代码软件来查看差分,然后自己手动合并,之后再push到远端仓库。
注意:理解上需要注意的地方,远端仓库不是用来存储更新的仓库,而是用来版本控制的仓库,如果每一次都把commit同步到远端仓库,在版本维护上会很乱,不利于维护了。
1.3Git命令行简介
git config --global user.name "onizukayao"
git config --global user.email "[email protected]"
#列出所有config配置
git config --list
#获取离线帮助 git help config
git help
二、Git基础
2.1本地仓库
获取Git仓库
- 在现有项目或目录下导入所有文件到Git中;
#仅仅是做了一个初始化的操作,生成一个.git子目录,里面就是git仓库的骨干。
git init
#git文件的跟踪
git add
#提交
git commit
- 从一个服务器克隆一个现有的Git仓库;
git clone [url]
git clone https://github.com/onizukayao/TyporaProject.git
#-o 使远端仓库默认origin改名test
git clone -o test [url]
记录每次更新到仓库
工作目录下的每一个文件都不外乎这两种状态:
- 已跟踪:未修改、已修改、己放入暂存区;
- 未跟踪:其他状态。
#查看当前哪些文件处于什么状态
git status
跟踪新文件、添加暂存区
#开始跟踪一个文件、或者把已跟踪的文件放到暂存区,还能用于合并时冲突文件的标记
#添加内容到下一次提交中(即暂存区中)
git add (file)
eg: git add . //当前目录下的所有文件
暂存已修改文件
#需要留意下 git commit时是把最近一次git add作为提交,如果有任何改变保存后,再用git add一下,然后再提交。
git commit -m "注释"
#直接add + commit一次完成
git commit -a -m "注释"
状态简览
#标准查看,其有许多参数
git status
#紧凑输出 看下面例子
git status -s/--short
M README //被修改、未进缓冲区
M lib/simplegit.rb //被修改、进缓冲区
MM Rakefile //被修改、进缓冲区|同时又修改了
A lib/git.rb //新添加到暂存区文件
?? LICENSE.txt //未跟踪文件
忽略文件
一开始就设置.gitignore文件的习惯,文件.gitignore的格式规范如下:
- 所有空格或者以#开头的行都会被Git忽略;
- 可以使用标准的glob1模式匹配;
- 匹配模式可以以(/)开头防止递归;
- 匹配模式可以以(/)结尾指定目录;
- 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
查看已暂存和未暂存的修改
git diff
#查看已暂存的将要添加到下次提交里的内容
git diff --cached/--staged
#使用图形版本
git difftool
提交更新
提交前,使用git status检查下,再commit。
#通常会启动编辑器以便输入提交的说明。
git commit
#使用提交信息命令行形式
git commit -m
#一次性提交,即包含使用暂存区git add的步骤
git commit -a -m
移除文件
若要移除某个文件,就必须从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后再提交,可以使用git rm命令,这样以后就不会出现在未跟踪文件清单中了。
git rm (file)
#强制删除(已修改过,放入暂存区的内容)
git rm -f (file)
#从git仓库中删除(从暂存区移除)
git rm --cached (file)
移动文件
#改名 相当于把file_from删了,把file_to添加到暂存区
git mv (file_from) (file_to)
查看提交历史
#--oneline 格式好读的样式
git log --oneline
#显示每次提交内容的差异-p -2(最近2次)
git log -p
#reflog可以查看所有分支的所有操作记录(包括已删除和reset等操作)吃后悔药!
git reflog
具体详细的内容详见文档 。
撤销操作
注意:有些操作是不可逆的!!!
#覆盖掉原来的提交信息。
git commit --amend
取消(撤销)文件
看图理解git reset
的三个参数hard、mixed、soft
#可通过git log 查看以前提交的历史 然后有针对性的reset
#--hard
git reset --hard [commit_id]
git reset --hard HEAD^
git reset --hard HEAD~
取消对文件的修改
git checkout --(file)
#这是一个非常危险的命令,你对这个文件的任何修改都会消失。
记住,在 Git 中任何 已提交的 东西几乎总是可以恢复的。 甚至那些被删除的分支中的提交或使用 --amend
选项覆盖的提交也可以恢复(阅读 数据恢复 了解)。 然而,任何你未提交的东西丢失后很可能再也找不到了。
2.2远端仓库的使用
查看远端仓库
#列出指定的每一个远程服务器的简写
git remote
#origin这是Git给你克隆仓库服务器的默认名字。
#简写对应的url,同时查看你对这个仓库拥有什么权限 fetch push等等都会显示
git remote -v
添加远程仓库(到本地的git remote中)
git remote add
#note就代表了远端的仓库,而不是用默认的origin
git remote add note https://github.com/onizukayao/TyporeProject.git
#修改
git remote origin set-url [url]
#删除
git remote rm origin
从远程仓库中抓取与拉取
#拉取所有你还没有的数据,执行后,,你讲拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
git fetch [remote-name]
#克隆,会自动将其添加到远程仓库并默认为“origin”为简写。
#会抓取克隆后新推送的所有工作。
git fetch origin
#git fetch将数据拉取到本地仓库,他并不会合并或修改你当前的工作,需要手动合并到你的工作内。
#自动抓取然后合并远程分支到当前分支,有尝试合并分支的!
git pull
#自动设置本地master分支跟踪克隆的远程仓库master分支
git clone
推送到远程仓库
git push [remote-name] [branch-name]
# 当你想要将 master 分支推送到 origin 服务器时
git push origin master
查看某个远程仓库
git remote show origin
#注意查看内容,来确认push pull对应的仓库。
远程仓库的移除与重命名(本地)
git remote rename old_resp new_resp
git remote rm (resp)
2.3打标签
列出标签
git tag
创建标签
两种主要类型:
- 轻量标签(lightweight):特定提交的引用。
#本质是将提交校验和存储到一个文件——没有保存任何其他信息,只用标签名字
git tag v1.4-lw
- 附注标签(annotated):存储在Git数据库中的一个完整对象。(-a)
#创建一个附注标签
git tag -a v1.4 -m "my version 1.4"
#-m 标签信息 强制得有
#查看标签信息
git show v1.4
后期打标签
#9fceb02补打标签的历史Git
git tag -a v1.2 9fceb02
共享标签
#
git push origin [tagname]
#推送多个标签 --tags
git push --tags
#现在当其他人从仓库中克隆或者拉取,他们也能得到你的那些标签
删除标签
#删除本地仓库上的标签
git tag -d
#删除本地轻量级标签
git tag -d v1.4-lw
#更新远程仓库
git push origin :refs/tags/v1.4-lw
检出标签
这个状态有些不好的副作用!
#查看某个标签所指向的文件版本,会使仓库处于“分离头指针”状态。
git checkout
2.4Git别名
#git commit 只需要输入 git ci
git config --global alias.ci commit
#根据习惯设置一些自己喜欢的别名,前期学习不建议使用别名
三、Git分支
3.1分支简介
Git 的 “master”
分支并不是一个特殊分支。 它就跟其它分支完全没有区别。 之所以几乎每一个仓库都有 master
分支,是因为 git init
命令默认创建它,并且大多数人都懒得去改动它。
分支创建
#其实就是创建一个名为testing的指针,但是不会切换到新分支去。
git branch testing
#新建分支并切换 -b
git checkout -b testing
一个名为HEAD的特殊指针,可以把它想象成为当前分支的又一个别名!
分支切换
#切换分支做了两件事:一是HEAD指针的移动,二是将工作目录恢复成HEAD指向的快照内容。
git checkout testing
#项目的分叉历史
git log --oneline --decorate --graph --all
分支删除
git branch -d
3.2分支的新建与合并
新建分支
#新建并切换到#53问题 (-b)
git checkout -b iss53
#上述等价于
git branch iss53
git checkout iss53
#新建hotfix分支
git checkout -b hotfix
分支合并
#合并分支hotfix,master指针移动到hotfix指向的位置。
git merge hotfix
#合并后需要删除分支
git branch -d hotfix
"快进(fast-forward
)"当前master
分支所指向的提交是你当前提交的直接上游,所以Git只是简单的指针移动。
复杂情况,不是原来的fast-forward,而是有共同的C2工作祖先,演变成为三方分支合并。
它的最终会生成一个新的提交指向C6,合并时切换到主干上,然后merge合并你要的分支。
遇到冲突时的分支合并
通常Git会自动根据提交版本去合并完成,这时大多情况下会出现一个常见的问题:
Auto-merging index.html
CONFLICT (content):Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
使用git status
查看那些原因包含合并冲突而处于未合并(unmerged)状态的文件。
<<<<<<< HEAD:index.html
=======
>>>>>>> iss53:index.html
这表示 HEAD
所指示的版本(也就是你的 master
分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(=======
的上半部分),而 iss53
分支所指示的版本在 =======
的下半部分。 为了解决冲突,你必须选择使用由 =======
分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突。
3.3分支管理
#不加参数得到所有分支的一个列表
git branch
#-v 查看每个分支的最后一次提交
git branch -v
#-vv 查看跟踪的所有分支
git branch -vv
#--merged 已经合并的分支
#如果分支名字前没有*号的分支可以使用 git branch -d 删除掉
git branch --merged
#--no-merged 尚未合并到当前分支的分支
#如果没有合并 git branch -d 删除会失败
git branch --no-merged
3.4分支开发工作流
正是由于分支管理的便捷,才衍生出这些典型的工作方式。
- 长期分支
- 特性分支
3.5远程分支
远程分支
远程引用是对远程仓库的引用(指针),包括分支、标签等等。
#显式地获取远程引用的完整列表
git ls-remote
#获取远程仓库信息
git remote show
# -a 查看本地和远程仓库所有分支
git branch -a
# -r 只查看远程仓库的所有分支
git branch -r
############## 重 要 ##############
#远程分支的获取
#方式一
#用于工作的本地分支,并且起点位于origin/serverfix
git checkout -b serverfix(本地分支名) origin/serverfix(远程分支名)
#将本地分支与远程分支设置为不同名字 git checkout -b [branch] [remotename]/[branch]
git checkout -b sf origin/serverfix
#如果提示错误,需要 git fetch 同步一下仓库
# git checkout -b serverfix //这表示纯粹在本地建一个分支。
#方式二
git fetch origin serverfix(远程):serverfix(本地)
#查看设置的所有跟踪分支
git branch -vv
#分支iss53正在跟踪origin/iss53 ahead” 是 2,意味着本地有两个提交还没有推送到服务器上
iss53 7e424c3 [origin/iss53: ahead 2] forgot the brackets
master 1ae2a45 [origin/master] deploying index fix
#ahead 3, behind 1 分支并且领先 3 落后 1,意味着服务器上有一次提交还没有合并入同时本地有三次提交还没有推送。
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it
testing 5ea463a trying something new
“origin” 并无特殊含义远程仓库名字 “origin” 与分支名字 “master” 一样,在 Git 中并没有任何特别的含义一样。 同时 “master” 是当你运行
git init
时默认的起始分支名字,原因仅仅是它的广泛使用,“origin” 是当你运行git clone
时默认的远程仓库名字。 如果你运行git clone -o booyah
,那么你默认的远程分支名字将会是booyah/master
。
远程跟踪分支是远程分支状态的引用。默认origin/master。
分支推送
#本地分支到远程仓库
git push --set-upstream origin 分支名
跟踪分支( 这是默认的一个跟踪分支origin/master
)
从一个远程跟踪分支检出一个本地分支会自动创建所谓的“跟踪分支”(它跟踪的分支叫做“上游分支”)。 跟踪分支是与远程分支有直接关系的本地分支。 如果在一个跟踪分支上输入 git pull
,Git 能自动地识别去哪个服务器上抓取、合并到哪个分支。
#设置跟踪分支
git checkout --track origin/serverfix
git branch -u origin/serverfix
拉取
#通过设跟踪分支,这样我们在分支中就可以直接抓取及拉取了
#从服务器上抓取本地没有的数据,并不会更新,需要自己合并
git fetch
# git fetch 后自己合并
git merge
#上述两者的合并用法
git pull
git fetch origin
抓取本地没有的数据,并且更新到本地数据库中,移动origin/master
指针的指向。
删除远程分支
#删除远程分支,只是删除了Git服务器上的指针
git push origin --delete serverfix
3.6变基
Git修改不同的分支主要两种方法:merge
和rebase
。说白了merge
的时候,本地会有分叉的,而rebase
后就一条线了。总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。
git rebase master
rebase常见使用方式
- 合并多个
commit
为一个完整commit
;
git rebase -i [commit_id]|[HEAD~n]
#交互参数设置
pick:保留该commit(缩写:p)
reword:保留该commit,但我需要修改该commit的注释(缩写:r)
edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
squash:将该commit和前一个commit合并(缩写:s)
fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
exec:执行shell命令(缩写:x)
drop:我要丢弃该commit(缩写:d)
- 克隆一个远程分支
master
及branch
,或者在本地创建一个新本地开发分支local
; - 切换至本地的开发分支上开发和测试;
- 阶段性开发完成后(包含功能代码和单元测试),可以准备提交代码;
- 首先切换到
master
分支,git pull
拉取最新的分支状态; - 然后切回
local
分支; - 通过
git rebase -i
将本地的多次提交合并为一个,以简化提交历史。本地有多个提交时,如果不进行这一步,在git rebase master
时会多次解决冲突(最坏情况下,每一个提交都会相应解决一个冲突); git rebase master
将master
最新的分支同步到本地,这个过程可能需要手动解决冲突(如果进行了上一步的话,只需解决一次冲突);- 然后切换到
master
分支,git merge
将本地的local
分支内容合并到master
分支;
- 首先切换到
git push
将master
分支的提交上传;- 本地开发分支可以灵活管理;
#操作流程
git clone master
git checkout -b local origin/local
#变基 合并最近两个
git rebase -i HEAD~2
git rebase master---->解决冲突(git add git commit)--->git rebase --continue
git checkout master
git pull
git merge local
git push
- 将某一段
commit
粘贴到另一个分支上;
git rebase --onto
#操作流程
git rebase [C->D] --onto master
#当前HEAD指针处于游离状态,需要移动HEAD指针
git checkout master
git reset --hard E
#更复杂一点的--onto
#如果要在合并两个分支时需要跳过某一个分支的提交
A---B---E---F---G master
\
C---D---H---I---J next
\
K---L---M topic
git rebase --onto master next topic
A---B---E---F---G master
\ \
\ K'---L'---M' topic
\
C---D---H---I---J next
图片来源:Git文档
所谓glob模式是指shell所使用的简化了的正则表达式。↩