Git 常用命令知识笔记

Git 仓库数据结构

  • Git 仓库由一个个的 commit 组成
  • 某些 commit 上会有一些 branch 指向它们,这些 branch 的本质是引用
  • 有一个特殊的引用叫做 HEAD,它始终指向当前的位置,这个位置可以是 commit,也可以是 branch

在这里插入图片描述

staging area 暂存区和 add

  • staging 原意:舞台表演前的筹划准备(例如汇集道具和演员)。Git 中的意思:把改动内容汇集起来以待提交。
  • staging area:待提交的修改内容暂时存放的地方。主要用于和已经改动但不打算提交的内容区分开来。
  • add 指令:把指定的内容放进暂存区。

在这里插入图片描述

  • Workspace:工作区
  • Stage:暂存区
  • Repository:仓库区(本地仓库)
  • Remote:远程仓库
git add [file1] [file2] ...                # 添加指定文件到暂存区
git add [dir]                              # 添加指定目录到暂存区,包括子目录
git add .                                  # 添加当前目录的所有文件到暂存区
git add -p                                 # 添加每个变化前,都会要求确认
										   # 对于同一个文件的多处变化,可以实现分次提交
git rm [file1] [file2] ...                 # 删除工作区文件,并且将这次删除放入暂存区
git rm --cached [file]                     # 停止追踪指定文件,但该文件会保留在工作区
git mv [file-original] [file-renamed]      # 改名文件,并且将这个改名放入暂存区 

git commit

  • commit 表示对于一次改动的提交,它可以代表当前时刻下 Git 仓库的完整快照,但本质上,commit 只是记录了距离上一次 commit 之间的改动。
git commit -m [message]                          # 提交暂存区到仓库区
git commit [file1] [file2] ... -m [message]      # 提交暂存区的指定文件到仓库区
git commit -a                                    # 提交工作区所有更改到仓库区
git commit -v                                    # 提交时显示所有diff信息
git commit -am 'message'                         # 将add和commit合为一步

# 使用一次新的commit,替代上一次提交
git commit --amend -m [message] # 如果代码没有任何新变化,则用来改写上一次commit的提交信息(用于反复修改)
git commit --amend [file1] [file2] ... # 重做上一次commit,并包括指定文件的新变化

branch 和 master

  • branch 的含义是分支,指的是仓库结构出现分叉时的不同的「叉」
  • 本质上,git 的 branch 是引用(reference),即指向某个 commit 的指针

直观感觉的 branch

在这里插入图片描述

而实质上的 branch

在这里插入图片描述

所以,branch 和你什么时候创建的它无关,也和仓库的起点无关,只和它当前指向哪个 commit 有关。

  • master 是⼀个特殊的 branch,因为它是 Git 的默认 branch(默认 branch 可以修改)。默认 branch 的特点:
    • 执行 clone 方法把仓库取到本地的时候,默认 checkout 出来的是默认 branch,即 master
    • 执行 push 命令把本地内容推送到远端仓库的时候,远端仓库的 HEAD 永远跟随默认 branch,而不是和本地 HEAD 同步。换句话说,只有 push master 分支到远端的时候,远端的 HEAD 才会移动。
git branch                    # 列出所有本地分支
git branch -r                 # 列出所有远程分支
git branch -a                 # 列出所有本地分支和远程分支
git branch -v                 # 查看各个分支最后一次提交
git branch –merged            # 查看哪些分支合并入当前分支
git branch –no-merged         # 查看哪些分支未合并入当前分支
  
git branch [branch-name]      # 新建一个分支,但依然停留在当前分支
git checkout -b [branch]      # 新建一个分支,并切换到该分支
git branch [branch] [commit]  # 新建一个分支,指向指定commit
git checkout -                # 切换到上一个分支

git branch -d [branch-name]            # 删除分支
git branch -D mybranch                 # 强制删除分支
git push origin --delete [branch-name] # 删除远程分支
git branch -dr [remote/branch]         # 删除远程分支

