Git的使用方法与最佳实践 | 青训营

116 阅读16分钟

1. Git简介

git是目前最流行的版本控制系统,而版本控制系统(VCS:version control system),是一种记录若干个文件的内容变化,以便将来查阅特定版本修订情况的系统,多用于软件源代码的文本文件的管理。 随着项目规模的不断扩大,面对大量源代码的更新和维护,如何做到版本控制、管理和追踪就成为了当务之急。在没有版本控制系统之前,为了保存维护源代码的历史版本,通常采用多文件保存的形式,保存在本地或者云端等在线文档中,而版本控制系统的出现,例如CVS、VSS、SVN以及今天的主角——Git,它们具有以下优点:

  • 记录代码、文件的历史变化、方便追溯
  • 团队协同开发,共享数据,解决冲突
  • 代码备份、版本控制与发布

而Git是最初由Linus Torvalds(Linux之父)开发的一个分布式版本控制系统,用于Linux内核的协作开发与版本控制,现已成为开源社区中最广泛采用的版本控制系统。分布式与集中式最大的区别: 开发者可以本地提交,避免网络或远程服务不可用时无法提交的情况,每个开发者机器上都有一个服务数据库。

image.png

2. Git概念

Git的核心概念主要包括了本地工作区、暂存区、本地版本库和远程仓库,它们的关系如下图所示

  • 本地工作区(workspace):是指在计算机上实际编辑和修改代码的地方。它是包含项目文件和目录的根目录。在本地工作区中,你可以创建、修改和删除文件,进行实际的开发工作。

  • 暂存区(staging area):也称为索引(Index),是一个缓冲区,用于暂时存放需要提交到版本库中的修改。在使用Git管理文件时,我们通常通过将文件修改保存到暂存区,然后再将暂存区的内容提交到版本库中。

    暂存区的主要作用是分离工作区(workspace)和本地版本库(local repository)之间的更改管理。通过使用暂存区,可以更加灵活地控制哪些更改应该被纳入下一次提交,从而可以选择只将某些文件的更改添加到暂存区,而不是一次性提交所有更改。这样可以让你在提交之前仔细审查更改,确保只提交了所需的更改,并排除了不必要的修改。 总结,暂存区的出现,起到了以下作用:

    1. 方便协作开发和代码审查的过程。
    2. 分离工作区和版本库之间的更改管理。
    3. 控制哪些更改应该被纳入下一次提交。
    4. 构建清晰的提交历史。
  • 本地版本库(local repository):本地版本库是Git用来保存项目完整历史记录的地方。它包含了所有的提交(commits),每个提交都代表了一次代码的快照。本地版本库存储在你的计算机上的 .git 目录中。本地版本库允许开发人员通过分支、合并和撤销更改等功能有效地管理代码历史记录和协作开发。本地版本库还通过克隆和推送到远程存储库进行备份和协作。

  • 远程仓库(remote repository):远程仓库是指位于网络上的一个存储库,用于存储和管理代码库的中央副本。远程仓库提供了团队成员之间协作开发的平台,使得多人同时使用同一个代码库成为可能。而远程仓库的不断增多,催生了代码托管平台,如GitHub、GitLab、Gitee、Bitbucket等,它们提供了远程仓库的托管服务,并提供了一系列的协作和管理功能,如问题跟踪代码审查持续集成等。

    此外,远程仓库允许多个开发者在同一个代码库上进行协作开发。每个开发者可以在本地克隆远程仓库,获取代码并进行修改,然后将更改推送到远程仓库,使得其他人能够获取并审查代码。

  • 分支(branch):分支概念是指在项目开发过程中,将当前的开发工作复制一份,创建一个新的“分支”,在这个新的分支上进行开发工作,这样不会影响原本的主分支。创建仓库时,mastermain)是“默认的”分支。在其他分支上进行开发,完成后再将它们合并到主分支上。

