商汇粹外网资源平台

搜索
查看: 1751|回复: 5

在实际开发项目中如何用好git?

[复制链接]

该用户从未签到

6

主题

20

帖子

69

积分

注册会员

Rank: 2

积分
69
发表于 2022-9-27 11:05:07 | 显示全部楼层 |阅读模式
本刚刚学了git的一些基本命令和用法,没有参与过实际的项目,现在想问下在实际开发中该如何利用好git这个工具。具体来说包括两个方面:
1. 在个人项目中,该如何写好commit信息?什么情况下需要签出分支?什么时候需要合并?什么时候要合并到master分支?
2. 在团队项目中,比如使用Gitlab时,该如何给成员分配权限?各成员应该把代码发布到哪个分支上?谁来负责代码合并?
希望相关有经验的人士分享一下自己的经验。
回复

使用道具 举报

该用户从未签到

5

主题

35

帖子

93

积分

注册会员

Rank: 2

积分
93
发表于 2022-9-27 11:18:27 | 显示全部楼层
简单来说,就这七点:

  • 使用 git rebase 让提交记录更加清晰可读
  • 使用  git reflog + git reset 跳到任意 commit
  • 使用 git cherry-pick 获取指定的 commit
  • 使用 git commit --amend 更改提交内容
  • 使用 git revert 回滚某次的提交
  • 使用 git stash 来暂存文件
  • 配置 git alias 提升工作效率
使用 git rebase 让提交记录更加清晰可读

rebase 基本用法

rebase 翻译为变基,它的作用和 merge 相似,用于把一个分支的修改合并到当前分支
如下图所示,经过 rebase 后提交历史的变化情况

不明白单分支的好处,可以在看看知乎的这个问题:Git commits历史是如何做到如此清爽的?
Vue 的作者尤雨溪就是说:多用 rebase
具体用法:

  • 基于 master 分支创建 feature 分支
  • 在 feature 分支上开发功能点
  • master 上也提交了commit
  • 在 feature 分支上执行 git rebase master,意为以 master 分支最后的提交作为基点,逐个应用 feature 的每个更改
git rebase VS git merge

合并分支有两种,即 rebase 、merge
merge 翻译为合并,即 git merge branchname,即合并分支代码,这种方法会保存每次 commit 的,当你使用 gitk 查看时就发现好几条颜色的线
另一种是 rebase,即去除一系列的提交记录,“复制”它们,然后在另一个地方逐个放下去
所以 rebase 的优势就明了了,它能创造更清晰的提交记录
但 merge 会保留你所有的 commit 的历史时间,当开发人员一多,历史记录就会变得混乱
rebase 的交互模式

在开发中,通常会在一个分支上产生很多无效的提交,这种情况下使用 rebase 的交互模式可以把多次 commit 压缩成一次提交,得到一个干净的提交历史
# 先看提交git log # f9f6f3b commit 3# 2feb45f commit 2# 07a3cb6 commit 1# 我们要修改 2 的话,rebase 到它的下一个 commit,这里是 1git rebase 07a3cb6 -i# 然后在打开的对话框里面修改,之后还要一个 rebase continuegit rebase -i <base-commit># 或者是 git rebase -i HEAD~2 对最近的两次 commit 进行合并
也有人称之为后悔药功能,即你无论写什么 commit,最后都可以修改,无论提交什么,都可以合并,DIY性强
使用  git reflog + git reset 跳到任意 commit

换个说法叫时光机,即通过查找所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作),通过 reset HEAD 跳到指定 commit
git reflog#afa2f45 HEAD@{10}: checkout: moving from 今天 to 明天#4abcda5 HEAD@{11}: commit: 打通1800处仙窍#de42069 HEAD@{12}: commit: 真言轮经大成git reset HEAD@{10}# 或者 git reset --hard afa2f45
如此一来,就回到了 afa2f45 commit 处,熟悉「时间法则」、「时光机」的人都知道,这是回到过去
使用 git cherry-pick 获取指定的 commit