HEAD

  • HEAD 也是引用,但它不是 branch,它代表了当前所处的位置。HEAD 不仅可以指向某个 commit,也可以指向某个 branch(例如 masterfeature1
  • 当每次 commit 的时候,HEAD 不仅随着新的 commit 一起移动,而且如果它指向了某个 branch,那么它也会带着 branch 一起移动

在这里插入图片描述

git clone

  • clone 是从远端仓库初次把数据取下来:
git clone https://github.com/xxxx.git

clone 命令具体会做两件事:

  1. 把整个仓库中的所有 branch 取下来,并把从初始 commit 到达这些 branch 的路径上的所有 commit 都取下来:
    在这里插入图片描述
  2. 从初始 commit 开始,向 master 指向的 commit,一个个地把每个 commit 应用,最终得到一个「当前」状态的仓库内容,写进 Git 所在的目录(这个目录叫做 working tree)

git log

  • HEAD 指向的 commit 开始,倒序显示每一个 commit 的摘要信息(最新的排在最前面)
git status        # 显示有变更的文件
git log           # 显示当前分支的版本历史
git log --stat    # 显示commit历史,以及每次commit发生变更的文件
git log -S [keyword]  # 搜索提交历史,根据关键词
git log [tag] HEAD --pretty=format:%s  # 显示某个commit之后的所有变动,每个commit占据一行
git log [tag] HEAD --grep feature      # 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件
git log --follow [file]                # 显示某个文件的版本历史,包括文件改名
git whatchanged [file]
git log -p [file]                      # 显示指定文件相关的每一次diff
git log -5 --pretty --oneline          # 显示过去5次提交
git shortlog -sn                       # 显示所有提交过的用户,按提交次数排序

git merge

  • merge 就是合并,它会把当前 commit 和指定 commit(所谓 commit,可以直接用它的 hash 值来指定,例如 4a0a1b ,也可以用一个直接或间接指向它的引用来指定,例如 master 或者 HEAD )进行合并,并把这个合并行为创建成一个新的commit
git merge feature1       # 合并feature1分支至当前分支
git merge origin/master  # 合并远程master分支至当前分支

在这里插入图片描述

git merge 会产生一个新的提交。

merge 行为所产生的 commit,是一种特殊的 commit

  1. 它不需要有改动,只要指定两个(或更多个)父 commit 就好
  2. 正如上面这句说的,它有两个或更多个父 commit,这是一般的 commit 不具有的性质

git checkout master 相当于merge from mastergit merge mybranch 相当于 merge from mybranch

merge 冲突

当 Git 不知道怎么合并某两处冲突的修改时,会中断自动合并,并对冲突文件进行标记。解决方法:

  1. 把文件手动修改好
  2. 把修改的内容使用 add 来添加进暂存区
  3. git merge --continue 来继续自动合并流程

关于 origin/ 打头的 branch

  • 本地仓库中,有一些以 origin/ 打头的 branch,它们是远端仓库(别名为origin)的本地镜像。它们的作用是方便在本地查看远端仓库的 branch 状态。

远端仓库默认名称是 origin,但也可以给它们起别的名称

origin/ 分支并不在本地直接操作,它们一般只在两种情况下会进行自动更新:

  1. 在执行 push 的时候,push 成功后,push 成功的 branch 会把它对应的 origin/ branch更新到当前 commit(因为远端的 branch 已经随着 push 的成功而更新,所以本地镜像也一起更新)
  2. 在执行 pull 或者 fetch 的时候,由于从远端拿到了所有最新的 branch 状态,所以也会一同更新所有的 origin/ branch

关于 origin/HEAD:这是一个永远跟随 origin/master 的引用,它最大的作用是用来标记默认 branch

git push

  • 把当前的本地分支推送到远端仓库的指定分支。
git push origin feature1

具体做两件事:

  1. HEAD 所指向的 branch (只是一个引用哦)推送到远端仓库
  2. 从这个 branch 向前回溯,远端仓库缺少的每一个 commit 也推送到远端仓库。
  3. pushbranch 的本地镜像 origin/xxx 更新

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意:origin/HEAD 并没有在图上画出来,但如果 pushmaster(即默认branch),那么本地的 origin/HEAD 也会更新到 master 的最新位置;当push 的是其他 branch 的时候,origin/HEAD 并不会更新。
也就是说, origin/HEAD 只和默认分支相关,和 HEAD 是无关的。

git push [remote] [branch]   # 上传本地指定分支到远程仓库
git push [remote] --force    # 强行推送当前分支到远程仓库,即使有冲突
git push [remote] --all      # 推送所有分支到远程仓库
git push origin master       # 将当前分支push到远程master分支

git pull

  • 把远端 branch 取到本地。
git pull origin feature1

具体做的事有三件:

  1. 把远端所有 branch 的最新位置更新到本地的 origin/xxx 镜像
  2. 要到达这些 branch,本地所缺少的所有 commit,也取到本地
  3. origin/当前branch 的内容合并到当前 branch

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

事实上,git pull origin feature1 会分成两部执行,它等价于下面两行:

git fetch
git merge origin/feature1

git fetch 的含义:

git fetch [remote]   # 下载所有远程分支,但不更新本地分支(另需merge) 

git checkout

  • 移动 HEAD,让它指向某个 commit 或某个 branch
  • checkout --detach :让 HEAD 脱离当前 branch,直接指向下面的commit
git checkout -b dev origin/master 

从远程的 master 分支检出到本地的dev上,并切换到新建的本地dev分支上。

其实也是相当于两个命令的缩写:

git branch dev  # 创建分支
git checkout dev # 切换分支

本地的项目中,一直都保留着一个master分支,并关联远程的master分支。本地的master分支,你可以当作是个干净的主线,每次新来一个需求的时候你都可以以该分支为基准,新建一个分支进行开发,最后进行合并,并由这个基准分支(本地master)提交到远程;具体的专业信息你可以参照文档:Git-分支-分支的新建与合并

通过 checkout 可以恢复指定文件(如不小心commit掉的)到工作区(未提交状态):

git checkout [file]             # 恢复暂存区的指定文件到工作区
git checkout [commit] [file]    # 恢复某个commit的指定文件到暂存区和工作区
git checkout .                  # 恢复暂存区的所有文件到工作区
git checkout -b master_copy                               # 从当前分支创建新分支master_copy并检出
git checkout -b master master_copy                        # 上面的完整版
git checkout features/performance                         # 检出已存在的features/performance分支
git checkout --track hotfixes/BJVEP933                    # 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支
git checkout v2.0                                         # 检出版本v2.0
git checkout -b devel origin/develop                      # 从远程分支develop创建新本地分支devel并检出
git checkout -- README                                    # 检出head版本的README文件(可用于修改错误回退)

git rebase

  • 把当前 commit(以及它之前的 commits)应用到指定的需要 rebasecommit上。

Git 中的每一个 commit 都是不会改变的,所以 rebase 之后的每个 commit 都是新产生的,而不是对原先的 commit 进行「修改」

在这里插入图片描述

rebase 冲突

rebase 的冲突解决方法和 merge 冲突一样,只是把 git merge --continue 改成 git rebase --continue 就行了。

git reset

  • 把当前 branch 指向指定的 commit
git reset [指定commit] # 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变

移动到指定 commit,并保留 working tree 的内容。

git reset --hard [指定commit]

移动到指定 commit,并重置 working tree。

git checkout .                         # 撤销未暂存修改
git reset                              # 撤销未提交的暂存至 git add . 状态
git reset --hard                       # 撤销所有修改,保持与上一次一致
git reset --hard origin/master         # 撤销本地提交
git reset --hard HEAD^ & git push –f   # 撤销已推送
git reset --keep [commit]              # 重置当前HEAD为指定commit,但保持暂存区和工作区不变

reset 和 checkout 的区别

它们都是移动 HEAD,但 chekcout 移动的时候是自己移动,不带着 branch 一起;而 reset 会带着 branch 一起移动。

git tag

git tag                     # 列出所有tag
git tag [tagname]           # 新建一个tag在当前commit
git tag [tagname] [commit]  # 新建一个tag在指定的commit
git tag -d [tagname]        # 删除本地tag

另⼀种引用类型。作用:设置持久标记,例如版本号。和 branch 区别:

  • 不能改变
  • 不能被 HEAD 指向
  • origin/master, origin/feature, origin/HEADtag 有相似之处:也不能从本地改变位置,也不能被 HEAD 指向
git tag                          # 显示已存在的tag
git tag -a v2.0 -m 'xxx'         # 增加v2.0的tag
git show v2.0                    # 显示v2.0的日志及详细内容
git log v2.0                     # 显示v2.0的日志
git checkout v2.0                # 检出版本v2.0

git checkout v2.0 会把远程 tag v2.0拉到本地,在本地创建一个无Head指向的分支,而远程tag分支不受任何影响,将本地分支push后将发布一个新的分支。

基于 taggit check out 出来的分支可以执行 git branch xxx 创建新的本地分支,然后git check out xxx , commitpush到远端可以创建一个新分支(也可以不提交)。

git push [remote] [tag]         # 提交指定tag
git push [remote] --tags        # 提交所有tag
git checkout -b [branch] [tag]  # 新建一个分支,指向某个tag
git push origin :refs/tags/[tagName] # 删除远程tag

git revert

  • 撤销指定的提交,用于已经 pushmaster 的内容需要删除。它的原理是创建一个新的commit,内容是指定 commit 的「相反内容」。
# 新建一个commit,用来撤销指定commit 后者的所有变化都将被前者抵消,并且应用到当前分支
git revert [commit]

git cherry-pick

  • 选择指定的某个或某些commit,合并进当前分支。 将 commitHEAD 处。
git cherry-pick [commit1] [commit2]
git cherry-pick ff44785404a8e           # 合并提交ff44785404a8e的修改

⽐如我想抛弃某个 branch,这个特性不要了,但它的某两个 commit 我希望保留。

git reflog

git reflog <branch>

查看指定的引用(HEADbranch)的移动历史,从而找到之前的某个特定 commit

branch 的作用

  • 让项目可以同时做多件事
  • 未做完的事不会被项目真正收录

在这里插入图片描述

Feature Branching

  • 做法:每开发一个新的功能做一个修复,都使用单独的分支,在做完之后 mergemaster
  • 本地 merge:由于别人可能在你之前 push 过,所以你的 push 可能失败。所以通常会需要先 pull 一下,然后再 push

在这里插入图片描述

  • 使用 github:先创建 pull request,在同事审阅完成之后,通过按钮实现在线 merge
  • pull request 是什么:是开发者对远端仓库的提出的「拉取某个 branch」的请求

Git Flow

在这里插入图片描述

在这里插入图片描述

两条持久主线:master 和 develop

master

  • 最稳定的分支(长期分支),只用于存放所有的发布
  • 每次有了新的 commit 之后,立即打一个 tag 来记录
  • 正式发布的集成目标

develop

  • 日常开发分支(长期分支),用于存放不稳定版本的发布
  • develop 并不是直接用于开发 feature 的,开发 feature 需要专门的branch
  • develop 在第一时间从 master 上分离出来
  • 需要开发任何功能的时候,从 develop 创建出新的 feature branch,开发完成后合并回 develop(合并的时候使用 --no-ff),然后删掉 feature branch
  • 当下一正式版本需要的所有功能开发完成之后,从 develop 上创建新的 release branch,并在 release branch 合并到 master 后合并回develop(合并的时候用 --no-ff),然后删掉 release branch

feature branches

  • 功能型开发分支(目的型分支)
  • 始于develop,终于develop,每次开发新功能是从 develop 创建,开发完成后合并到 develop(使用 --no-ff),然后被删掉

release branches

  • 预交付分支(目的型分支)
  • 始于develop ,终于master & develop
  • 每次下一版本的功能开发完毕后,从 develop 上创建
  • 创建完成后,更新版本号,然后单独做一个新的 commit
  • 如果有 bug fix,直接在 release branch 上修复,bug fix 完成后,合并到 masterdevelop(使用 --no-ff),然后 release branch 被删掉

hotfix branches

  • 热修复分支(目的型分支)
  • 始于master,终于master & develop
  • 已正式发布的产品发现 bug,直接从 master 或者出问题的 tag 上创建 hotfix branch 进行紧急修复,修复完成后合并到 masterdevelop(或 release branch 如果有的话)(使用 --no-ff),然后被删掉。

这里还有一个点,如果是本地创建的 feature 分支(从develop分支创建)开发完成后,成功合并到 develop 分支后,这个本地 feature 分支是可以删掉的,并不一定要提交到远程,不然远程仓库里会有很多冗余分支。

Git 配置

首先需要下载Git客户端:https://git-scm.com/

然后输入下面命令进行配置(主要是用户名信息):

# 设置提交代码时的用户信息
git config [--global] user.name "[name]"               # 配置用户名
git config [--global] user.email "[email address]"     # 配置邮件
git config --list          # 显示当前的Git配置
git config -e [--global]   # 编辑Git配置文件

Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)