保护分支:将选中的分支保护起来,防止未经允许的更改;实现分级管控;

  1. 防止意外更改:保护分支可以防止不经意间对关键分支进行的意外更改。这种保护可以防止开发人员在不经过审查或测试的情况下直接对关键分支进行更改,从而降低了引入错误或破坏性更改的风险。
  2. 代码审查:通过对保护分支的设置,要求所有的更改都需要经过代码审查才能合并到关键分支中。代码审查是一种重要的质量控制措施,可以确保代码质量、一致性和最佳实践的遵循。只有经过审查并获得批准的更改才能合并到保护分支中。
  3. 强制规范:保护分支还可以用于强制执行项目规范和最佳实践。例如,可以配置保护分支,要求代码通过静态代码分析工具的检查,或者要求提交信息符合特定的格式要求。这样可以确保团队成员在向保护分支提交更改时遵循一致的规范。
  4. 权限控制:通过保护分支,可以限制对关键分支的访问权限。只有具有特定权限的团队成员才能合并更改到保护分支,防止未经授权的人员对关键代码进行更改或破坏。
  5. 版本控制:保护分支也有助于管理代码库的版本控制。通常,保护分支被视为稳定的版本或可发布的版本,只有经过充分测试和验证的更改才能合并到保护分支中。这有助于确保发布的版本是经过验证的、高质量的版本。
  • HEAD:HEAD是当前分支上最新提交的指针; Git在新的提交之后将HEAD指向新的提交,以表示最新的状态。

它起到以下作用:

  1. 标识当前分支:当你在Git仓库中切换分支时,HEAD 会随之移动并指向当前所在的分支。它指示了当前工作树正在基于哪个分支进行操作。
  2. 指向当前提交:当你在分支上进行提交时,HEAD 会指向最新的提交,即当前所在分支的最新提交。这个指向的提交称为当前分支的"头部"。
  3. 作为检出点:你可以使用HEAD来进行检出操作,即将工作树回滚到特定的提交或分支。通过改变HEAD的指向,你可以切换到其他分支或回到过去的提交状态。
  4. 引用上一次提交:HEAD 也可以用来引用上一次的提交,即上一个提交的父提交。这在查看和比较提交历史时很有用。
  5. origin/HEAD -> origin/main 是一个远程跟踪引用,用于指示远程仓库中的默认分支。

3. Git基本操作

3.1 Help

git help
git help pull/clone

#键入后看一看效果
git help everyday

3.2 初始化本地仓库

#在当前目录新建一个版本仓库
git init

#新建⼀个目录,将其初始化为Git仓库,等价于[mkdir + cd + git init]
git init [project-name]

#克隆、下载一个仓库
git clone [url]
如:git clone https://github.com/jquery/jquery.git

3.3 把文件添加到暂存区

#添加指定⽂文件到暂存区,.或*代表当前⽬录的所有文件。
git add file1.txt
git add file2.txt file3.txt
git add *.txt 
#将当前目录和它的所有子目录都添加到暂存区
git add .
#将当前目录下的文件添加到暂存区
git add *
git add src/ #目录

3.4 提交至本地仓库

git commit -m 'add : 新增readme.txt'
git commit [file1] [file2] ... -m [message]

3.5 推送到远程仓库

git push <远程主机名> <本地分支名>:<远程分支名> 
省略远程分支则补推送到远程同名分支;无同名分支则重建;
省略本地分支则表示删除指定的远程分支(即推送一个空分支,等同于git push origin --delete master)
git push origin master
#-u,push的同时设置远程跟踪分支
git push -u origin master
#可以简写为:
git push

#推送所有分支到远程仓库
git push [remoteName] --all

#强行推送当前分支到远程仓库,即使有冲突
git push [remoteName] --force
或
git push [remoteName] -f

#推送所有分支到远程仓库,一般在仓库迁移时用的多
git push [remote] --all

3.6 拉取远程仓库的更新

#下载远程仓库的所有变动,但不会自动merge 
git fetch [remoteName]
可简写为:
git fetch

#取回远程仓库的变化,并与本地分支合并。相当于git fetch和git merge。
git pull [remoteName] [branch]
可简写为:
git pull

#在实际使用中,git fetch更安全一些。因为在merge前,
我们可以查看更新情况,然后再决定是否合并。
git pull origin next
等效于:
git fetch origin
git merge origin/next

3.7 查看信息

查看提交历史摘要---git log

#查看历史
git log  --pretty=oneline
#显示最近5次
git log -5
#查看文件的更改历史与明细
git log -p <file> 
#只查看某个用户的提交
git log --committer=chenxushao 

