NingG +

Git 系列:典型操作

1. 关于版本控制说几句

版本控制,最简单的含义:写一个版本的内容,提交,再写一个版本,再提交,突然想重新查看某个版本,通过版本号直接查看即可。整体来说:

  1. 版本号:标识不同版本
  2. 版本库:存放多个版本的信息,能够新增版本、查看旧版本
  3. 工作空间:通常用于向版本库添加新版本、从版本库获取旧版本

版本库有两个作用:

  1. 版本控制
  2. 团队协作

2. Git版本控制原理

Git,就是进行版本控制的,因此,至少应包含2个结构:

  1. 版本库(空间):存放各个版本的信息;
  2. 工作空间:当前能够查看、编辑文件的位置,通常一个目录,目录下的所有文件/文件夹,都会被纳入版本库进行版本控制;

实际上,Git包含了3个空间:

  1. 版本库空间:HEAD指向最后一次提交的结果
  2. 暂存区(Stage/Index):为什么要有这个空间?
  3. 工作区(Working Dir):

由于Git是分布式的版本控制,因此,时间上是4个空间:

  1. 远端版本库
  2. 本地版本库
  3. 暂存区
  4. 工作区

为实现团队协作,Git进行分布式版本控制时,引入了“分支(Branch)”,即,不同的人从同一个分支上,创建各自独立的分支,在各个分支上进行版本控制,并在适当的时候,合并分支。官方的说法:一个分支进行一个特性的开发,不同的分支可以并行开发不同的特性,在分别调试之后,最后进行合并。

梳理一下逻辑,到目前为止,几个基本问题:

  1. 怎么创建一个版本库?git init
  2. 怎么标识一个本地版本库?
  3. 怎么标识一个本地分支?
  4. 怎么标识一个远端版本库?git remote add origin
  5. 怎么标识一个远端分支?

3. 实用操作

关于Git在本地版本库空间、暂存空间、开发空间之间,相互提交文件、回滚文件的操作命令,参考:Git work cmd,是一张插图。

3.1. Git 团队协作原理

主要围绕分支来进行,不同的功能,在不同的分支上进行开发,实现开发功能的隔离,避免相互干扰。整体上,分支有本地分支、远程分支,针对每个分支又有:创建分支、切换分支、删除分支、查询分支;在本地分支与远程分支之间,又有:以远程分支内容更新本地分支,将本地分支内容更新到远程分支上。梳理一下:

本地分支:

  1. 创建分支:
    1. git init,初始化新仓库
    2. git branch feature_x start-point,在start-point上创建新的分支,不切换到新分支上;
    3. git checkout -b feature_x start-point,创建feature_x分支,并且,自动切换到新分支
  2. 切换分支:
    1. git checkout branchname
  3. 删除分支:
    1. git branch -d branchname
    2. git branch -D branchname
  4. 查询分支:
    1. git branch -a
    2. git branch –list
  5. 合并分支:
    1. git merge anotherbranch,将「其他分支」中内容合并到「当前分支」

远端分支:

  1. 创建分支:
  2. 切换分支:
  3. 删除分支:
    1. git branch -r -d branchname
    2. git branch -d -r origin/todo origin/html,会删除远端分支上内容吗?
  4. 查询分支:
    1. git branch -a(显示所有分支)

本地分支与远端分支之间:

  1. 获取远端分支内容:git pull origin branchname
  2. 提交本地分支内容:git push origin branchname

3.2. Git 实践

3.2.1. 基本配置

常用配置信息:

# 配置用户信息:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
 
# 配置文本编辑器
$ git config --global core.editor vim
 
# 查看配置信息
$ git config --list
 
# 查看单个配置信息
$ git config user.name

如果用了 --global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者邮箱,只要去掉 --global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。

补充说明:

# .bash_profile 中,配置别名
# Alias for init git for Work
alias initGitForWork="source ~/initGitInfoLocalOfWork.sh"

# 具体 initGitInfoLocalOfWork.sh 的配置
git config --local user.name "郭宁"
git config --local user.email "guoning@china.com"

git config --local -l | grep user

3.2.2. 查看帮助文档

想了解 Git 的各式工具该怎么用,可以阅读它们的使用帮助,方法有三:

$ git help <verb>
$ git <verb> --help
$ man git-<verb>

3.2.3. 疑问:汇总

几点:

一个 branch 对应的upstream是什么?有什么作用?如何设置?如何删除?如何修改?

疑问:如何查看不同的提交版本?如何比对不同提交版本之间的差异?

4. Case:删除分支

删除 branch:

实际上有 3 个分支需要删除,具体参考:http://stackoverflow.com/a/23961231

删除分支:

5. Case:Git 切换到其他节点

5.1. 背景

远端Git服务宕机,切换其他Git节点替代

5.2. 具体操作

备用节点上,在仓库外,创建文件:local-git-daemon.sh,其内容如下(git daemon –help 查看命令说明):

git daemon --reuseaddr --verbose --base-path=./Project --export-all ./Project/.git

执行上述命令,启动Git守护进程: ./local-git-daemon.sh

执行命令:netstat -an -p tcp,查看 9481端口是否启动

其他节点,执行命令:git clone git://172.28.170.217/ 即可获得备用节点上的内容

5.3. 常见错误

常见错误:

repository not exported.

这跟配置有关,具体,帮助文档中,有说明:

It verifies that the directory has the magic file “git-daemon-export-ok”, and it will refuse to export any Git directory that hasn’t explicitly been marked for export this way (unless the –export-all parameter is specified).

因此,解决方案两个:

5.4. 参考来源

6. Case:添加远端分支,协作开发

6.1. 背景

小组协作开发新模块,一个队员folk出单个分支(下文记此分支地址为:folk-repo-url),所有队员都基于此分支进行开发。

6.2. 具体操作

本地开发分支中,利用 folk-repo-url 分支进行开发:

  1. 添加远端分支: git remote event
  2. 查看现有的远端分支:git remote -v
  3. 将远端分支拉取到本地:git fetch event
  4. 本地切换至/event/feature/magiccard工作区:git checkout -b feature/magiccard event/feature/magiccard
  5. 向远端提交自己的改动:
    1. git add
    2. git commit
    3. git push

【特别说明】:如果 git checkout 时,设置本地分支的名称与 feature/magiccard 不匹配,则,有些地方需要注意:

6.3. 参考来源

7. Case:回滚版本,放弃之前 commit 内容

改动的内容,没有提交到本地 HEAD 中,放弃当前的修改:

git log
git reset [hashcode]
git clean -dxf

改动的内容,已经提交到本地 HEAD 中,具体操作:

git log :查询 commit 的 hashcode
git reset --hard commit_hashcode
git push origin HEAD --force:强制提交

思考,上述方式,对其他人已有的分支,是否会产生影响?会产生影响,最好的方式 git revert 如果改动已经 commit 到远端仓库,则,使用 git revert 命令来回退版本:

git log
git revert [hashcode]
git revert -m 1 [hashcode]
上述改动,会增加一次 commit

补充说明:从远端拉取代码,然后 merge,如果希望 revert 回退,则:

# 查看 log
git log

# 回退:去掉 remote 的代码
git revert -m 1 [hashcode]

# 回退:去掉 local 的代码
git revert -m 2 [hashcode]

8. Case:合并多次commit

合并多次 commit,直接 rebase:

# hashcode,为要合并commit 之前的最近一次 commit,通常是一次 merge commit 的 hashcode
git rebase -i hashcode
# 如果出错, abort 即可
git rebase --abort

9. Case:如何编写 git commit msg

我当前 commit msg 的样式为:

Fix: (支付回调)提交收银台后,限制订单修改,Closes SHOW-422, SHOW-423

更多细节参考:

10. Case:修改 git commit msg

具体操作:

// 提交 commit
git commit -m "msg"

// 提交 commit 之后,仍可以修 msg
git commit --amend

// 查看 git msg
git log

11. Case:checkout 出指定 tag or commit 的代码

具体操作:

// 查看 log
git log

// 从指定的 tag, checkout 出新分支
git checkout -b new_branch_name tag_name

// 从指定的 commit, checkout 出新分支
git checkout -b new_branch_name commit_id

12. Case:从 git 仓库迁移出代码,并导入到另一个 git 仓库

具体操作:

# 1. 拷贝一份「裸版本库」,不包含工作区,完全的代码、分支、提交记录,无法提交新的 commit
git clone --bare git://github.com/username/project.git

# 2. 创建新的 git 仓库/项目
...

# 3. 迁移:进入老的代码目录,然后,以镜像方式,上传代码到新的 git 仓库/项目
cd project
git push --mirror git@gitcafe.com/username/newproject.git

# 4. 删除本地代码
rm -rf project

# 5. clone 新的 git 仓库中的代码
git clone git@gitcafe.com/username/newproject.git

更多细节,参考:

13. Case:删除已经加入 git 仓库的文件

情况描述

焦点

如何忽略已经加入 git 仓库的文件?

解决办法

git rm --cached abacus-check-report.iml

