Git 操作手册

Huy大约 16 分钟linuxlinux

记录一些常用的 Git 命令,旨在一文弄懂所有常用的 Git 指令。

新建项目操作

  1. 克隆项目到本地:git clone xxxxx

  2. 设置 Git 用户信息:

  3. 设置 GPG 签名:

    若无 GPG 秘钥,可看 GIthub 添加 GPG 签名 一文。

    • 列出本地公钥:

      $: gpg --list-keys
      
      /xxxx...xxxx/pubring.kbx
      -----------------------------
      pub   edxxx 2023-xx-xx [SC] [expires: 2025-xx-xx]
            C1Fxxxxxxx79D
      uid           [ultimate] yourName <123xxx678+yourEmail@users.noreply.github.com>
      sub   cvxxx 2023-xx-xx [E] [expires: 2025-xx-xx]
      
    • 对提交 commit 进行签名:

      $: git config user.signingkey [PRIMARYKEYID]
      
      • 其中 PRIMARYKEYID 为公钥 C1Fxxxxxxx79D
    • 设置自动对 commit 进行签名:

      $: git config commit.gpgsign true
      

    注:上述的 git config 均可在后面添加 --global 将设置变成全局。

分支管理规范

  • master 主分支,受保护,不存放源代码,不直接提交代码,所有的 上线文件 需要推送到此分支。

  • develop 受保护,主分支,不能直接提交代码,在这个分支只能增加从 feat 合并 过来的 commit

  • feature 开发新功能,以 develop 为基础,创建该分支, 命名规范:feature/xxx 。开发完成后,提交合并请求到 release 分支进行提测。

  • release 提测分支:

    当有一组 feature 开发完成,首先会合并到 develop 分支,进入提测时,会创建 release 分支。 如果测试过程中若存在 bug 需要修复,则直接由开发者在 release 分支修复并提交。 当测试完成之后,合并 release 分支到 masterdevelop 分支,此时 master 为最新代码,用作上线。

  • hotfix 修复分支,从 develop 切出,命名规则同feature, 以 hotfix/ 命名。

常用的 GIT 操作指令open in new window

日常指令

1 正常开发流程

后续还会具体介绍,开发流程规范。此处介绍正常开发使用的指令:

// 1 列出分支(无参数时, 会列出本地分支)
$: git branch
// 查看本地及远程分支情况
$: git branch -a
// 查看本地分支的追踪情况
$: git remote show origin

// 2 创建分支指令
$: git branch branchname

// 3 切换分支指令(加指令 -b 为创建新分支,并切换过去)
$: git checkout branchname
$: git checkout -b newBranchname

// 4 删除分支
// 4.1 删除本地分支
$: git branch -d branchname
// 4.1.2 强制删除用大写
$: git branch -D branchname

// 4.2 删除远程分支
$: git push origin -d remoteBranchName
$: git push origin :remoteBranchName
// 4.3 远程已删除分支,本地同步
$: git remote prune origin


// 5 合并分支到【当前主分支中去】, 因此需要先切换到"待合并分支"
$: git checkout master
$: git merge --no-ff newBranch

// 6 个人开发,在个人分支上用分基 rebase 合并 master主分支 到个人分支上
$: git checkout myBranchName
    // 开发 xxxx, 开发完成后。先 rebase master 主分支
$: git rebase master
  // 变基后, 再执行步骤5。将个人分支合并到 master 分支上
$: git checkout master
$: git merge --no-ff myBranchName

// 合并完后, 删除本地及远程分支
$: git branch -d newBranch
$: git push origin -d newBranch

git mergegit merge --no--ff 的区别:

  • fast-forward,默认使用,保留分支的提交记录,但不会生成合并的提交记录。 新特性分支删除后,会丢失分支信息。
  • –no-ff,关闭fast-forward模式,在提交时,保留分支的commit历史,并生成一次合并的提交记录。
  • --squash,将多次分支commit历史压缩为一次,合并的时候相当于提交一次额外的 commit 进行总结。
git merge几种模式
git merge几种模式