生成 RSA 密钥 SSH Key

ssh-keygen -t rsa
cat ~/.ssh/id_rsa.pub  

第一次使用该命令时,之后会在用户目录下生成.ssh文件夹(如果是windows就是在C:/Users/用户名/目录下, 如果是Mac就用上面的cat命令查看即可),其中会包括:id_rsaid_rsa.pub以及其他的文件,其中 id_rsa 是私钥,id_rsa.pub 是公钥。

id_rsa.pub公钥文件中的内容全部复制,以使用 gitlab 仓库为例,打开gitlab,找到Profile Settings –> SSH Keys —> Add SSH Key,把复制的内容粘贴到Key所对应的文本框,在Title对应的文本框中给这个sshkey设置一个名字,点击Add key按钮。

在这里插入图片描述
在这里插入图片描述

检测是否配置成功:本地 git-bash 输入以下命令

$ ssh -T git@github.com

若显示如下,则代表配置成功:

在这里插入图片描述

绑定成功了之后,就可以复制 gitlab上面项目的 ssh 地址进行 git clone ,将 gitlab上项目克隆到本地了:

在这里插入图片描述

gitlab 上还可以添加项目成员和设置权限:

在这里插入图片描述

访问权限

  • Private - 私有,只有属于该项目成员才能clone
  • Internal - 内部,有Gitlab账号的人都可以clone
  • Public - 公开,任何人可以clone

行为权限

  • Guest - 访客
  • Reporter - 报告者; 可以理理解为测试员、产品经理理等
  • Developer - 开发者
  • Master - 主人,负责对Master分⽀支进行维护
  • Owner - 拥有者

git 克隆项目时使用 HTTPS url 跟 SSH url 的区别:

  • 使用 https url 克隆对初学者来说会比较方便,复制 https url 然后到 git Bash 里面直接用 clone 命令克隆到本地, 但是每次fetchpush代码都需要输入账号和密码,这也是https方式的麻烦之处。
  • 而使用 SSH url 克隆却需要在克隆之前先配置和添加好SSH key,因此,如果你想要使用SSH url克隆的话,你必须是这个项目的拥有者(或者你的项目的主管有权限添加SSH key)。否则你无法添加SSH key,另外 ssh 默认每次fetchpush代码不需要输入账号和密码,如果你想要每次都输入账号密码才能进行fetchpush也可以另外进行设置。

Git 命令速查表

在这里插入图片描述

这个图上的命令不全,这里有一个总结的比较全面的帖子:Git常用命令,总结的很全了!