意为“挑拣”提交,和 merge 合并一个分支的所有提交不同,它会获取某个分支的单个提交,并作为一个新的提交接入到当前分支上
这个需要故事背景才容易理解
张三在分支上开发功能,每个功能点提交一次commit,共六个提交六个功能点(分别是 feature1~feature6),再回到第一个提交点,即他使用 git reset --hard feature1 跳转第一个 commit,在此基础上开发一个新功能,即 feature7,那么如果把 feature7 合并到 feature6 上怎么做?
git reflog# git reflog 查看所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作)# 找到 feature7 的 commit 4c97ff3# 回到 feature6 的 commit cd52afcgit reset --hard cd52afc# 使用 cherry-pick 拿到 feature7 的代码git cherry-pick 4c97ff3
具体可看小蝌蚪的这篇 小蝌蚪传记:git时光穿梭机--女神的侧颜 来体会一二
简单来说,你的每一次 commit,就是一次记录,可以合并到任意地方。所以开发功能点或者修复bug之类,尽量做到一个功能点一个commit,方便出错时挑拣代码
使用 git commit --amend 更改提交内容

amend 的意思是修正
# 继续改动你的文件git add . git commit --amend --no-edit# 你这次的改动会被添加进最近一次的 commit 中
合并到上次的commit 中
git commit --amend:弹出让你修改内容
git commit --amend --no-edit:保持上一次的commit内容
PS:假如你的代码已经 push 了的话,要慎用,因为会修改提交历史。
使用 git revert 回滚某次的提交

上文提到一个回滚操作:git reset --hard xxx,能回到某次的 commit,除此之外,还有一种则是能撤销某次 commit
# 先找到你想撤销的那个 commit hash值git loggit revert <commit-id>
这种做法会新建一条commit 信息,来撤回之前的修改。
而 git reset 会直接提交记录退回到指定的 commit 上。
所以就个人开发或个人 feature 分支而言,可以使用 git reset 来回滚代码,但在多人协作的集成分支上,git revert 更适合。这样,提交的历史记录不会被抹去,可以安全地进行撤回
使用 git stash 来暂存文件

顾名思义,就是把本地的改动暂存起来
先了解下 git 的四大工作区域
四大工作区域



  • Workspace(工作区):本地电脑所见的文件和目录
  • Index/Stage(暂存区):一般存放在 .git 目录下,当你 git add 改动文件,改动的文件就放入在「暂存区」
  • Respository(本地仓库):当你 git clone 地址,就将远程仓库克隆到本地仓库。它是存在本地的版本库,其中HEAD指向最新放入仓库的版本。当你执行 git commit,文件改动就到本地仓库
  • Remote(远程仓库):类似Github、Gitlab、码云等放在代码托管平台
常见的场景是你还在开发一个功能点的时候,突然有个线上 bug 需要你紧急修复,这次你可以git commit 提交到本地仓库,后续通过 git commit --amend  继续在原 commit 上修改内容。但这里还有一种方法,即将代码存在暂存区,等 bug 修复完后,再从暂存区取出
基本命令如下:
git stash # 将本地的改动暂存git stash save "message" # 执行存储时,添加备注git stash pop # 应用最近一次暂存,并删除暂存记录git stash apply #恢复最近的存储,但不会把存储从存储列表中删除,某人使用第一个存储,即 stash@{0},如果要使用其他,git stash apply stash@{$num}git stash list # 查看 stash 了哪些存储git stash clear #删除所有缓存的 stashgit ls-files --stage #查看 index 暂存区
配置 git alias 提升工作效率

主要是为了简化命令,它的基本用法是 git config --global alias.<简化的字符> 原始命令
如下面的例子:
git config --global alias.co checkoutgit config --global alias.ci commitgit config --global alias.br branch
当然,另一种方法是在 .gitconfig 文件中设置
[alias]st = status -sbco = checkoutbr = branchmg = mergeci = commitds = diff --stageddt = difftoolmt = mergetoollast = log -1 HEADlatest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"ls = log --pretty=format:\"%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green)[%cn]\" --decorate --date=shorthist = log --pretty=format:\"%C(yellow)%h %C(red)%d %C(reset)%s %C(green)[%an] %C(blue)%ad\" --topo-order --graph --date=shorttype = cat-file -tdump = cat-file -plg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
参考政采云的配置
除此之外

