一文搞定Git
Git
通俗来讲Git就是一个代码版本控制系统,多个开发人员可以协同开发,开发时可以将代码提交到本地,开发完成后,push到远端即可,不用到处备份copy代码,方便查看版本间的diff。
安装git
快速上手
首先我们需要在终端界面初始化git文件夹,这里我们创建了demo文件夹并初始化
$ mkdir demo && cd demo $ git init 已初始化空的 Git 仓库于 /private/tmp/demo/.git/
在初始化后,本地目录的文件就是在工作区中,在执行add后就添加到了暂存区,commit之后就正式存在本地仓库了
创建一个README.md文件,并添加Git中
$ touch README.md $ echo '# git demo' > README.md $ cat README.md # git demo
添加到stage中
$ git add README.md
生成一个提交到本地仓库, -m后跟的内容为本次提交的描述信息,提交的文件为已经add到stage中的文件
$ git commit -m "add README.md" [master(根提交) 8c28b6f] add README.md 1 file changed, 1 insertion(+) create mode 100644 README.md
随时查看本地git状态
$ git status 位于分支 master 无文件要提交,干净的工作区
如图就表示工作区中的文件均已提交到本地git仓库中
添加远端仓库(可以到Github中创建远端仓库,获取地址),origin为远端仓库名,可以自行指定
git remote add origin git@github.com:icankeep/demo.git
可以通过下面命令查看是否添加远端仓库成功
$ git remote show origin * 远程 origin 获取地址:git@github.com:icankeep/demo.git 推送地址:git@github.com:icankeep/demo.git HEAD 分支:(未知)
push本地提交到远端仓库
$ git push origin master 总共 0(差异 0),复用 0(差异 0),包复用 0 remote: remote: Create a pull request for 'master' on GitHub by visiting: remote: https://github.com/icankeep/demo/pull/new/master remote: To github.com:icankeep/demo.git * [new branch] master -> master
以上我们就完成了最简单的git提交
进阶
查看分支
查看本地分支
$ git branch * dev master merge_demo rebase_demo
查看所有分支
$ git branch -a dev * master merge_demo rebase_demo remotes/origin/dev remotes/origin/master
创建并切换分支
一般来说,一个项目组的主分支为master,当我们开发时,会创建一个dev分支,在本地开发测试完成后会合并到线上的分支,如staging,提交到线上staging测试完成后,再合并到主分支发布。我们可以使用下面的命令进行创建并切换分支
$ git checkout -b dev
切换到一个新分支 'dev'
git checkout
一般有以下几种用途: 1. 切换创建新分支
检出commit,该操作会检出该commit,并detach(和
git reset
不一样,reset是在当前分支回退,checkout不影响当前分支,只是检出)。如下,switch是新版本git引入切换分支的命令$ git checkout 2ce5a34 注意:正在切换到 '2ce5a34'。 您正处于分离头指针状态。您可以查看、做试验性的修改及提交,并且您可以在切换 回一个分支时,丢弃在此状态下所做的提交而不对分支造成影响。 如果您想要通过创建分支来保留在此状态下所做的提交,您可以通过在 switch 命令 中添加参数 -c 来实现(现在或稍后)。例如: git switch -c <新分支名> 或者撤销此操作: git switch - 通过将配置变量 advice.detachedHead 设置为 false 来关闭此建议 HEAD 目前位于 2ce5a34 master demo
去除对暂存区中文件的修改
git checkout -- filename
拉取远端分支
git fetch
会将远端commit更新到本地,但并不会主动合并$ git fetch origin master 来自 github.com:icankeep/demo * branch master -> FETCH_HEAD
git pull
就相当于git fetch origin master && git merge origin/master
将修改保存进缓冲堆栈
如果暂存区的内容发生了改变,想要将这个改变暂时保存,可以使用git stash
如下,我们修改了README.md,新增了test文件
$ git status
位于分支 dev
要提交的变更:
(使用 "git restore --staged <文件>..." 以取消暂存)
修改: README.md
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: test
按照下面命令就可以保存啦
$ git stash
保存工作目录和索引状态 WIP on dev: ea15522 Merge branch 'merge_demo'
可以使用git stash list
和git stash show stash@{0}
查看stash信息
$ git stash list
stash@{0}: WIP on dev: ea15522 Merge branch 'merge_demo'
$ git stash show stash@{0}
README.md | 1 +
test | 1 +
2 files changed, 2 insertions(+)
将缓冲堆栈中内容提取出来
$ git stash pop
位于分支 dev
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git restore <文件>..." 丢弃工作区的改动)
修改: README.md
修改: test
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
丢弃了 refs/stash@{0}(f6965744573d97e52b165e886d625ae1ddccf36a)
查看提交历史
当我们开发完代码git commit之后,我们就可以通过下面的命令查看提交历史
$ git log
commit abf83aa02ceb51e71d209777e2411b27a0f6b417
Author: passer <whzhoua@gmail.com>
Date: Sat Nov 7 21:32:32 2020 +0800
add README.md
如图,每个commit都有对应的一个hash值(基本不可能碰撞)
查看文件修改差异
后续如果我们需要开发,可以通过git diff等查看修改后的文件和之前commit文件的差异
$ git diff README.md
diff --git a/README.md b/README.md
index 7b81bad..b4f319c 100644
--- a/README.md
+++ b/README.md
@@ -4,3 +4,4 @@ master demo
1
2
3
+modify some things
合并分支
当代码在本地开发并测试完成后,我们一般需要将代码合并到其他正式分支,解决冲突的具体操作我们在后面rebase中说明
$ git checkout master
$ git merge dev
合并多个提交rebase
rebase是git中非常强大的功能,既可以merge分支,也可以合并修改commit信息等。当我们本地开发时,可能会经常遇到刚提交完一个commit,发现代码中还有一些错误,于是又提交一个commit,会造成一个功能太多个commit,提交信息混乱,于是我们就需要合并这些无用的commit。
git rebase 分支名
git rebase demo
指当前分支合并demo分支,git merge
和git rebase
的差异在于如果当前分支已经有了新的提交(demo分支中不存在的提交),git merge
就会产生一个merge commit(即一个额外的提交),git rebase
则不会。实践出真知,直接开始操作在之前master分支"add README.md"提交之后切出新创建的rebase_demo分支和merge_demo分支
git checkout -b rebase_demo echo 'rebase demo' >> README.md git commit -am "rebase demo"
通过git log查看提交历史,发现已经提交
$ git log --graph --pretty=oneline --abbrev-commit * 0eec621 (HEAD -> rebase_demo) rebase demo * abf83aa (master) README.md
切换到master分支rebase我们的rebase_demo分支
$ git checkout master 切换到分支 'master'
$ git rebase rebase_demo 成功变基并更新 refs/heads/master。
$ git log --graph --pretty=oneline --abbrev-commit * 0eec621 (HEAD -> master, rebase_demo) rebase demo * abf83aa README.md
将rebase_demo分支合并到master分支成功,并且没有产生新的提交,我们接着通过merge的方式来看有没有新的提交
git checkout -b merge_demo echo 'merge demo' >> README.md git commit -am "merge demo"
通过
git log
查看提交历史,发现已经提交$ git log --graph --pretty=oneline --abbrev-commit * 65d472a (HEAD -> merge_demo) merge demo * abf83aa (master) README.md
git checkout master git merge merge_demo
$ git log --graph --pretty=oneline --abbrev-commit * 65d472a (HEAD -> master) merge demoREADME.md * abf83aa README.md
可知,当发起合并的分支中没有新commit时,rebase和merge都是不会产生新的提交的,我们现在来看看当存在新提交时,会是什么情况
首先,我们来看下当前的分支情况
$ git log --graph --pretty=oneline --abbrev-commit --all * 1fe102c (merge_demo) merge demo | * 0eec621 (rebase_demo) rebase demo |/ * abf83aa (HEAD -> master) README.md
master当前位于第一个提交,而rebase_demo分支提交了一个rebase demo, merge_demo提交了一个merge demo,为了产生冲突,我们在master分支提交一个master demo即可
echo 'master demo' >> README.md git commit -am "master demo"
$ git log --graph --pretty=oneline --abbrev-commit --all * 2ce5a34 (HEAD -> master) master demo | * 1fe102c (merge_demo) merge demo |/ | * 0eec621 (rebase_demo) rebase demo |/ * abf83aa README.md
此时每个分支都对文件README.md做出了修改,这时将rebase_demo或merge_demo合并到master,都将产生冲突
$ git rebase rebase_demo 自动合并 README.md 冲突(内容):合并冲突于 README.md error: 不能应用 2ce5a34... master demo Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort". 不能应用 2ce5a34... master demo
通过git status查看状态
$ git status 交互式变基操作正在进行中;至 0eec621 最后一条命令已完成(1 条命令被执行): pick 2ce5a34 master demo 未剩下任何命令。 您在执行将分支 'master' 变基到 '0eec621' 的操作。 (解决冲突,然后运行 "git rebase --continue") (使用 "git rebase --skip" 跳过此补丁) (使用 "git rebase --abort" 以检出原有分支) 未合并的路径: (使用 "git restore --staged <文件>..." 以取消暂存) (使用 "git add <文件>..." 标记解决方案) 双方修改: README.md 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
修改冲突文件,将<<<<<<< HEAD >>>>>>> 2ce5a34... master demo之间的内容修改成最终的,wq保存退出,重新
git add
$ vim README.md # git demo <<<<<<< HEAD rebase demo ======= # master master demo >>>>>>> 2ce5a34... master demo
$ git add README.md $ git rebase --continue
rebase_demo分支已经rebase到master分支,并没有产生新的提交
$ git log --graph --pretty=oneline --abbrev-commit --all * 24e6dfa (HEAD -> master) master demo * 0eec621 (rebase_demo) rebase demo | * 1fe102c (merge_demo) merge demo |/ * abf83aa README.md
回到最初状态继续尝试下merge
通过git reflog可以查看所以git操作的id,随时可以回退到对应的id
$ git reflog 24e6dfa (HEAD -> master) HEAD@{0}: rebase (continue) (finish): returning to refs/heads/master 24e6dfa (HEAD -> master) HEAD@{1}: rebase (continue): master demo 0eec621 (rebase_demo) HEAD@{2}: rebase (start): checkout rebase_demo 2ce5a34 HEAD@{3}: commit: master demo ...
我们通过git reset回到对应操作id上
$ git reset --hard 2ce5a34 HEAD 现在位于 2ce5a34 master demo
$ git log --graph --pretty=oneline --abbrev-commit --all * 2ce5a34 (HEAD -> master) master demo | * 1fe102c (merge_demo) merge demo |/ | * 0eec621 (rebase_demo) rebase demo |/ * abf83aa README.md
一切如初,继续尝试merge
$ git merge merge_demo 自动合并 README.md 冲突(内容):合并冲突于 README.md 自动合并失败,修正冲突然后提交修正的结果。
$ git status 位于分支 master 您有尚未合并的路径。 (解决冲突并运行 "git commit") (使用 "git merge --abort" 终止合并) 未合并的路径: (使用 "git add <文件>..." 标记解决方案) 双方修改: README.md 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
vim README.md # git demo <<<<<<< HEAD # master master demo ======= merge demo >>>>>>> merge_demo
发现解决完冲突之后,会提示需要commit来结束冲突,所以当存在冲突时merge会产生额外一个commit
git add README.md $ git status 位于分支 master 所有冲突已解决但您仍处于合并中。 (使用 "git commit" 结束合并)
$ git commit -m "merge" [master 52ecde5] merge
$ git log --graph --pretty=oneline --abbrev-commit --all * 52ecde5 (HEAD -> master) merge |\ | * 1fe102c (merge_demo) merge demo * | 2ce5a34 master demo |/ | * 0eec621 (rebase_demo) rebase demo |/ * abf83aa README.md
可知产生了52ecde5这个额外的commit,以上就是rebase和merge合并分支上的区别
git rebase
合并commit查看当前master分支状态
$ git log --graph --pretty=oneline --abbrev-commit * d576335 (HEAD -> master) 开发测试3 * b30a41e 开发测试2 * 209a0c1 开发测试 * 2ce5a34 master demo * abf83aa README.md
可知,存在一些开发测试过程中的commit,我们需要对其进行合并,即将3个开发测试的commit合并成一个,可以执行如下命令,3为合并的commit数目
git rebase -i HEAD~3
就会进入如下界面
pick 209a0c1 开发测试 pick b30a41e 开发测试2 pick d576335 开发测试3 # 变基 2ce5a34..d576335 到 2ce5a34(3 个提交) # # 命令: # p, pick <提交> = 使用提交 # r, reword <提交> = 使用提交,但修改提交说明 # e, edit <提交> = 使用提交,进入 shell 以便进行提交修补 # s, squash <提交> = 使用提交,但融合到前一个提交 # f, fixup <提交> = 类似于 "squash",但丢弃提交说明日志 # x, exec <命令> = 使用 shell 运行命令(此行剩余部分) # b, break = 在此处停止(使用 'git rebase --continue' 继续变基) # d, drop <提交> = 删除提交 # l, label <label> = 为当前 HEAD 打上标记 # t, reset <label> = 重置 HEAD 到该标记 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] # . 创建一个合并提交,并使用原始的合并提交说明(如果没有指定 # . 原始提交,使用注释部分的 oneline 作为提交说明)。使用 # . -c <提交> 可以编辑提交说明。 # # 可以对这些行重新排序,将从上至下执行。 # # 如果您在这里删除一行,对应的提交将会丢失。 # # 然而,如果您删除全部内容,变基操作将会终止。
按照提示修改pick为f,保存并退出即可
pick 209a0c1 开发测试 f b30a41e 开发测试2 f d576335 开发测试3
发现commit已经合并了
$ git log --graph --pretty=oneline --abbrev-commit * 86b2868 (HEAD -> master) 开发测试 * 2ce5a34 master demo * abf83aa README.md
当rebase用于合并commit时
如果需要修改commit信息,可以使用如下命令
git commit --amend
进入如下界面,修改并保存即可
开发测试 # 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交 # 说明将会终止提交。 # # 日期: Sat Nov 7 23:20:29 2020 +0800 # # 位于分支 master # 要提交的变更: # 修改: README.md #
发现commit信息已经被修正
$ git log --graph --pretty=oneline --abbrev-commit * 5d0d00a (HEAD -> master) 正式提交 * 2ce5a34 master demo * abf83aa README.md
重置提交reset
简单粗暴,直接命令上手,下面命令将commitId改成要回退到的commit hash值在执行就可以回退到指定的commit
git reset --hard commitId
除了--hard还有--mixed和--soft, git reset默认是--mixed
mixed
$ git reset --mixed 2ce5a34 重置后取消暂存的变更: M README.md
使用git status查看状态,发现之前commit的文件已经回退到工作区
$ git status 位于分支 master 尚未暂存以备提交的变更: (使用 "git add <文件>..." 更新要提交的内容) (使用 "git restore <文件>..." 丢弃工作区的改动) 修改: README.md 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
soft
$ git reset --soft 2ce5a34 $ git status 位于分支 master 要提交的变更: (使用 "git restore --staged <文件>..." 以取消暂存) 修改: README.md
发现使用soft重置之后,修改内容会添加到暂存区
hard
$ git reset --hard 2ce5a34 HEAD 现在位于 2ce5a34 master demo
$ git status 位于分支 master 无文件要提交,干净的工作区
使用hard重置提交,提交的内容均会消失,如果不小心重置了,可以用git reflog查看并重置到指定的操作上哦!
提取commit
我们在一个分支中可能会需要另一个分支的一个commit,这个时候我们就可以用到下面这个命令进行提取
如下状态,我们如果在rebase_demo分支获取master分支5d0d00a这个commit的内容,我们就可以使用cherry-pick
$ git log --graph --pretty=oneline --abbrev-commit --all
* 5d0d00a (HEAD -> master) 正式提交
* 2ce5a34 master demo
| * 1fe102c (merge_demo) merge demo
|/
| * 0eec621 (rebase_demo) rebase demo
|/
* abf83aa README.md
git cherry-pick 5d0d00a
给commit打上标签
给当前分支最新的提交打上标签
$ git tag v1.0
查看标签
$ git tag
v1.0
$ git log --graph --pretty=oneline --abbrev-commit
* ea15522 (HEAD -> dev, tag: v1.0, master) Merge branch 'merge_demo'
将标签推送到远端仓库
$ git push origin v1.0
总共 0(差异 0),复用 0(差异 0),包复用 0
To github.com:icankeep/demo.git
* [new tag] v1.0 -> v1.0
推送本地的所有tag
git push origin --tags
将标签打到具体的commit上
$ git tag v0.7 8c28b6f
可以看到v0.7已经打到8c28b6f上了
$ git show v0.7
commit 8c28b6fbe67d37723f43f1df20a0d360cabf14f5 (tag: v0.7)
Author: passer <whzhoua@gmail.com>
Date: Sun Nov 8 13:51:04 2020 +0800
add README.md
如果打错标签了,需要删除,可以按照下面的命令
$ git tag -d v0.7
已删除标签 'v0.7'(曾为 8c28b6f)
当然,这只是在本地删除标签,如果该标签已经推送到远端,想要删除的话,还得加上下面这一步
$ git push origin :refs/tags/v0.7
To github.com:icankeep/demo.git
- [deleted] v0.7
最后更新于
这有帮助吗?