✏️
blog
  • README
  • 2023 11
    • expect使用
  • 2023 10
    • 通过Appium给iOS应用自动化执行脚本
  • 2023 06
    • 三种ThreadLocal详解
    • 常见限流算法总结
    • 分布式ID生成算法
  • 2023 05
    • 线上机器CLOSE_WAIT连接数异常排查
    • 多数据源引发transactional事务回滚失效
  • 2023 04
    • MySQL中BufferPool
  • 2022 12
    • Linux IO
    • Netty总结
  • 2022 04
    • Thrift
  • 2022 03
    • JVM命令总结
    • 频繁FullGC定位思路
    • Redis总结
    • Spring常见问题总结
    • Kafka总结
  • 2022 02
    • Dubbo柔性服务天池大赛总结
  • 2021 12
    • 泛型中的extends和super
    • 手写一个Spring Boot Starter
  • 2021 11
    • 常用消息队列总结
  • 2021 10
    • swagger2快速使用
    • SpringBoot接口cors跨域访问
  • 2021 08
    • 常用shell命令总结
  • 2021 05
    • 线程cpu飙升排查
    • zookeeper install
  • 2021 04
    • Java虚拟机
    • [Spring Boot](2021-04/2021-04-04-Spring Boot.md)
    • [Spring MVC](2021-04/2021-04-04-Spring MVC.md)
    • 分布式ID
    • 消息队列
    • [Spring AOP](2021-04/2021-04-05-Spring AOP.md)
    • 布隆过滤器
    • Scala内核Spark阻塞排查
  • 2020 12
    • 使用Python优雅实现tail命令
  • 2020 11
    • Spark基础架构
    • 一文搞定Git
    • Spark线上问题引发的思考
  • 2020 04
    • 使用GitBook
  • 2019 05
    • SELinux、Netfilter、iptables、firewall和ufw五者关系
    • 安装npm和nodejs
    • 访问不到云服务器中的项目
  • 2019 04
    • 二叉树中节点与度数
    • 实现会话跟踪的技术有哪些
    • 计算机操作系统-死锁
    • Semaphore Count Down Latch Cyclic Barrier
    • Java内存模型
    • 双重检查锁定
    • synchronized实现底层
    • Lock接口
    • HTTP与HTTPS的区别
    • Java中线程池
    • Java中的阻塞队列
    • 排序算法
  • 2019 03
    • MySQL中索引
    • MySQL存储引擎
    • MySQL锁机制
    • n的阶乘结果后面0的个数
由 GitBook 提供支持
在本页
  • Git
  • 安装git
  • 快速上手
  • 进阶
  • 查看分支
  • 创建并切换分支
  • 拉取远端分支
  • 将修改保存进缓冲堆栈
  • 查看提交历史
  • 查看文件修改差异
  • 合并分支
  • 合并多个提交rebase
  • 重置提交reset
  • 提取commit
  • 给commit打上标签

这有帮助吗?

  1. 2020 11

一文搞定Git

上一页Spark基础架构下一页Spark线上问题引发的思考

最后更新于4年前

这有帮助吗?

Git

通俗来讲Git就是一个代码版本控制系统,多个开发人员可以协同开发,开发时可以将代码提交到本地,开发完成后,push到远端即可,不用到处备份copy代码,方便查看版本间的diff。

安装git

  1. Windows

  2. Mac OS X

    终端执行

     $git --version
  3. Linux

快速上手

  1. 首先我们需要在终端界面初始化git文件夹,这里我们创建了demo文件夹并初始化

     $ mkdir demo && cd demo
     $ git init 
     已初始化空的 Git 仓库于 /private/tmp/demo/.git/

    在初始化后,本地目录的文件就是在工作区中,在执行add后就添加到了暂存区,commit之后就正式存在本地仓库了

  2. 创建一个README.md文件,并添加Git中

     $ touch README.md
     $ echo '# git demo' > README.md
     $ cat README.md  
     # git demo
  3. 添加到stage中

     $ git add README.md
  4. 生成一个提交到本地仓库, -m后跟的内容为本次提交的描述信息,提交的文件为已经add到stage中的文件

     $ git commit -m "add README.md" 
     [master(根提交) 8c28b6f] add README.md
     1 file changed, 1 insertion(+)
     create mode 100644 README.md
  5. 随时查看本地git状态

     $ git status 
     位于分支 master
     无文件要提交,干净的工作区

    如图就表示工作区中的文件均已提交到本地git仓库中

  6.  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 分支:(未知)
  7. 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提交

进阶

查看分支

  1. 查看本地分支

     $ git branch   
     * dev
     master
     merge_demo
     rebase_demo
  2. 查看所有分支

     $ 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. 切换创建新分支

  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
  2. 去除对暂存区中文件的修改

     git checkout -- filename

拉取远端分支

  1. git fetch会将远端commit更新到本地,但并不会主动合并

     $ git fetch origin master
     来自 github.com:icankeep/demo
     * branch            master     -> FETCH_HEAD
  2. 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。

  1. 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合并分支上的区别

  2. 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

  1. 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")
  2. soft

     $ git reset --soft 2ce5a34
     $ git status 
     位于分支 master
     要提交的变更:
     (使用 "git restore --staged <文件>..." 以取消暂存)
             修改:     README.md

    发现使用soft重置之后,修改内容会添加到暂存区

  3. 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

访问 下载安装

访问 下载安装

添加远端仓库(可以到中创建远端仓库,获取地址),origin为远端仓库名,可以自行指定

Git官网
Git官网
快速上手
进阶
查看分支之git branch
拉取远端分支之git fetch 和 git pull
创建并切换分支之git checkout
将修改保存进缓冲堆栈之git stash
查看提交历史之git log
查看文件修改差异之git diff
合并分支之git merge
合并分支、提交之git rebase
重置提交之git reset
提取commit内容之git cherry-pick
给commit打上标签之git tag
Github