还有一些不常见却好用的命令

  • gitk:打开git的图形化工具
  • gitjk:撤消您刚刚在git中所做的操作
  • git help -g:展示帮助信息
  • cat .git/HEAD:查看分支文件
  • git fetch --all && git reset --hard origin/master:回到远程仓库的状态

    • 抛弃本地所有的修改,回到远程仓库的状态

  • git push -f origin master:强行覆盖远程master
参考资料


  • git 时光穿梭机
  • 我在工作中是如何使用 git 的
  • 程序员必会的六条黄金 Git 命令,让你效率提高百分之百
  • Oh Shit, Git!?!
  • 我是如何使用 git 的?
回复

使用道具 举报

该用户从未签到

9

主题

32

帖子

110

积分

注册会员

Rank: 2

积分
110
发表于 2022-9-27 11:31:47 | 显示全部楼层
git的commit最好写清楚,在描述里写好。在新开发功能的时候或者是试验性代码。git团队合作的时候应该用git flow、github flow(gitlab 没有用过不好意思),具体使用
Git教程及使用经验
GitHub for windows使用教程(三)
回复

使用道具 举报

该用户从未签到

1

主题

13

帖子

69

积分

注册会员

Rank: 2

积分
69
发表于 2022-9-27 11:45:07 | 显示全部楼层
如果是刚刚开始学习使用git的初学者,可能直接面对的并不是commit message如何写,团队成员权限如何分配等。
我的建议是先从团队协作的角度思考,来选择一种适合团队的工作流程。
所谓工作流程就是团队成员之间对于不同分支的角色的理解的共识。

  • 比如功能分支和发布分支的关系,多个功能分支之间采取什么样的合并流程;
  • 功能分支与开发主分支之间的合并方法,合并时间点;
  • 团队成员之间协定同步代码的频率,一个分支应该涵盖的内容粒度等等;
在这个基础之上进行基本的基于分支的协作,在这之后才会面临到如何使用好Git,以及它相关的更加广泛的功能和生态链。
这里介绍一个git的基本工作流,很多更加复杂的工作流都是发展自这种“中心化工作流”。
初学者可以先从这种工作流入手,然后再结合社区内其他复杂的工作流延伸打造出适合自己团队和业务节奏的工作流。正如本文最后所说

  • 没有绝对普适的Git工作流
  • 工作流应该尽量简单,且能够提升团队产出
  • 业务需求应该能帮助你塑造适合的Git工作流
<hr>

中心化的工作流对于刚从SVN切换到GIT的团队来说是一种非常合适的工作流。就像SVN一样,中心化的工作流会使用中心仓库作为项目更改的唯一入口。当然不同于将中心分支命名为trunk,默认的开发分支在Git中被称为main,所有的修改会被提交到这个分支中。这一工作流除了main以外,就不再需要其他分支了。
虽然从一种版本管理系统切换到另外一种看上去是一件令人生畏的任务,但迁移到Git上并不需要对之前的工作流做什么改变。团队成员的协作方式仍然可以一如既往。
然而仍然需要说明,相比于SVN,在开发流程中使用Git会自动赋予开发团队一些优势。首先,Git能够让每个团队成员在本地保留一份完整的项目拷贝。由于这种隔离的开发环境,使得每个开发人员都可以独立于整个项目中所有其他变更进行自己的开发 ——也就是说,他们可以在自己的本地拷贝中提交新的变更,完全不需要理会上游的变更,直到开发人员认为自己的开发工作已经准备就绪。
其次,可以使用Git健壮的分支合并模型。与SVN不同,Git的分支模型设计为一种故障安全的机制,因此对于集成代码以及共享协作的流程来说,开发人员在很大程度上会减少心理负担。在利用远程服务端托管项目仓库,以便开发者进行pull和push操作的角度来说,中心化工作流与其他工作流没有什么太大差别。但与其他工作流相比,中心化工作流没有明确的pull request或者fork模式的定义。总体来说中心化工作流更适合与刚从SVN迁移到Git的团队或者是小型团队。
工作方式