git commit -m "fix: 从原始版本中,删除 IDEA 的 iml 文件"

git push origin release/1.1.x 

补充信息:git rm 和 git rm –cached 区别:

git rm: 当我们需要删除暂存区或分支上的文件,同时工作区 ‘不需要’ 这个文件,可以使用 ‘git rm’

git rm --cached:当我们需要删除暂存区或分支上的文件,但是本地 ‘需要’ 这个文件,只是 ‘不希望加入版本控制’,可以使用 ‘git rm –cached’

更多细节,参考:git忽略已加入到版本库的文件

14. Case:当前目录下,设置 git 的用户名和邮箱

如果一台电脑下,不同目录,需要切换不同的 git 用户,则,需要针对具体的目录,执行对应的设置命令:

# .bash_profile 中,配置别名
# Alias for init git for Work
alias initGitForWork="source ~/initGitInfoLocalOfWork.sh"

# 具体 initGitInfoLocalOfWork.sh 的配置
git config --local user.name "郭宁"
git config --local user.email "guoning@china.com"

git config --local -l | grep user

15. Case:当前分支提交的 commit,转移到其他分支

修改尚未commit

把branch A上修改的代码,转移到branch B下:

# 在 A 分支下,操作
git stash

# 在 B 分支下,操作
git stash pop

修改已经 commit

把branch A上修改的代码(已经 commit),转移到branch B下:

# 在 A 分支下,操作
# 查看 commit 对应的 hashCode/commitID
git log


# 在 B 分支下,合并指定的 commit
git cherry-pick commitID

更多细节,参考: git分支,改动转移

16. Case:github 上,fork 了项目,如何跟项目保持同步更新

整体思路:

  1. 本地拉取:原始仓库 的代码
  2. 本地合并:本地将 原始仓库 的代码,合并到 fork 的本地代码
  3. 本地推送:本地将 合并后的代码,推送到 fork 的远端仓库

参考资料:

具体的操作命令:

# 1. 配置一个 remote 分支
# a. 查看 remote 分支
$ git remote -v
origin	git@github.com:ningg/checkstyle.git (fetch)
origin	git@github.com:ningg/checkstyle.git (push)

# b. 增加 remote 分支:「原始仓库」
$ git remote add upstream https://github.com/checkstyle/checkstyle.git


$ git remote -v
origin	git@github.com:ningg/checkstyle.git (fetch)
origin	git@github.com:ningg/checkstyle.git (push)
upstream	https://github.com/checkstyle/checkstyle.git (fetch)
upstream	https://github.com/checkstyle/checkstyle.git (push)


# 2. 本地代码合并
# a. 拉取「原始仓库」代码
$ git fetch upstream
remote: Enumerating objects: 4079, done.
remote: Counting objects: 100% (4079/4079), done.
remote: Compressing objects: 100% (26/26), done.
remote: Total 15053 (delta 4045), reused 4065 (delta 4045), pack-reused 10974
Receiving objects: 100% (15053/15053), 4.82 MiB | 98.00 KiB/s, done.
Resolving deltas: 100% (8330/8330), completed with 1281 local objects.
From https://github.com/checkstyle/checkstyle
 * [new branch]          master     -> upstream/master
 * [new tag]             checkstyle-8.12 -> checkstyle-8.12
 * [new tag]             checkstyle-8.13 -> checkstyle-8.13
 * [new tag]             checkstyle-8.14 -> checkstyle-8.14
 * [new tag]             checkstyle-8.15 -> checkstyle-8.15
 * [new tag]             checkstyle-8.16 -> checkstyle-8.16
 * [new tag]             checkstyle-8.17 -> checkstyle-8.17
 * [new tag]             checkstyle-8.18 -> checkstyle-8.18
 * [new tag]             checkstyle-8.19 -> checkstyle-8.19
 * [new tag]             checkstyle-8.20 -> checkstyle-8.20
 * [new tag]             checkstyle-8.21 -> checkstyle-8.21
 * [new tag]             checkstyle-8.22 -> checkstyle-8.22
 * [new tag]             checkstyle-8.23 -> checkstyle-8.23
 * [new tag]             checkstyle-8.24 -> checkstyle-8.24
 * [new tag]             checkstyle-8.25 -> checkstyle-8.25

 
# b. 合并:原始仓库的代码,合并到本地 fork 的代码分支中
$ git merge upstream/master

# 3. 推送到远端
$ git push origin master

15. 参考来源

原文地址:https://ningg.top/git-series-best-practices/
微信公众号 ningg, 联系我

同类文章:

微信搜索: 公众号 ningg, 联系我, 交个朋友.

Top