2. 本地与远程端的交互

正常流程是,查看状态、拉取、修改代码后,推送

// 远端数据库操作
// 0. 查看上次提交后是否有对文件进行再次修改, 若加 -s 则为获取简短输出结果
$: git status

// 1. 拉取 git pull 等于 git fetch + git merge
$: git pull

// 本地开发 xxxx, 开发完成后 推送

// 2. 推送
// 2.1 添加文件到暂存区: 单一文件用 git commit xxx
$: git add .
// 2.2.1 将暂存区内容添加到仓库中去
$: git commit -m [message]
// 2.2.2 或者可以不需要执行 git add 命令直接提交代码。 不推荐
$: git commit -a
// 2.3 正常推送
$: git push
// 2.3.1 推送本地所有的分支到远程
$: git push --all

解决冲突

中途会有问题,如多人协作时,可能在你拉取后,别人已经推送了代码。此时,我们要用到一些高级操作,如 rebase 变基。有几种方案:

假设此时,他人已经 push 相关代码到远程端了。

  1. 【方案 1】正常流程,在 push 时,先用 git pull --rebase 拉取变基代码。而后,再解决冲突,推送。

    $: git add .
    $: git commit -m [message]
    // 变基拉取 git pull --rebase 等于 git fetch + git rebase
    $: git pull --rebase
    // 【有冲突】这时Git会停止rebase并让用户去解决冲突,解决完冲突后,用git add命令去更新这些内容,然后不用执行git-commit,直接执行 git rebase --continue, 这样git会继续apply余下的补丁。
    $: git add .
    $: git rebase --continue
    // 在任何时候,都可以用git rebase --abort参数来终止rebase的行动,并且mywork分支会回到rebase开始前的状态
    $: git rebase --abort
    $: git push
    
  2. 【方案 2】先 git stash 临时贮藏代码,正常拉取。而后 git stash pop 推出,解决冲突,推送。

    $: git stash
    // 不同分支, 则用 git rebase <otherBranch>
    $: git pull
    // 推出贮藏
    $: git stash pop
    // 若有冲突 解决冲突; 注意, 若冲突,并不会将贮藏记录消除,还需使用 git stash drop 删除记录
    $: git stash drop
    // 若冲突过多, 可撤销贮藏改变
    $: git reset --hard
    $: git add .
    $: git commit -m [message]
    $: git push
    

个人使用,已经写完代码了,用方案 1 推送;还未写完,则用方案 2 临时贮藏。

版本回退

版本回退需注意 git revertgit reset 的区别

  • git revert

    git revert :是撤销某次操作,此次操作之前和之后的 commit 和 history 都会保留,并且把这次撤销作为一次最新的提交。git revert 是提交一个新的版本,将需要 revert 的版本的内容再反向修改回去,版本会递增,不影响之前提交的内容。

    1. 当代码已经 commit 但没有 push 时,可使用如下命令操作:

      git revert HEAD //撤销倒数第一次提交

      git revert HEAD^ //撤销倒数第二次提交

      git-revert HEAD~2 //撤销倒数第三次提交

      git revert commit //(比如:fsxxxxff)撤销指定的版本,撤销也会作为一次提交进

    2. 当代码已经 commit 并 push 时,可使用如下命令:

      git revert HEAD~1 //代码回退到前一个版本

    当回退有冲突时,需手动合并冲突并进行修改,再 commit 和 push。这相当于增加了一次新的提交并且版本库中有记录。

  • git reset 不可逆回退!!!

    git reset 是撤销某次提交,但是此次之后的修改都会被退回到暂存区。除了默认的 mixed 模式,还有 soft 和 hard 模式。

    --soft不删除工作空间改动代码,撤销 commit不撤销 git add .

    --hard删除工作空间改动代码,撤销 commit撤销git add .

    • 注意完成这个操作后,就恢复到了上一次的 commit 状态。

    --mixed:【默认参数】不删除工作空间改动代码,撤销 commit撤销git add .

    简单的讲,正常提交顺序是: git add . --> git commit

    对应上面的回退顺序是: git reset --soft --> git reset --mixed

    • 撤销 commitgit reset --soft HEAD^
    • 撤销 commit 且 撤销贮藏 add .git reset HEAD^
    • 撤销全部提交且删除改动代码(慎重): git reset --hard HEAD^

    总是若是记不清楚 soft 软回退和 hard 硬回退的区别,就无脑选择默认的 mixed 混合回退就好了,此命令也最常用。下面看几个实际情况:

    1. 如果我们的有两次 commit 但是没有 push 代码

      $: git reset HEAD~1      //撤销前一次 commit,所有代码回到 Working Copy
      
    2. 假如我们有几次代码修改,并且都已经 push 到了版本库中。

      $: git reset --hard HEAD~2   //本地的Wroking Copy回退到2个版本之前。
      $: git push origin <banchName> --force  // --force 为强制覆盖远程分支
      
      // 但更建议使用 “--force-with-lease”,确保不会覆盖他人的代码!!!
      

      注意!当我们使用强制指令时,若在远程的该分支中有他人的贡献,--force 是会覆盖掉他人的代码的,所以为了保险起见,应当用 --force-with-lease

    3. 只回退某个指定文件到指定版本

      $: git reset a4xxxxa35e [options]
      
    4. 回退到指定版本

      $: git reset --hard commitId(通过git log可查看提交的commitId)
      