开发者的工作始于从中心仓库将整个仓库克隆到本地。在本地项目中,进行编辑,然后提交变更,这一切都和使用SVN一致;只不过,这些提交到此为止都只是存储在本地——正如前面所述,这些操作都是与中心仓库隔离的。这样可以让开发者延迟与上游的同步操作,直到他们觉得需要进行同步了。
向中心仓库发布自己的变更时,开发者执行push命令将本地的main分支推向中心仓库。这一操作与svn commit相当,不同的是push会将所有远程仓库中没有的变更一次性推向远程。
初始化中心仓库


在一切发生之前,得有个人在远程服务器上创建中心仓库。如果是一个全新的项目,可以初始化一个空白的仓库。否则需要导入已经存在的Git或者SVN仓库。
中心仓库应该总是一个裸仓库(不包含工作目录的仓库),可以通过下面的命令进行创建:
ssh user@host git init --bare /path/to/repo.git
请使用有效的SSH username替换 user,用有效的IP或者域名替换host,并且将 /path/to/repo.git替换为你希望的远程服务器路径。注意路径末尾的.git扩展名通常会保留在最后以表明远程仓库是一个裸仓库。
托管的中心仓库

大多数情况下,我们已经不需要自己维护git server,而是通过第三方服务进行托管,让他们来为我们创建中心仓库,比如使用 gitlab 或者github。在这种情况下第三方服务会帮你处理初始化一个裸仓库的过程。创建之后托管服务商会提供一个中心仓库的地址,以便可以在本地使用中心仓库。
clone中心仓库

接下来,开发者在本地创建整个项目的拷贝。使用git clone命令来进行:
git clone ssh://user@host/path/to/repo.git
在对仓库执行clone操作的同时,Git会自动为远程仓库添加一个叫做origin别名,毕竟未来你还会不断与远程仓库进行交互。
编辑和提交变更

一旦完成本地拷贝的克隆,开发者即可使用标准Git提交流程对仓库作出一些变更:编辑,暂存,提交。也许你还不太了解暂存的概念,这是一种用来准备仅提交一部分变更的方式,通过这种方式不需要把本地所有变更都进行提交。这一能力允许开发者每次提交都保持高度的聚焦,即便此时的本地仓库已经包含一大堆的变更,也仍然可以仅进行一小部分代码的提交操作。
git status # View the state of the repogit add <some-file> # Stage a filegit commit # Commit a file</some-file>
请注意由于以上命令仅在本地创建提交记录,张三可以不断重复这些操作而不用担心会对中心仓库产生什么影响。当你面对一个大功能开发,需要对此大功能分解为几个小的步骤时,这一特性会显得非常有意义。
向中心仓库推送提交

一旦对本地仓库提交了新的变更,为了与其他开发者共享这些新的变更,我们需要把它们推向远程仓库。
git push origin main
这个命令会把本地的新提交推送到中心仓库。当向中心仓库推送变更时,会有可能由于其他开发者在之前已经推送了与本次推送的更新产生冲突的代码段落。这时Git会输出一些信息表明推送产生了冲突。在这种情况下,需要先执行git pull命令。我们将会在下面的段落中深入这里所提及的冲突的场景。
应对冲突

中心仓库代表着官方项目代码,所以中心仓库的提交历史应当神圣不可侵犯。如果开发者本地的提交历史与中心仓库的提交历史产生分歧,Git会拒绝将变更推送到中心仓库,因为会覆盖中心仓库的提交记录。

对于开发者来说正确的做法是,在推送变更之前,先从中心仓库更新远程的提交历史记录,然后将本地的变更rebase到远程提交记录的顶端。这就好比是说:让我的变更建立在所有其他人的工作的基础之上。操作的结果会显示为一条完美的线性提交历史。
如果本地变更与上游提交内容产生了冲突,Git会暂停rebase过程,让你手动解决这些冲突。与发起提交时需要使用的git status和git add一样,在解决冲突时也是使用相同的命令。这一统一的行为让新手也能够比较轻松的应对好合并过程中的冲突。另外,如果在合并过程中发生了大麻烦,Git也提供了简单的命令直接退出rebase过程,可以再重新来过,或者去找别人帮忙。
举例

