配置用户名邮箱
$ git config --global user.name "Your Name"
$ git config --global user.email "[email protected]"
本地创建仓库
$ mkdir demo_repo && cd demo_repo
$ git init
添加文件
$ git add readme.txt
$ git add dummy1.txt
$ git add dummy2.txt
提交到仓库
$ git commit -m "commit 1 readme file, 2 dummy file"
工作区,暂存区(stage), 分支commit(快照)历史
$ git add #工作区文件添加到暂存区
$ git commit #暂存区文件提交commit
查看仓库状态,当前分支,文件修改等
$ git status
查看具体修改内容
$ git diff readme.txt
$ git diff HEAD -- readme.txt #比较工作区和最新commit
从近到远显示commit历史, commit id, author,message等等
$ git log
$ git log --pretty=oneline #清爽信息
回退
$ git reset --hard HEAD^ #回退到前1个commit
$ git reset --hard HEAD^^ #回退到前2个commit
$ git reset --hard HEAD~100 #回退到前100个commit
$ git reset --hard 1094a #切换到指定commit id
误删commit, 如何恢复
查看命令记录,找回误删的commit id
$ git reflog
$ git reset --hard xxxx
丢弃工作区文件的修改
$ git checkout -- readme.txt #回退到暂存区的状态
$ git checkout -- readme.txt #如果暂存区干净回退到最新commit的状态
丢弃暂存区的修改
$ git reset HEAD <file> #回退到最新commit的状态
删除文件
$ rm test.txt
$ git rm test.txt #删除版本库中版本
$ git commit -m "remove test.txt" #提交删除
误删回退
$ git checkout -- test.txt
HEAD指向当前分支,分支指向commit
HEAD
\
master
\
O--O--O
创建,切换分支
$ git branch dev #创建分支
$ git checkout dev #切换分支 $git switch dev
$ git checkout -b dev #创建并切换分支 $git switch -c dev
master
\
O--O--O
|
dev
|
HEAD
查看当前分支
$ git branch
切换分支发生的事
$ git add readme.txt #在dev分支上 add
$ git commit -m "on branch dev" #在dev分支上 commit
master
|
O--O--O~~O
|
dev
|
HEAD
$ git checkout master #切换回master
HEAD
\
master
\
O--O--O~~O
|
dev
Fast-forward合并分支
$ git merge dev #在master上合并dev
#实际是Fast-forward类型的合并
HEAD
\
master
\
O--O--O--~~O
|
dev
强制禁用 fast forward,会创建新的commit
$ git merge --no-ff -m "merge with no-ff" dev
HEAD
\
master
\
O--O--O------O
\ /
O
|
dev
删除分支
$ git branch -d dev
HEAD
\
master
\
O--O--O--~~O
当前分支状态
HEAD
\
master
|
O
/
O--O--O--O
\
O
|
feature1
在当前分支(master)
$ git merge feature1
Auto-merging...
CONFLICT (content)
...
#发生了冲突
查看冲突,手动修改
$ git status
手动修改冲突文件
$ vi ...
然后在当前分支
$ git add foo.bar
$ git commit -m "fix conflict"
HEAD
|
master
/
O--O--O--O --O -- O
\ /
O---/
|
feature1
查看合并情况
$ git log --graph --pretty=oneline --abbrev-commit
删除分支
$ git branch -d feature1
廖雪峰给的一个管理策略,未必就是最佳实践
master:应当很稳定,仅用来发布新版本,不用来开发
dev:分支用来开发,发布新版本时候,在master分支合并dev
平时开发:从dev上创建新分支合并。。。
在dev分支上,接到任务修bug
$ git status #查看当前分支,比如dev
$ git stash #暂存当前工作区
加入从master分支着手修复bug
$ git switch master
$ git checkout -b issue-101 #bug分支
在issue-101分支上
$ git add readme.txt
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
修复完成后,回到master合并修复内容
$ git switch master
$ git merge --no-ff -m "merged bug fix 101" issue-101
回到dev继续干活
$ git switch dev
#查看工作现场
$ git stash list
$ git stash apply #恢复工作区
$ git stash drop #删除保存的状态
$ git stash pop #先恢复,然后删除保存的状态
cherry-pick “复制”一个commit到当前分支
dev是从master过来的,可能这个bug在dev上也有
cherry-pick只复制commit,不用merge
$ git branch
* dev
master
$ git cherry-pick 4c805e2 #修bug的commit [issue-101 4c805e2] fix bug 101
[master 1d4b803] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
从dev创建新分支feature,开发完成后在合并到dev
中间feature取消的话可以强制删除
$ git branch -D <name>
以某种方式创建远程仓库 e.g. github
在本地
$ cd demo_repo
$ git remote add origin [email protected]:username/demo_repo.git
#添加了远程库 名字: origin, 地址 git@,,,.git
推送本地内容至远程库
$ git push -u origin master
-u参数,
不仅把本地的master分支内容推送的远程新的master分支,
还会把本地的master分支和远程的master分支关联起来,
在以后的推送或者拉取时就可以简化命令
$ git push origin master #关联后的简化命令
克隆远程仓库
$ git clone [email protected]:username/demo_repo.git
git clone 远程仓库时,自动把本地master和远程master对应,远程仓库默认为origin
查看远程仓库
$ git remote
$ git remote -v #verbose
推送分支:把本地分支上的commit推送到远程库
$ git push origin master #指定本地分支master, 推送到远程库的对应分支
$ git push origin dev #推送本地的dev分支
用户b在a之前推送更新到远程仓库
# user_b执行clone远程仓库
user_b@ubuntu$ git clone [email protected]:username/demo_repo.git
# 默认只有master分支
$ git branch
# clone远程仓库后,在本地使用远程分支
user_b@ubuntu$ git checkout -b dev origin/dev #在本地创建远程分支
...git add...git commit...
user_b@ubuntu$ git push origin dev #推送到远程分支
user_a本地也作了修改,也想push到origin/dev
# 当前在dev分支
user_a@ubuntu$ git push origin dev
# 推送失败,两个推送有冲突,因为当前分支落后了
1)user_a 拉取最新内容
#git pull <=> git fetch; git merge
user_a@ubuntu$ git pull
# 拉取失败,当前的dev分支和origin/dev分支没有指明链接
user_a@ubuntu$ git branch --set-upstream-to=origin/dev dev #指明链接
user_a@ubuntu$ git pull #可以拉取
#显示有冲突
2)手动解决冲突的文件
3)然后添加commit,推送
user_a@ubuntu$ git add foo.bar
user_a@ubuntu$ git commit -m "fix conflict"
# 然后推送到dev分支
user_a@ubuntu$ git push origin dev
完成。
rebase & squash
on master:
1--2--3
on master:
git checkout -c feature
on master:
master
|
1--2--3--4--5
on feature:
feature
|
1--2--3~~10~~11~~12
merge
$ git checkout feature
$ git merge master
master
|
1--2--3--4--5----------|
\~~10~~11~~12~~20
|
feature
rebase
$ git checkout feature
$ git rebase master
master
|
1--2--3--4--5~~10~~11~~12
|
feature
$ git rebase -i master # interactive mode
squash 10xxxxx Commit message#1
squash 11e8a9b Commit message#2
# pick
# squash
# fix up
# 清理历史,压缩commit
可能的结果
master
|
1--2--3--4--5~~12
|
feature
The biggest problem people face is they force push but haven’t set git push default. This results in updates to all branches having the same name, both locally and remotely, and that is dreadful to deal with.
如何选择呢? 取决于团队
把开发分支向dev目标分支创建pull request
可以在开发dev的时候,持续开发,持续得到dev和目标分支的比较,可以用做代码review
标签是某分支上某commit的别名
$ git tag v0.1 #为当前分支的最新commit打tag
$ git tag -a v0.1 -m "version 0.1 released" 1094adb
#为指定的commit打tag,并加注释
查看tag
$ git tag
$ git show v0.9
推送标签到远程仓库
$ git push origin v1.0 #推送单个
$ git push origin --tags #推送全部
删除标签
$ git tag -d v0.1 # 1)先从本地删除
$ git push origin :refs/tags/v0.9 # 2)然后删除远程
引用链接
patch中存储的是对代码的修改
生成patch就是记录你对代码的修改并将其保存在patch文件中
打patch就是将patch文件中对代码的修改,应用到源代码,从而把对代码的修改应用到code中
diff和patch两个命令可以生成patch和打patch,但是对多个文件不方便;没办法保存commit信息
下面的方法可以以project为单位;可以保存commit信息;灵活,可以获取任意commit之间的patch集
$ git format-patch
$ git am
$ git format-patch HEAD^
#生成最近的1次commit的patch
$ git format-patch HEAD^^
#生成最近的2次commit的patch
$ git format-patch <r1>..<r2>
#生成两个commit间的修改的patch(包含两个commit. 和都是具体的commit号)
$ git format-patch -1 <r1>
#生成单个commit的patch
$ git format-patch <r1>
#生成某commit以来的修改patch(不包含该commit)
$ git format-patch --root <r1>
#生成从根到r1提交的所有patch
git am
$ git apply --stat 0001-limit-log-function.patch
# 查看patch的情况
$ git apply --check 0001-limit-log-function.patch
# 检查patch是否能够打上,如果没有任何输出,则说明无冲突,可以打上
(注:git apply是另外一种打patch的命令,
其与git am的区别是,
git apply并不会将commit message等打上去,
打完patch后需要重新git add和git commit,
而git am会直接将patch的所有信息打上去,
而且不用重新git add和git commit,
author也是patch的author而不是打patch的人)
$ git am 0001-limit-log-function.patch
# 将名字为0001-limit-log-function.patch的patch打上
$ git am --signoff 0001-limit-log-function.patch
# 添加-s或者--signoff,还可以把自己的名字添加为signed off by信息,作用是注明打patch的人是谁,因为有时打patch的人并不是patch的作者
$ git am ~/patch-set/*.patch
# 将路径~/patch-set/*.patch 按照先后顺序打上
$ git am --abort
# 当git am失败时,用以将已经在am过程中打上的patch废弃掉
#(比如有三个patch,打到第三个patch时有冲突,那么这条命令会把打上的前两个patch丢弃掉,返回没有打patch的状态)
#$ git am --resolved
#当git am失败,解决完冲突后,这条命令会接着打patch
patch冲突的解决
(1) 根据git am失败的信息,找到发生冲突的具体patch文件,
然后用命令git apply --reject <patch_name>,强行打这个patch,
发生冲突的部分会保存为.rej文件
(例如发生冲突的文件是a.txt,那么运行完这个命令后,发生conflict的部分会保存为a.txt.rej),
未发生冲突的部分会成功打上patch
(2) 根据.rej文件,通过编辑该patch文件的方式解决冲突。
(3) 废弃上一条am命令已经打了的patch:git am --abort
(4) 重新打patch:git am ~/patch-set/*.patchpatch
本文大部分内容是对这篇教程二次整理所得