日志统计--- git shortlog

#查看用户提交量
git shortlog -sne

引用日志---git reflog

#引用日志是一个包含了 Git 引用变化历史的日志,其中包括分支、标签、HEAD 引用等。通过 git reflog 命令,你可以查看这个日志并了解引用的变化情况。
git reflog

查看历史详情--- git show

#显示某次提交的元数据和内容变化
git show [commit]
eg: 
git show 1c002d
显示某次提交发生变化的文件
git show --name-only [commit]

查看工作区状态--git status

git status
#紧凑式格式显示
git status --short
等价于git status -s

文本过滤--git grep

# 从当前目录的所有文件中查找文本内容
git grep 'Git'

差异对比--git diff

#1.查看工作区和暂存区之间差异。
git diff

#2.查看工作区与当前仓库版本HEAD版本差异。
git diff HEAD

#3.查看暂存区与当前仓库版本差异 
git diff --cached / git diff --staged

#4.不查看具体改动,只查看改动了哪些类
git diff --stat

#5.比较两个分支的差异。
git diff 分支a 分支b

3.8 .gitignore

Git 的文件系统被分为三类:

Tracked: 追踪的文件是指以前被暂存或提交的这类文件。

Untracked: 未跟踪的文件是指这种先前未被分阶段或提交的文件。

Ignored: 不应该被提交的文件,如缓存、日志、本地工程配置、本地编译代码等。

.gitignore 文件是一个用于指定 Git 忽略哪些文件和文件夹的配置文件。在版本控制中,有些文件或文件夹是不应该被包含在 Git 仓库中的,比如编译生成的文件、临时文件、敏感信息等。通过使用 .gitignore 文件,可以告诉 Git 忽略这些文件,从而保持仓库的干净和简洁。

.gitignore 文件的语法规则如下:

  1. 每行指定一个忽略规则。
  2. 使用斜杠(/)指定路径。比如 /build 表示根目录下的 build 文件夹。
  3. 使用#进行注释。#之后的内容会被忽略。
  4. 使用 ! 表示不忽略指定文件或文件夹。
  5. 使用 * 匹配零个或多个字符。
  6. 使用 ? 匹配一个字符。
  7. 使用 [abc] 匹配 abc 中的任意一个字符。
  8. 使用 ** 表示匹配任意路径,包括子文件夹。
# 忽略编译生成的文件
/build/

# 忽略临时文件
*.tmp

# 忽略敏感信息
config.ini

# 不忽略特定文件
!important.txt