让我们通过一个具体的例子来看看一个典型的小型团队如何使用中心化的工作流开展协作。在示例中有两名开发者,李雷和韩梅梅,他们俩独立开发各自的功能,然后通过中心仓库分享各自的工作成果。
李雷和他的功能


在他的本地仓库中,李雷按照标准Git提交流程开发自己的功能:编辑,暂存,提交。
记住这些操作都是在本地发生,李雷可以无数遍的操作而无需担心会影响到中心仓库。
韩梅梅和她的功能


与此同时,韩梅梅也在她自己的本地环境中开发自己的功能,同样通过:编辑,暂存,提交的流程。跟李雷一样,韩梅梅也不用担心自己的所作所为会对中心仓库造成什么影响。并且她也一点不关心李雷在做些什么,因为所有的本地仓库也都是各自独立的。
李雷发布他的功能


当李雷完成功能开发,他应当把本地提交发布到中心仓库,这样团队的其他成员就可以使用这个新功能。他可以像下面这样使用git push命令:
git push origin main
还记得origin是李雷当初clone中心仓库内容时,Git自动创建的用于指向中心仓库的别名吗?main参数告诉Git这次发布是将本地的main分支的变更推向中心仓库的main分支。由于此时此刻距离李雷首次clone中心仓库的内容这段时间内,中心仓库还没有任何的更新,所以这次push操作不会造成什么冲突,发布结果会如期而至。
韩梅梅尝试发布新功能


让我们来看看如果韩梅梅尝试在李雷推送完成之后,推送自己的新功能时会发生什么。首先,韩梅梅会使用相同的命令进行push操作:
git push origin main
但是由于她本地提交历史已经与中心仓库的提交历史产生了不一致,Git会拒绝这次推送,并输出一些错误信息
error: failed to push some refs to '/path/to/repo.git'hint: Updates were rejected because the tip of your current branch is behindhint: its remote counterpart. Merge the remote changes (e.g. 'git pull')hint: before pushing again.hint: See the 'Note about fast-forwards' in 'git push --help' for details.
这样韩梅梅就不能直接推送本地更新,也阻止了韩梅梅的提交历史覆盖中心仓库的提交历史。此时韩梅梅需要先拉取李雷的更新内容,然后将李雷的更新内容整合进自己的提交历史,再进行push操作。
韩梅梅rebase到李雷的提交历史顶部


韩梅梅可以使用git pull把上游的变更融合进本地仓库中的本地提交。
git pull --rebase origin main
--rebase选项告诉Git把所有韩梅梅的本地提交放到main分支的顶端,在与中心仓库进行同步之后,main分支的顶端也就是远程仓库的提交历史的顶部,如下图所示:

如果你没有添加--rebase选项,仅仅使用默认的pull命令也可以。但是这会在提交历史中留下一个多余的合并提交记录。在中心化的工作流中,最好是使用rebase方式而不是生成一个合并提交,这样会让提交历史看起来更加清爽。
韩梅梅解决合并冲突


rebase的工作流程是,将本地的commit转移到更新后的main分支顶部,对于本地的多个commit会多次重复执行这一过程。这意味着在rebase过程中需要一次一次地解决每一个commit相对于main分支的局部冲突,而不是像合并提交一样,一次解决一大堆commit整合在一起之后相对于main分支的整体冲突。这过程会保持每个提交更加专注,并且让项目的提交历史更清晰。反过来说,这也能够让我们更容易发现bug是在哪次提交引入的,如果必须要回退,可以回退特定commit把对于整个项目的影响降到最低。
如果韩梅梅和李雷分别开发不相关的功能,rebase过程中通常不会产生冲突。但是如果有冲突发生,Git会暂停在rebase当前commit的操作步骤过程中,并输出下面的信息,其中包含解决冲突所需要的相关信息:
CONFLICT (content): Merge conflict in <some-file>