汇总

# 恢复暂存区的指定文件到工作区
$ git checkout [file]

# 恢复某个commit的指定文件到暂存区和工作区
$ git checkout [commit] [file]

# 恢复暂存区的所有文件到工作区
$ git checkout .

# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
$ git reset [file]

# 重置暂存区与工作区,与上一次commit保持一致
$ git reset --hard

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

# 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
$ git reset --hard [commit]

# 重置当前HEAD为指定commit,但保持暂存区和工作区不变
$ git reset --keep [commit]

# 新建一个commit,用来撤销指定commit。
# 实际上是多了一次提交且撤销了这次commit提交的改动
$ git revert [commit]

# 暂时将未提交的变化移除,稍后再移入
$ git stash
$ git stash pop

贮藏与清理

贮藏(stash)会处理工作目录的脏的状态——即跟踪文件的修改与暂存的改动——然后将未完成的修改保存到一个栈上, 而你可以在任何时候重新应用这些改动(甚至在不同的分支上)。

1. 贮藏
$: git stash
或者可以添加备注: 方便查找
$: git stash save "save message"

2. 查看
$: git stash list

3. 显示做了哪些改动, 或者加后缀参数,显示其它贮藏。number 为数值
$: git stash show
$: git stash show stash@{number}

4. 应用贮藏, 默认第一个,加后缀为应用其它贮藏。number 为数值
$: git stash apply
$: git stash apply stash@{number}
 4.1 应用贮藏,同时清除该stash
 $: git stash pop

5. 清理贮藏,默认第一个。加后缀为清理其它贮藏。number 为数值
$: git stash drop
$: git stash drop stash@{number}
 5.1 清理所有贮藏
 $: git stash clear

打标签 Tag

# 列出所有tag
$: git tag

# 新建一个tag在当前commit
$: git tag [tag]

# 新建一个tag在指定commit
$: git tag [tag] [commit]

# 删除本地tag
$: git tag -d [tag]

# 删除远程tag
$: git push origin :refs/tags/[tagName]

# 查看tag信息
$: git show [tag]

# 提交指定tag
$: git push [remote] [tag]
$: git push origin [tagname]
// 推送本地所有分支
$: git push origin --tags

# 提交所有tag
$: git push [remote] --tags

# 新建一个分支,指向某个tag
$: git checkout -b [branch] [tag]

# 检出标签
$: git checkout [tagname]

修改 Commit

// 1. 列出 commit 列表:
$: git rebase -i
 1.1 修改 commit 信息
 1.2 修改完后,重复执行如下命令直到完成
  $: git commit --amend --message="modify message by daodaotest" --author="name <email@gmail.com>"
  $: git rebase --continue
  1.3 中间也可跳过或退出 rebase 模式
  $: git rebase --skip
  $: git rebase --abort