# 忽略日志文件夹下的所有文件
/log/**

# 忽略所有扩展名为 .txt 的文件
*.txt

在使用 .gitignore 文件时,需要注意以下几点:

  • .gitignore 文件可以放在仓库的根目录下,也可以放在特定的子文件夹中。多个 .gitignore 文件会按照路径层级进行合并。
  • .gitignore 文件中的规则是逐行匹配的,先匹配的规则具有高优先级。
  • .gitignore 文件中的规则只适用于未被添加到 Git 版本控制中的文件。如果文件已经被添加到版本控制,那么修改 .gitignore 文件将不会影响已经被跟踪的文件。

4. Git协作机制

4.1工作流程

Git的分支机制是实现多人协同开发的核心,可以让团队成员在不影响彼此代码的前提下,独立地开发和实验新功能。Git的基本协作机制可以总结如下: 1. 获取远程代码:clone一个仓库,每个开发者需要先克隆一个远程仓库到本地;通过git pull命令获取远程仓库的最新代码。 2. 创建本地分支:开发者通过git branch命令创建一个本地分支进行开发。 3. 修改代码:开发者在本地分支上进行代码开发,提交代码。 4. 将本地分支推送到远程仓库:开发者通过git push命令将本地分支推送到远程仓库,供其他人查看和合并。 5. 合并代码:其他开发者可以通过git fetch命令获取远程仓库的代码,然后通过git merge命令将新代码合并到本地分支上。 6. 解决冲突:如果代码存在冲突,开发者需要手动解决冲突,并重新提交代码。 7. 发起Pull Request:开发者在完成某个功能后,可以发起一个Pull Request,让团队管理员进行代码审核。如果审核通过,管理员可以将分支合并到主分支上。 Git的协作机制和原理是基于分支和代码合并的,让开发者能够独立地开发和测试代码,最后通过代码合并将代码整合到一起,实现多人协同开发的目的。

4.2 版本冲突与恢复

Git冲突是指在合并不同分支的代码时,Git无法自动解决代码冲突,需要手动解决。通常情况下,Git会在代码中标记冲突的地方,帮助开发者进行解决。解决Git冲突的方法: 1. 查看冲突部分:可以使用Git命令行工具或Git客户端,查看冲突部分代码,了解冲突的原因。 2. 解决冲突:打开冲突文件,手动将冲突部分进行修改。修改冲突部分时,需要注意合并后的代码逻辑是否正确。 3. 提交解决方案:在解决冲突后,可以使用Git命令行工具或Git客户端,提交解决方案。在提交时,需要注明解决了哪些冲突。 4. 测试解决方案:在提交解决方案后,需要测试代码是否完全解决了冲突问题。可以使用Git的代码审查工具,如GitLab或GitHub,进行代码审查和测试。 除了上述解决方案,还有一些其他的方法可以帮助解决Git冲突,如合并代码之前,先进行代码的规范化、优化等操作,这样可以降低代码冲突的可能性。同时,团队成员需要定期进行通信,沟通代码开发进度和可能存在的代码冲突,在及时解决冲突的同时,也可以提高代码开发的效率。

5. 最佳实践

个人开发实践

  • 从github中 git clone 一个现有仓库
  • 在github上创建一个仓库后,要将这个仓库关联的本地仓库,和一个相同的gitee仓库进行关联,用于备份自己的项目。
  • 创建的gitee项目仓库不要带上模板,直接创建一个空白的项目来操作。
  • git add . / git commit -m 之后,先将本地的origin remote添加上gitee的ssh链接
  • git push 的时候,一般情况下,开发是本地的main 推到 远程仓库的指定分支 git push orgin main:dev
  • git commit -m 之后,要创建一个tag来标记,作为一个发布版本的里程碑,记录当前这个版本的特性

Git Flow分支管理

命名规范参考

如下为建议标准

  1. master、develop/hotfix、feature 四分支管理
  2. feature-date-desc 分支命名:分支类型-日期--描述
  3. tag-date-version tag命名:tag-日期-描述

提交规范参考

  1. feature或feat:新功能
  2. fix:修复bug
  3. docs:文档添加、修改,如README, CHANGELOG。
  4. style:格式(不影响代码运行的变动,如格式化,缩进等)
  5. refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  6. test:增加测试
  7. chore:构建过程或辅助工具的变动(如package.sh)
  8. deps:依赖变更(比如guava版本变更)
  9. revert:撤销以前的commit(必须写清楚)
  10. log:增加、调整log输出等
  11. perf:性能优化
  12. config:配置文件修改(如第三方接口url调整)
  13. remove:移除
  14. experience:体验优化
  15. ui:纯粹CSS样式变动,不影响功能代码
  16. other:其他原因,如上述不能覆盖,才用。如:合并代码,解决代码冲突等

最佳实践:

  1. 每个功能或修复一个Bug都应该建立一个新的分支,这样可以允许多个开发人员在同一代码库上工作,而不是在相同的代码上工作。

  2. 使用有意义的提交消息,避免使用不清楚或无意义的提交消息,这样可以使团队成员更容易了解提交的内容。

  3. 定期推送您的更改,避免长时间保留未推送的更改,这样可以避免任何人的工作丢失或混淆。

  4. 使用标签来跟踪发布版本号,在每次发布代码时,使用标记来跟踪版本号,这样可以让其他人知道该版本是正式发布的版本。

  5. 定期合并主分支,合并主分支后,可以使其他人的工作得到更新,并且可以及时修复问题。

  6. 使用.gitignore文件来过滤不必要的文件,避免将不必要的文件包含在代码库中,这样可以减少代码库的大小。

  7. 定期删除不需要的分支,删除不需要的分支可以减少代码库的大小,并保持代码库的整洁。

  8. 使用Pull Request进行代码审查,以确保代码符合规范和质量标准,并避免潜在的错误。

  9. 使用Git Flow或类似的工作流程,使整个团队能够进行高效的代码开发和发布。