此时韩梅梅执行git status命令来查看到底哪些文件产生了冲突。命令的输出内容会标注出哪些文件没有得到自动合并,并产生了冲突:
# Unmerged paths:# (use "git reset HEAD <some-file>..." to unstage)# (use "git add/rm <some-file>..." as appropriate to mark resolution)## both modified: <some-file>
然后根据上面的输出,韩梅梅按照自己的想法编辑产生冲突的文件。处理完成之后她就可以将文件暂存,然后执行git rebase --continue以便让rebase过程继续进行。
git add <some-file>git rebase --continue
这就是所有需要的操作了。Git会继续将新的commit转移到main分支的顶部,不断重复这一过程,直到某个其他commit产生冲突时再暂停rebase。
如果在rebase某个commit的过程中,冲突内容让你觉得手足无措了,也不要紧张。执行下面的命令会退出rebase过程,并回到整个rebase开始之前的状态
git rebase --abort
韩梅梅成功发布了新功能


到此为止韩梅梅已经完成了本地仓库与中心仓库的同步工作,可以将本地的新功能推送到中心仓库去了:
git push origin main
还有什么

中心化工作流对于小团队来说相当不错。但是当团队规模成长起来之后,上面示例中所使用的冲突解决流程将成为团队协作的瓶颈。如果你的团队觉得中心化工作流使用起来很舒适,同时又想获得更简洁的协作方式,那么应该尝试了解一下功能分支工作流。通过引入一个对应单独功能的独立分支,足以让独立功能在整合进主分支之前获得充分的检查和调整空间。
其他常见工作流

第一个介绍看上去过于老旧的中心化工作流,其实是因为其他所有工作流程都是基于这个最简单的工作流之上。大部分流行的Git工作流都会包含一个某种程度上中心化的仓库,以及个人开发者与其进行pull和push之类的交互。下面我们会对其他一些常见Git工作流做一些简单的介绍。这些扩展后的工作流在诸如多功能开发,hotfixes,以及发布版本管理等场景下的分支管理提供了特定的解决模式。
功能分支工作流

功能分支对于中心化工作流的扩展似乎是非常自然而合乎逻辑的。功能分支工作流背后的核心思想就是:所有功能的开发应该发生于一个专有的分支,而不是在主分支之内。这一封装可以让多个开发者同时协作开发某一个功能,而不会对主代码库产生任何影响。这也意味着main分支永远不会含有任何为开发完成的代码,这在持续集成环境中是一个不可或缺的优势。
Gitflow 工作流

Gitflow工作流围绕项目的发布流程定义了严格的分支模型。这一工作流没有在功能分支工作流之上引入任何新的概念或者命令。它只是定义了不同的分支如何承担不同的角色,以及何时何地在不同角色的分支之间进行交互。
Forking工作流

Forking工作流与本文中介绍的工作流在本质上存在不同。相对于中心化工作流中使用一个单独的服务端仓库作为中心的代码库,forking工作流允许让每一个开发者拥有一个属于自己的服务端仓库。这意味着对于每个贡献者来说,拥有的不仅仅是一个,而不是两个仓库:一个私有的本地仓库,一个公共的服务端仓库。
指导建议

没有一种Git工作流可以适合所有团队。如前所述,对于自己的团队来说,应该发展出一个适合自己团队提升产出的工作流程。除了团队文化的适应度以外,工作流还应该适应团队的业务文化。比如说Git的分支和tag管理应该适用于你的业务发布周期。如果你还在使用一些项目管理工具来追踪项目周期,你也许还希望定义与任务开发进程对应的分支。此外,下面也列了一些选择工作流时需要考虑的因素:
短期分支

一个分支存在的时间越长,也就意味着它与主分支隔离的时间越长,也进一步意味着在合并时产生冲突的概率越大。尽量让分支的生命周期短一些可以让合并过程更顺畅,合并结果更干净。
细粒度的回退