// 列出最近的前两条
$: git rebase -i HEAD~2

2. 修改 commit 信息 具体操作
// 修改显示的内容,将 pick 修改为 reword 或者 简写 r [保留提交的分支记录,但是编辑提交的信息]
// 然后:wq保存退出后,会按顺序自动进入需要编辑的提交信息框

3. 查看分支信息: 最近5条
$: git log --oneline -5

4. 若是修改已经 push 的 commmit message, 则在推送push的时候需要加 --force,强制覆盖远程分支上的提交信息。
$: git push --force

git config 定制

git 也支持自定义指令:

比方说,你想添加一个别名,用于添加一个空的提交。 在这种情况下,你可以在配置文件(在 ~/.gitconfig )中添加以下内容:

[alias]
    empty = "git commit --allow-empty"

或者在终端:

$: git config --global alias.empty "git commit --allow-empty"

使用自定义指令:

$: git empty "Empty commit"

也可以在 Git 之外添加其他 shell 命令作为别名。例如,删除一个已经合并到远程的本地分支的别名:

[alias]
    delete-local-merged = "!git fetch && git branch --merged | egrep -v 'master' | xargs git branch -d"

感叹号 ! 告诉 Git 把它作为一个 shell 命令运行,而不是 git 命令。

对于别名,我们做一个 git fetch。然后我们得到合并后的分支,把它作为 egrep 命令的输入,过滤掉 master 分支,然后删除这些分支。

配置代理

配置 Git 全局代理:

访问 Github 走代理模式

# 全局配置 socket5
$: git config --global http.https://github.com.proxy socks5://127.0.0.1:7890
  • git config:Git 命令行工具的配置命令。
  • --global:表示该配置是全局性质的,会应用在你的所有代码仓库中。
  • http.https://github.com.proxy :作为配置项名称,它表示对应的是使用 HTTPS 协议访问 GitHub 并需要进行代理配置。
  • socks5://127.0.0.1:7891:代理服务器的地址和端口号。其中,socks5 表示使用 SOCKS5 协议进行代理,127.0.0.1 指的是代理服务器的 IP 地址或主机名,7891 则是端口号(Clashx 中 socks5 的默认端口为 7891)。可根据工作中的实际代理情况进行相应的修改,并保证对应的代理能够正常工作。

取消代理:

# 取消全局代理
$: git config --global --unset http.proxy
$: git config --global --unset https.proxy

查看 git 配置:

# 查看全局配置
$: git config --global --list
# 查看本地局部配置
$: git config --local --list

#  打开查看 Mac 中的 git config 文件
$: open ~/.gitconfig

查看 git log 提交信息

利用 git log 统计特定时间内提交的代码信息量。

# 依据用户名 username
$: git log --author="username" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }'

# 依据时间跨度
$: git log --since=2023-01-01 --until=2023-12-31 --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }'

# 输出结果: added lines: xxx, removed lines: xxx, total lines: xxx

俩者也可以结合。

开发流程

  1. develop 分支检出分支 feat/xxx : git checkout feat/xxx
  2. develop 分支检出预发环境测试分支 release/xxxx : git checkout release/xxxx
  3. 开发完成后将各个开发分支合并至 release 分支: 切换分支 git checkout release/xxx 、 合并分支 git merge feat/xxx
  4. 测试通过后,发起 merge request,待 code review 通过后,负责人 merge 代码,即: git checkout develop 、 合并分支 git merge release/xxx

上线流程

  • 当所有的研发分支都已经 mergerelease 后,使用 release 分支的代码进行测试,若测试通过, 则将 release 分支代码合并到 develop 分支上去;
  • 并在 develop 分支上构建打包,推送合并到 master 分支上,而后上线;
  • 当发布完成后,为了更方便地参考提交,可以在 developmaster 分支上加上标签(打上的标签是固定的,不能像分支那样可以移动位置) :
    1. 轻标签: git tag <tagName>
    2. 注解标签: git -am "注解文字说明" <tagName>
    3. 删除标签: git tag -d <tagName>

