文章目录
git学习路线
- 常见概念
- 常用指令
- 常见的场景下的操作
常见概念
分区
git是一种分布式的代码管理工具,每一个git终端用户就拥有一个本地代码版本仓库。
他的代码分区也是相对比较复杂:
- 工作区(workspace):用户工作区,直接修改的代码就是工作区代码。
- 缓存区(index/stage):用户缓存区,工作区代码add以后,进入缓存区。
- 本地仓库(local repository):本地版本的仓库,缓存区代码commit以后,进入本地仓库。
- 远程仓库(remote repository):在服务器上的代码仓库,本地仓库代码push以后,进入远程仓库。
以上各个分区对初学者有点懵,其实只要记住几个基本操作就能对应不同分区:
- 工作区:
- 缓存区:
add
- 本地仓库:
commit
- 远程仓库:
push
概念
远端(remote):远程仓库,服务器端仓库,push以后到达。
commitId
:顾名思义,就是做完git commit
后,得到的一个SHA-1 Hash
值。
当前分支(current branch):就是工作区当前切换到的一个分支,通过命令git branch
可以查看。
HEAD
:可以理解为一个游标,指向你在操作的某个commitId
版本。新的commitId
会加在HEAD
后面,然后HEAD
指向新的commitId
。
FETCH_HEAD
:记录远端仓库各个分支的HEAD
常用指令
建议申请一个远程仓库,然后手动试一下。抛去了不常用的命令,以及不常用的参数。
git config
顾名思义,配置相关的一个命令。
常见用法:
git config --global credential.helper store
:保存密码git config --global user.name <name>
:用户git config --global user.email <email>
:邮箱git config --global core.autocrlf true
:行尾设置,windows用truegit config --global alias.<alias-name> <git-command>
:设置命令缩写
git clone
将远程仓库复制到本地。
常见用法:git clone <repository> [<directory>]
git clone https://github.com/xxxxx/xxxx
git init
将指定文件夹设置为一个git仓库。
常见用法:git init <directory>
mkdir gitteset
cd gittest
git init
git add
将文件放入缓存区,可以指定文件,也可以全部。
常见用法:git add <filename>
、git add .
git commit
将缓存区的文件,提交到本地仓库。
常见用法:git commit -m <msg>
、git commit -F <file> -m <msg>
# 假设在之前的gittest下
git status -s
touch a.java
git status -s
git add a.java
git status -s
git commit -m "add a.java"
git status -s
git push
将本地仓库的代码,提交到远程仓库。
常见用法:
git push
:推送当前工作分支到他的远端仓库和远端分支。git push <remote> <branch>
:推送<branch>
分支,而不是当前分支。不存在则新建。git push <remote> <branch_local>:<branch_remote>
:跨分支推送。不存在则新建。git push <remote> HEAD
:使用HEAD推送当前分支。不存在则新建。git push <remote> HEAD:<branch_remote>
:使用HEAD
跨分支推送。不存在则新建。
注意push前最好拉取远端代码,否则他人提交的代码会导致你"非快进更新(non-fast forward)"。
git push
git push origin testpush
git push origin testpush:testpush2
git push origin HEAD
git push origin HEAD:testpush3
git checkout
可以签出分支,也可以签出某个分支某个版本的某个文件。
常见用法:
git checkout <branch>
:签出某个分支,HEAD
移到分支的commitId
上。git checkout <filename>
:签出某个文件,从缓存区。(一般场景是你修改/删除了这个文件,但是没有add)git checkout <branch> <filename>
:签出其他分支的某个文件。git checkout -m <branch>
:切换分支,并合并。(一般场景是你当前分支的修改内容会被目标分支覆盖)git checkout -b <new_branch>
:新建分支,如果是-B
,那就是强行开新分支(同名分支会被覆盖)。
试一试:
# 建分支
git checkout -b testb #创建分支
git checkout -b testb #提示已存在
git checkout -B testb #重值分支
# 有修改内容会丢失,不能切换分支
echo 'this is testb' >> b.txt && git add . && git commit -m "b.txt" #加个b文件
git checkout master #签出分支master
echo 'this is testb' >> b.txt #master新建个b
git hash-object b.txt
git checkout testb #切换分支失败,因为b的信息会丢失
# 虽然有修改内容,但是可以在切换的分支重保留
git checkout testb b.txt #签出b文件,其实两个文件内容相同
git hash-object b.txt #通过hash值的比较,可以看到两个文件虽然内容相同,但是hash不同
git checkout testb #签出成功,因为这个b.txt文件是testb分支的文件,所以修改的内容直接保留了。
结论:checkout失败,就是尝试将修改内容带到目标分支失败,原因有:
- 已跟踪的文件,目标分支没有相同hash的同名文件。
- 未跟踪的文件,目标分支已有不同hash的同名文件。
git fetch
从其他仓库下载东西。
常见用法:
git fetch [<remote> ]
:所有的fetch指令都会更新FETCH_HEAD
,只不过根据参数可能是:所有远端、一个远端、一个分支git fetch <remote> <remote_branch>:<local_branch>
:只有指明了本地分支,才会拉取分支代码。
# 所有fetch指令都会更新 FETCH_HEAD
rm .git/FETCH_HEAD && git fetch && cat .git/FETCH_HEAD &&\
rm .git/FETCH_HEAD && git fetch origin && cat .git/FETCH_HEAD &&\
rm .git/FETCH_HEAD && git fetch origin master && cat .git/FETCH_HEAD
# 具体到本地分支,会拉取分支代码;
# 注意不要fetch到当前分支,会提示:fatal: 拒绝获取到非纯仓库的当前分支
rm .git/FETCH_HEAD && git fetch origin testb:testb && cat .git/FETCH_HEAD
git branch -av
git merge
将多个commit版本的内容,合并到当前分支。
注意:两个版本merge时,强烈要求所有内容已经commit。
常见用法:
git merge <branch_1> <branch_2> .... <branch_n>
:将多个分支,合并到当前分支。git merge -s <branches> <strategy>
:使用合并策略,简单来说:recursive
:默认值,递归三路合并算法。首先找到同一个commitId
祖先。然后分析冲突,a随祖先,b不随祖先,那就保留b。ours
:如果有冲突,自动应用我方代码。
# 构造一个测试环境
# 新建 testmerge1 testmerge2 分支,其中包含一个log文件
git checkout master && echo "this is point a" > log && git add . && git commit -m "test merge step 1" &&\
git checkout -B testmerge1 && git checkout -B testmerge2
# 两个分支都修改这个文件,然后merge
git checkout testmerge1 && echo "branch 1 change" >> log && git add . && git commit -m "test merge step 2"
git checkout testmerge2 && echo "branch 2 change" >> log && git add . && git commit -m "test merge step 3"
git merge testmerge1 # 合并失败,显然系统无法判断该留哪个
git reset --hard HEAD # 回退到合并前
git merge -s ours testmerge1 # 合并成功,产生一个新commit
合并失败的原因,是按照递归三路合并算法,找到他们的祖先 step 1,然后发现两个分支都不随祖先,系统无法取舍。
git pull
拉取远端<remote>
分支<branch>
代码,和当前本地分支进行合并。
其命令可以理解为 git fetch
+ git merge
。
常见用法:
git pull <remote> <branch>
:默认都是拉取到当前分支(并进行merge)。
# 构造一个环境,通过update文件来查看commit版本
git checkout master && rm * && echo "1111">>update && echo "2222">>delete && git add . && git commit -m "1" && git push
# 增删改,提交,上传,服务器update=11112222
rm delete && echo 2222 >> update && echo "add">>insert && git add . && git commit -m "1"&& git push
# 本地退回一个版本,master和HEAD都指向上一个版本。update=1111
git reset --hard HEAD~ && cat update
# 拉取代码,因为远端版本更新,直接更新了本地。update=11112222
git pull origin master:master && cat update
###### 下面测试本地版本更高的时候
# 再提交一个版本,但不发到远端
echo 33333 >> update && git add . && git commit -m "1"
# 拉取代码,由于本地版本更高,会提示"Already up-to-date"
git pull
# 指定本地分支,提示"(非快进式)"
git pull origin master:master
###### 下面测试本地版本和远端分岔了的情况,这在多人合作开发中常见
# 新开个仓库,模拟一下其他用户的操作,远端update 变成111122224444
git clone xxxxxxxxxxxx folder2 && cd folder2
echo 4444 >> update && git add . && git commit -m "1" && git push
# 回到自己的仓库,拉取代码,提示:
# 自动合并 update
# 冲突(内容):合并冲突于 update
# 自动合并失败,修正冲突然后提交修正的结果。
git pull
# 禁招,轻易别用;强行(--force -f)拉取/推送,覆盖自己/他人的commit。
git pull -f origin master:master
git push -f origin master
git reset
重置HEAD
到某一个commitId
。
常见用法:
git reset <commitId> [<filename>]
:移动HEAD
到commitId
。
选项:
--soft
:不处理工作区、缓存区;--mixed
:默认值;不处理工作区,重置缓存区;--hard
:处理工作区、重置缓存区;所有跟踪文件被处理。未跟踪(新增)代码会被保留。--merge
:和--hard
类似,但是未被git add
的修改内容,会保留下来。--keep
:和--hard
类似,但是如果有差异文件(两个版本中不同)被修改了,则命令失败。
其中--hard
选项经常用来放弃当前分支中的所有修改。
# 签出master分支,在该分支上,commitId为: 5ea663c (head) 4ee69b7
# 两个分支都有一个update文件,内容分别是 1111 2222
git checkout master
# 重置
git reset --hard 5ea663c && touch a && git add a && echo abc>>update && git status
# soft测试,可以看到工作区update文件和暂存区a文件没有任何变化
git reset --soft 4ee69b7 && git status
# 重置
git reset --hard 5ea663c && touch a && git add a && echo abc>>update && git status
# mixed测试,可以看到工作区update文件没变化;暂存区被清了
git reset --mixed 4ee69b7 && git status
# 重置
git reset --hard 5ea663c && touch a && git add a && echo abc>>update && git status
# hard测试,可以看到工作区update被更新了、a文件被删除了,暂存区清了。
git add a && git reset --hard 4ee69b7 && git status
# 重置
git reset --hard 5ea663c && touch a && git add a && echo abc>>update && git status
# merge测试,由于update未进暂存,被拒绝了。git add后,效果和hard一样。
git reset --merge 4ee69b7 && git status
# 重置
git reset --hard 5ea663c && touch a && git add a && echo abc>>update && git status
# 测试keep,由于update是差异文件,并且被修改了,命令失败。
git reset --keep 4ee69b7 && git status
git stash
常见用法:
git stash
:将当前未commit
的信息放入stash栈git stash pop
:将栈顶的存储信息弹出。git stash apply
:使用栈顶的存储信息,但是不弹出。git stash list
:显示栈中列表git stash save $name
:以名字形式进栈git stash drop $name
:按名字索引,删除某个存储信息。git stash clear
:清空存储信息。