一个工作流规范的重要价值在于可以主动防范回退的发生。在进行分支合并之前就对分支进行测试是一个例子。然而通常情况下仍然会有其他原因导致的回退需要发生。这种情况下,一套好的工作流程应该能够让回退流程变得简单,而且尽量降低对其他团队成员的影响。
匹配发布周期

工作流应该适应你的业务发布周期。如果你的业务周期要求一天发布若干次,那么你需要一种工作流可以保证主分支永远是稳定的。如果你的业务发布周期没有那么频繁,你可能可以考虑使用Git的标签功能,来为一个待发布分支打上对应的版本号标签。
总结

在本文档中我们讨论了Git工作流。过程中我们深入检视了中心化工作流在实际应用中的示例。之后又扩展到基于中心化工作流的其他工作流模式。最后我们希望在本文中得出以下结论:

  • 没有绝对普适的Git工作流
  • 工作流应该尽量简单,且能够提升团队产出
  • 业务需求应该能帮助你塑造适合的Git工作流
回复

使用道具 举报

该用户从未签到

15

主题

156

帖子

486

积分

中级会员

Rank: 3Rank: 3

积分
486
发表于 2022-9-27 11:58:27 | 显示全部楼层
使用Git,要理解分支和标签的作用。“分支”是一个独立的开发任务,“标签”是标识一个特别的commit,一般用来标识一个版本。
最重要的原则就是使用一个独立的“分支”来做一个开发任务,把所有的工作都直接提交到master上是一个非常错误的习惯,这么做就相当于水果店把所有档次的苹果都放在一个框里,客户来买苹果,就要翻动这个框,给他拿出想要的苹果。工作任务重要度不同,紧急度不同,应该使用不同的分支分割他们,完整的做完一项工作,才允许这个分支合并到master,这样就能最大程度的保证master分支的正确性,到了预定的发布时间,就可以从master分支上发布一个可用的版本。对于那些没有来得及并入master,实际上也不是很紧急的功能,只需要让他们留在自己的分支上继续开发,不会对master分支的正确性造成影响。
可以遵循GitHub Flow,这是一个非常简单的工作流,网上介绍文档非常多,我这里结合实际的使用场景说一下过程和原则:
1、开发新功能:开发组长建立Issue #11,说明功能要求,把Issue分配给研发人员;研发人员接受到任务开始编码,基于master创建issue-11分支,在issue-11分支上提交代码;开发人员完成代码,提交Pull Request,从issue-11分支合并到master分支,注明Close #11;开发组长评审Pull Request,任务代码并入主分支,issue-11分支删除,Issue #11完成。
2、发布版本:开发组长从master分支创建发布分支release-1.2.0,在release-1.2.0修改代码,一般只允许做小幅度调整,比如修改构建脚本里的版本号。完成修改后把代码并回master分支,在master分支上做一个Tag,名称是v1.2.0,标识发布版本。发布人员从v1.2.0标签构建发布包,上传到发布平台,运维人员就可以从发布平台拿到1.2.0版本到系统上部署了。一个重要原则是无论什么时候都不要直接向master分支push代码,错误的push会危害master分支的稳定性,一定要经过验证的分支才能向master合并。另外使用Pull Request的方式,是一个非常好的评审过程,多一双眼睛看,就能避免很多错误。
3、修复一般缺陷:对于一般缺陷,可以在下一个版本修复。比如缺陷版本是1.2.0,可以在1.3版本修复这个缺陷。这个流程和开发新功能没有区别,只要把这个缺陷编写一个Issue #12,加入1.3的版本计划就可以了。对于版本编号可以去查看语义化版本规范。
4、修复紧急缺陷:紧急缺陷意味着线上系统正在出错,运维人员提出Issue #13,这个Issue是不能等下一个版本发布的,必须快速修复升级。比如缺陷版本是1.2.0,就要快速升级到1.2.1,紧急解决问题。编码人员基于v1.2.0标签建立分支hotfix-13,在hotfix-13分支上修复缺陷,经过验证,在hotfix-13分支上创建标签v1.2.1。发布人员从v1.2.1标签构建发布包,上传到发布平台,运维人员就可以更新部署了。最后修复人员不能忘了一定要提交一个Pull Request,把hotfix-13分支合并到master分支,这样以后发布的版本就就不存在缺陷了,Issue #13才能Close。1.2.1版本要在hotfix-13分支上分布,不能在master分支上发布,因为master分支可能并入了新的代码,这些代码没有经过测试,是不能在1.2.1发布的。
可以看到,分支的作用就是区分不同优先级的工作,开发团队可以同时做很多工作,可以按计划推进下一个版本,重要的功能可以提前插入到版本里来,没有完成的任务可以推迟到下次发布,如果线上系统有了紧急故障可以在独立的分支上修复,各种工作可以在独立的分支上互不干扰。Tag的作用就是标识一个特殊的Commit,一般是一个版本,这样线上的系统就可以回溯代码。
开发任务如果要做很长的时间,Pull Request的时候可能会出现大量的冲突。开发人员要经常从master代码向自己的任务分支merge代码(也可以用rebase,效果几乎一样),保持自己的任务分支有新的代码。尽量不要把冲突放到Pull Request的时候再解决,应该提前解决好,做一个友好的开发者。
回复

