文章目录
一、git 是什么
分布式版本控制系统
官网:https://git-scm.com/book/zh/v2
二、git 简单使用
2.1 创建版本库(可被git追踪的库)
# 1、新建文件夹
mkdir learngit
# 2、将该文件夹变成可被git管理的仓库
git init
执行后,learngit文件夹就变成了一个可以被管理的仓库了,且这个目录下会出现一个.git/
目录,是git用来跟踪和管理版本库的。如果没看到,则是被隐藏了,可以使用ls -ah
来看。
2.2 git 把文件添加到版本库
# 把文件夹下的所有文件都添加
git add .
# 添加特定的文件
git add xxx.py
2.3 把版本库中的文件提交到仓库
# -m 后面是本次提交的说明,最好说清本次提交修改或添加了什么
git commit -m "add an python file"
# 修改上一次的commit,这会算一次commit
git commit -amend --no-edit
输入该命令后,成功后会返回
1 file changed
:一个文件被改动2 insertions
:插入了两行内容1 delations
:删除了一行内容
2.4 修改文件并查看修改,提交
# 查看仓库当前状态
git status
# 查看被修改文件的具体修改内容
git diff xxx.py
# 修改后先 add
git add .
# 查看仓库状态
git status
# 然后 commit
git commit -m "add distributed"
# 再次查看状态 (nothing to commit, working tree clean)
git status
2.5 查看多个版本每次分别修改了什么内容
git log # 查看每次修改,从近到远显示,显示每次commit id,和commit的注释
git log --pretty=oneline # 简单显示
commit id
是SHA1计算出来的一个非常大的数字,用十六进制表示,因为git是分布式的版本控制系统,如果简单的使用1,2,3作为版本号,会多人冲突。
2.6 submodule
git submodule update --recursive --init # 会拉取依赖的git子模块(通常命名为third_party库)
2.7 git-lfs
三、版本修改和管理
3.1 回退版本
如果想回退版本,git必须知道回退到哪个版本,git的版本都是按提交顺序管理的,HEAD
表示当前版本,也就是最新提交的,上一个版本是 HEAD^
,上上个版本是 HEAD^^
或 HEAD~2
,以此类推。
# 回退到上个版本,会返回 HEAD is now at ...
git reset --hard HEAD^
回退完之后,所有的文件会恢复到上一次提交的内容。
回退到特定版本,这个时候要先用 git log
看 commit id
,用 commit id
来回退
# 回退到某个特定的版本,版本号只写前几位就可以了,git会自动找到
git reset --hard 1094a
如果回退到了旧版本,但是又想恢复到新版本怎么办?
-
第一种方法是回退之前,先打一下
git log
,把所有的commit id
都打出来,然后恢复的时候使用新版本的commit id
来恢复就可以了。 -
第二种方法是使用
git reflog
,打出来你的每一次命令,从这找commit id
就可以
3.2 工作区和暂存区
工作区(working directory): 就是电脑里能看到的目录,比如 learngit
文件夹就是一个工作区
版本库(Repository): 工作区有一个目录 .git/
,这个目录不算工作区,而是 git 的版本库,版本库存了下面的东西:
stage(index)
——暂存区git
自动创建的分支master
- 指向
master
的指针HEAD
git add
:把工作区的文件添加到版本库的暂存区git commit
:把暂存区的东西提交到当前分支,可以add
多次,然后执行commit
一次,把暂存区的所有的内容都提交上去,暂存区就啥都没啦
提交之后,如果没有修改任何东西,git status
后看到的就是:nothing to commit, working tree clean
如果做了两次修改,确只add了一次,然后 commit 了,怎么办:继续 add
,然后再 commit
# 第一次修改,然后 add
git add xxx.py
# 第二次修改,没有 add,直接commit了
git commit -m "modified: xxx.py"
然后会发现第二次修改的没有被提交,因为每次提交都只会提交暂存区中的东西,第二次修改又没被放入暂存区,所以只提交了第一次的修改。
提交后,可以用 git diff HEAD --xxx.py
来查看工作区和版本库中的区别
正常流程:
第一次修改→ git add
→第二次修改→ git add
→ git commit
也就是说,如果每次修改,不用 git add
放到暂存区,则就不会加入到 commit
中。
3.3 撤销修改
改乱了工作区的文件,想直接丢弃时:
- 如果还没放到缓存区:
git checkout -- xxx.py
,就可以把文件在工作区的修改全部撤销 - 如果已经放到了缓存区:
- ①
git reset HEAD xxx.py
,git reset
既可以回退版本,也可以把暂存区的修改回退到工作区,HEAD
表示最新版本。 - ②
git checkout -- xxx.py
- ①
- 如果已经提交:
git reset --hard xxx.py
git reset HEAD
:暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。
git rm --cached <file>
:会直接从暂存区删除文件,工作区则不做出改变。也就是仅从跟踪清单中删除,工作区不受影响。
git checkout .
或者 git checkout -- <file>
:会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。
git checkout HEAD .
或者 git checkout HEAD <file>
:会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
3.4 删除文件
在工作区中删除了文件:
rm xxx.py
如果真的要删,则要把版本库中的也删掉:
# 1、从版本库中删除文件
git rm xxx.py
# 2、提交删除
git commit -m "remove xxx.py"
如果工作区误删了,则可以从版本库中恢复到工作区,git checkout
其实是用版本库中的版本替换工作区的版本,无论工作区里边是修改还是删除了,都可以还原到版本库中的版本:
git checkout -- xxx.py
3.5 revert
git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] <commit>…
git revert (--continue | --skip | --abort | --quit)
git revert 会增加一次新的 commit(其内容为之前 commit 的反向撤销)。
四、远程仓库
4.1 生成 ssh key 并添加到远程仓库
ssh-keygen -t rsa -C "xxx@example.com"
然后一路回车,就可以在 ~/.ssh/
中生成 id_rsa
和 id_rsa.pub
,前者是私匙,不能泄露,后者是公匙,可以公开。
复制 id_rsa.pub
中的内容,添加到 github、gitlab、服务器等设备里边,就可以推送远端仓库了。因为远端仓库要识别出提交确实是你的电脑。
一个设备也可以多加几个 key,也就是多个电脑的key都可以添加到 github。
4.2 本地仓库推送远程
在本地 learngit
仓库下运行下面的命令,把远程仓库和本地仓库关联:
git remote add origin git@github.com:wang/learngit.git
添加后,远程仓库的名字就是 origin。
下一步,就可以推送到远程仓库了
# 第一次推送
git push -u origin master
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
# 后面的推送
git push origin master
删除远程库:
如果添加的时候地址写错了,或者要删除本地库和远程库的连接:
# 查看远程库信息
git remote -v
# 删除远程库连接
git remote rm origin
4.3 从远程克隆仓库
git clone git@github.com:wang/learngit.git
Git支持多种协议,包括https,但ssh协议速度最快。
五、分支管理
5.1 创建与合并分支
创建分支:
# 1 -b: 表示创建分支并切换
git checkout -b dev
# 2 先创建再切换
git branch dev
git checkout dev
查看当前分支,当前分支前面有 *:
git branch
* dev
master
创建 readme.txt
并提交:
git add readme.txt
git commit -m "branch test"
从 dev
切回 master
分支:
git checkout master
把dev
上的修改合并到master上:
git merge dev
删除 dev
分支:
git branch -d dev
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。
使用 switch
来切换分支:
# 创建并切换到新分支
git switch -c dev
# 直接切换到已有的分支
git switch master
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
或者git switch <name>
创建+切换分支:git checkout -b <name>
或者git switch -c <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
5.2 解决冲突
如果在 dev 分支上修改了 readme.txt
,master 分支上也修改了 readme.txt
,合并会出现以下问题:
# dev 分支
git switch dev
git add readme.txt
git commit -m "change the last row of readme.txt to AND"
# master 分支
git switch master
git add readme.txt
git commit -m "change the last row of readme.txt to &"
# 合并
git merge dev
问题:
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result
也就是 readme.txt 文件存在冲突了,必须手动解决后再提交,
Git在有冲突的文件里边会用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,可以修改一个分支的内容后,保存,再提交。
git add readme.txt
git commit -m "conflict fixed"
查看分支合并情况:
git log --graph --pretty=oneline --abbrev-commit
5.3 rebase
多人协作时,很容易出现冲突,后 push 的人要先 pull 一下,在本地合并后,才能 push 成功。
$ git log --graph --pretty=oneline --abbrev-commit
* 582d922 (HEAD -> master) add author
* 8875536 add comment
* d1be385 (origin/master) init hello
* e5e69f1 Merge branch 'dev'
|\
| * 57c53ab (origin/dev, dev) fix env conflict
| |\
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
...
(HEAD -> master)
:表示当前分支的HEAD
,582d922
(origin/master)
:远程origin
的位置,d1be385
可以看出本地分支比远程分支快两个提交(本地在远程前面两行),也可以用 git status
来看:
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use 'git push' to publish your local commits)
也就是提交历史分叉了,可以使用 git rebase
,rebase
操作可以把本地未push的分叉提交历史整理成直线。
Your branch and ‘origin/indoor_sundries_detection’ have diverged,
and have 1 and 1 different commits each, respectively.
git rebase
git status
pull 报错 Found a swap file by the name “.git/.MERGE_MSG.swp”
cd .git/
rm -rf MERGE_MSG
获取commit id
# 最近一次
git rev-parse HEAD
# 最近两次
git rev-parse HEAD~2
六、常用命令
创建仓库:
1、git init <directory>
:带参数则在指定目录下创建,不带参数则在当前目录创建
2、git clone <repo>
:克隆指定的 repo 到本地当前目录
3、git add
:添加文件到暂存区
4、git commit -m “<message>”
:提交暂存区的文件到本地仓库
5、git commit --amend --no-edit
:将当前staged修改合并到最近一次的commit
5、git status
:显示当前文件信息(staged、unstaged、untracked)
查看不同:
1、git diff
:比较工作区和暂存区的修改
2、git diff HEAD
:比较工作区和上一次 commit 后的修改
3、git diff --cached
:比较暂存区和上一次 commit 后的修改
查看 log:
1、git log
:查看历史提交记录
2、git log --oneline
:简洁查看
3、git log --graph
:展现什么时候出现分支、合并
4、git reverse --oneline
:逆向显示所有日志
5、git log --author=wang --oneline -5
:展示wang修改的5行
6、git reflog
:查看本地仓库的所有日志
移除修改:
1、git reset
:移除暂存区所有修改
2、git reset --hard
:移除暂存区所有修改,并强制删除所有工作区的修改
3、git reset <commit>
:将当前分支回滚到指定 ,清除暂存区修改,保存工作区不变
4、git rest --hard <commit>
:将当前分支回滚到指定,清除暂存区所有修改,并强制删除工作区所有修改
七、问题记录
1、change xxx closed
上一次的push已经被merge,出现无法push的情况,解决方法:
# 使用 git reflog 查看commit的记录
git reflog
把push成功的那次 commit id 复制下来,然后soft回退:
git reset --soft xxx
之后重新commit和push即可
2、HEAD -> refs/for/indoor_sundries_nes (n/a (unpacker error))
使用下面的方法 push:
git push --no-thin origin HEAD:refs/for/indoor_sundries_nes
3、Your branch and 'origin/master' have diverged, and have 1 and 1 different commits each, respectively.
使用 git status
查看状态,显示上述问题,则可以使用下面命令解决
git rebase origin/master
git pull --rebase
4、You are currently editing a commit while rebasing branch 'pre-master' on 'f236bf72'.
Last commands done (9 commands done):
pick da34a9a9 fix load model
pick 8f7d7bc5 add model and region
(see more in file .git/rebase-merge/done)
No commands remaining.
You are currently editing a commit while rebasing branch 'pre-master' on 'f236bf72'.
(use "git commit --amend" to amend the current commit)
(use "git rebase --continue" once you are satisfied with your changes)
使用 git rebase --quit
解决
On branch detect_sundries
Your branch is up to date with 'origin/detect_sundries'.
nothing to commit, working tree clean