Bugfix 流程

  • 当上线产生 Bug 时,应当从 develop 分支中 git checkout hotfix/xxx 检出 hotfix/xxx 分支, 并完成修复,通过测试后,再 git merage hotfix/xxx 合并到 develop 分支上,即 develop -> hotfix/xxx -> develop ;
  • 而当开发新功能在 feat 分支中产生的 Bug ,应当直接在 feat/xxx 分支上修复,无需开辟新的分支。

持续集成: GitLab CI/CD

若有代码迭代问题,可以考虑是否 加入 GitLab CI/CD 做持续集成, 本文对此概念做简单介绍。

GitLab CI/CD 是一个内置在 GitLab 中的工具,用于通过持续方法进行软件开发 :

  • Continuous Integration (CI) 持续集成: 在开发分支上,当最终要合并到 master 主支之前,会通过编译和自动化测试对代码进行验证,确保代码的质量。可以理解为自动化测试,因此需要事先对功能创建自动化测试用例。
  • Continuous Delivery (CD) 持续交付: 交付即将代码发布出去的过程。而持续交付就是可以依据业务需求定时定点的将应用部署上线。
  • Continuous Deployment (CD) 持续部署:意为持续集成和持续部署的合并。当开发人员在 master 分支上合并一个提交时,该分支将被构建、测试,通过自动化测试后,则直接部署上线到生成环境中去。

Git 提交规范

参考 angular 团队的 git 提交规范。

Commit message 都包括三个部分:Header (必须),Body 和 Footer。

提交格式type(scope): subject , 例如: fix(Button): 修复按钮问题

- type
  - 用于说明 `commit` 的类别,只允许使用下面10个标识。
    - feat:新功能(feature)【会出现在 CHANGELOG 中】
    - fix:修补bug          【会出现在 CHANGELOG 中】
    - docs:文档修改(documentation)
    - style:代码格式修改,不影响代码含义的更改(空格、格式、缺少分号等)
    - refactor:代码重构(即不是新增功能,也不是修改bug的代码变动)
    - perf:性能优化
    - test:测试用例增加/修改
    - chore:对构建过程或辅助工具和库的更改,例如文档生成
    - revert:回退
- scope(可选)
  - 用于说明 `commit` 影响的范围,比如Button组件、store、首页、路由等等,视项目不同而不同。
- subject(可选))
  - 是 `commit` 目的的简短描述,不超过50个字符。
    - 以动词开头,使用第一人称现在时,比如 change,而不是 changed 或 changes
    - 第一个字母小写
    - 结尾不加句号(.)

Body

Body 为此次提交的详细描述,可多行显示。

  • 使用第一人称现在时,比如使用change而不是changedchanges
  • 应该说明代码变动的动机,以及与以前行为的对比。

Footer 仅在 不兼容变动关闭 issue 时 使用:

  • 不兼容变动

    BREAKING CHANGE: isolate scope bindings definition has changed.
     xxxxx
     Before: xxxx
     After: xxxx
    
  • 关闭 Issue

    Closes #996, #007
    

特殊情况 Revert

在版本回退中的格式为:

Headerrevert: feat(pencil): add 'graphiteWidth' option

BodyThis reverts commit (SHA 标识符).

利用 git-gzopen in new window 规范代码提交

  • 全局安装 commitizen,如此一来可以快速使用 czgit cz 命令进行启动。
$: npm install -g commitizen
  • 下载依赖 cz-git

    git cz 代替 git commit ,生成 Commit message。

$: npm install -g cz-git
  • 全局配置适配器类型
$: echo '{ "path": "cz-git" }' > ~/.czrc
$: npm install -g conventional-changelog-cli
# 自动生成 CHANGELOG.md 文件
$: conventional-changelog -i CHANGELOG.md -s

# 覆盖重写
$: conventional-changelog -i CHANGELOG.md -s -r 0

# 依据 angular 规范
$: conventional-changelog -p angular -i CHANGELOG.md -s

参考文档

Loading...