使用道具 举报

该用户从未签到

15

主题

156

帖子

486

积分

中级会员

Rank: 3Rank: 3

积分
486
发表于 2022-9-27 12:11:47 | 显示全部楼层
首先要知道git的基本操作

新建项目
1.git init
2.到gitHub网站中新建项目(推荐使用)
新建完成 git clone 把项目克隆到本地
工作区-暂存区-历史区
查看当前版本状态 git status
工作区到暂存区 git add ./文件名
暂存区到历史区 git commit -m '注释'
提交到远程仓库 git push origin master/分支名
工作区查看暂存区 git diff
暂存区查看历史区 git diff --cached
工作区查看历史区 git diff master 查看修改之前与现在的区别
快速从工作区提交到历史区 git commit -a -m '注释'
查看所有提交过的版本信息 git log
查看所有分支的所有操作记录 git reflog
还原版本 git reset --hard 版本号(通过查看版本去找)
查看当前目录所有子目录 ls
进到目录 cd
查看gitHubID是不是自己(远程仓库地址) git remote -v
不是的话 问组长 你是给我添加白名单还是我fork开分支
地址直接给你,你直接克隆 就是白名单
其次你要知道git的工作流程

第一种
1.先fork项目(去项目上点击fork按钮)
2.克隆项目到本地
3.修改代码并提交代码
4.到你fork的项目上点击new pullrequest
5.create pull request


第二种
1.先fork项目(去项目上点击fork按钮)
2.克隆项目到本地
3.开分支
创建分支 git branch 分支名
查看分支 git branch,带*号的是当前
切换分支        git checkout 分支名
快速创建分支并且切换 git checkout -b 分支名
合并分支        git merge 分支名
在合并代码的时候有可能会出现冲突,会进入MERGING状态
需要手动的去删除或者添加代码,然后再提交到历史区
(使MERGING状态变成分支状态,这个时候就合并成功)


删除分支 git branch -d 分支名
第一次使用create pullrequest
提交一次后,作者会保留合并信息(修改者多次提交,作者都能看到)
撤销
git checkout -- 文件名
git reset HEAD 文件名
git commit --amend -m "注释"
git reset --hard HEAD|版本号


如果说有白名单,在提交的时候代码冲突,那么 git pull
直接把远程仓库的代码和本地就进行了合并
(但是不一定有效,有时候会直接覆盖)
从远程获取最新到本地(不会自动merge,更安全) git fetch
从远程获取最新版本并merge到本地 git pull
查看差异 git diff origin/master
合并 git merge master origin/master
手动解决冲突,然后提交在版本区,在push
<hr>其实学习这种操作性强的东西最快的方法不是自己闷头背口令自己练,
而是找到跟你一起学习git的人,面对面的抱着电脑互相创建互相改。
多玩几遍不知不觉的就都会了,我当年也是如此哈哈哈
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表