admin管理员组

文章数量:1547960

本文使用一个windows下的git用户,两个Linux下的git用户来演示相关操作。

阳了,有些内容还是没有添加上,文章已经太长了,后面单独写文章吧。

本文围绕这张图展开:

  • 一、梗概
    • 1.1 git概述
    • 1.2 什么是版本控制
    • 1.3 Git相对于集中式版本控制系统的优势
    • 1.4 git和github
      • 1.41 GitLab
      • 1.42 Gitee(码云)
      • 1.43 其他
  • 二、git配置及git仓库概念
    • 2.1 安装和配置
      • 2.11 windows 下配置git
        • ▶ 安装和更新
        • ▶ 配置文件与凭据
      • 2.12 Linux
        • ▶ 安装和更新
        • ▶ 配置文件与凭据
    • 2.2 git 仓库与工作区
  • 三、Git 单分支
    • 3.1 从本地到远程
      • 3.11 创建本地git仓库:git init
      • 3.12 将文件添加到暂存区:git add
      • 3.13 提交:git commit
      • 3.14 关联本地仓库和远程仓库:git remote add
      • 3.15 推送:git push
    • 3.2 从远程到本地
      • 3.21 克隆:git clone
      • 3.22 拉取:git pull
  • 四、多分支
    • 4.1 查看分支的名称
    • 4.2 修改分支名称
    • 4.3 关联本地分支和远程分支(设置上游分支)
    • 4.4 新建与删除分支
    • 4.5 分支切换:git checkout
    • 4.6 拉取与合并
      • 4.61 拉取并合并git pull
      • 4.62 获取更改:git fetch
      • 4.63 合并分支:git merge
      • 4.64 冲突解决(概述)
    • 4.7 版本回退
  • 五、其他重要内容
    • 5.1 .gitignore
    • 5.2 标签:tag
  • 六、Github 重要操作
    • 6.1 问题追踪:issue
    • 6.2 拉取请求:pull requests
    • 6.3 讨论:Discussion
    • 6.4 Wiki
  • 七、命令速查

一、梗概

这一节全是文字描述,但你不应该跳过。

1.1 git概述

Git是一款优秀的分布式版本控制工具,是由Linux社区的开发者Linus Torvalds所开发的。Git已成为程序员们必备的工具之一,许多大型的软件项目都在使用Git进行版本管理。

Git的核心就是一个版本控制系统,可以实现多人在同一份代码的基础上进行开发,避免了因代码版本不同而导致的冲突问题。直接在README或代码的某个位置留下修改的时间和内容的日志,并提交到中央仓库,以便其他人可以通过下载相应的版本来获得你的工作成果。当多人修改代码时,可以使用Git来解决版本冲突,为了更好地保留项目的历史记录,以便日后的查看和追踪。并在需要时,可以返回到任何一个历史版本

Git的速度非常快,因为Git管理的是一组文件的快照。关于Git分支的一个优点是,如果你发现自己的项目中某一块出毛病了或者是需要某些新的特性,你可以同时有多个代码分支在开发不同的功能。

1.2 什么是版本控制

版本控制是一种记录和管理文件或代码变更历史的系统,它允许开发团队或个人开发者跟踪文件的每个修改、恢复到任意历史版本,以及协同合作开发。版本控制系统的主要目的是解决文件的并发编辑和多人协作过程中可能出现的冲突、错误以及代码管理等问题。

为什么需要版本控制呢?

  1. 历史追溯与恢复:版本控制系统可以记录文件或代码的完整修改历史,包括每次提交的详细信息、作者、时间等,因此可以方便地追溯到任意历史状态,并能够恢复到之前的任意版本。这对于错误修复、功能回退或者回滚到某个稳定版本都非常重要。
  2. 并发协作与团队协作:在多人协同开发的情况下,每个开发者都可以独立地在本地进行修改和测试,而版本控制系统可以很好地管理并合并这些修改,避免不同人之间的冲突。开发者可以轻松地在不同分支上并行开发,然后将它们合并到主干分支上。
  3. 错误修复与回退:当出现错误或者bug时,版本控制系统可以快速定位问题,查找导致问题的特定提交或修改,并进行修复。如果修复引入了新的问题,还可以方便地回退到之前的正确版本。
  4. 版本迭代与特性管理:版本控制系统允许开发者在不同的分支上同时进行不同特性的开发,而不会相互干扰。通过分支的合并和切换,可以轻松管理不同特性的开发进度和版本迭代。
  5. 备份与灾难恢复:版本控制系统可以作为重要文件和代码的备份工具,确保数据的安全性。在灾难性事件发生时,可以通过版本控制系统轻松地恢复到之前的状态。

版本控制是一种必不可少的工具,它提供了对文件或代码修改历史的追踪、管理和协作能力,提高了开发效率、减少了错误、便于团队协作,并为错误修复、版本迭代等提供了强大的支持。

1.3 Git相对于集中式版本控制系统的优势

Git相对于集中式版本控制系统(如SVN)有以下几个主要的优势:

  1. 分布式架构:Git是一款分布式版本控制系统,每个开发者都可以拥有完整的代码仓库副本。这意味着每个开发者都可以在本地进行完整的版本控制操作,而不依赖于中央服务器的可用性。这样的架构提供了更好的灵活性和可靠性,即使在没有网络连接的情况下,开发者仍然可以进行提交、分支切换等操作(推送这些肯定就不行了哈)。
  2. 强大的分支管理:Git的分支管理是其最大的特点之一。与集中式系统不同,Git鼓励开发者频繁地创建和合并分支,因此每个特性或修复都可以在独立的分支上进行开发和测试,而不会干扰主干代码。这样可以更好地支持并行开发、快速迭代和团队协作。
  3. 快速而高效:Git采用了先进的数据结构和算法,使得它在处理大型代码库和大量历史记录时表现出色。Git的提交历史是基于快照而非差异存储的,这样使得查找和恢复历史版本非常快速。此外,Git通过压缩和存储差异来减小存储空间的需求,使得仓库的体积相对较小。
  4. 本地操作:Git的本地操作特性是非常强大的。开发者可以在本地进行提交、分支切换、合并等操作,而无需与中央服务器频繁交互。这不仅提高了操作的速度,还减少了对网络的依赖性,使得开发者可以在离线状态下继续工作。
  5. 强大的分布式协作:由于每个开发者都有完整的仓库副本,Git使得分布式团队协作更加简单和灵活。开发者可以通过推送和拉取操作轻松共享和同步代码,合并分支的过程也相对简单。此外,Git提供了更丰富的协作工具和特性,如分支管理、代码审查等,有助于团队更好地协同工作。

Git相对于集中式版本控制系统具有分布式架构、强大的分支管理、快速高效、本地操作和强大的分布式协作等优势。这些特点使得Git成为当今软件开发领域最流行和广泛使用的版本控制系统之一。

1.4 git和github

Git和GitHub是两个相关但不同的概念和工具。

Git是一款分布式版本控制系统,它专注于管理和跟踪文件或代码的修改历史。Git通过记录每次提交、创建分支和合并等操作来追踪代码的演变,并提供了强大的分支管理和版本控制功能。

GitHub是一个基于Git的代码托管平台和社交开发平台。它提供了一个集中式的、云端的仓库托管服务,使开发者能够将自己的代码仓库存储在云端,并进行版本控制和协作开发。GitHub提供了一个图形化的界面,方便用户进行代码管理、版本控制、代码审查、问题跟踪和团队协作等操作。

Git和GitHub之间的关系可以理解为Git是版本控制系统的核心工具,而GitHub则是基于Git的代码托管和协作平台。

开发者可以使用Git在本地进行版本控制操作,如创建仓库、添加文件、提交更改等。

然后,可以使用GitHub将本地仓库与远程仓库进行同步,即将代码推送(Push)到GitHub上的仓库或从GitHub上的仓库拉取(Pull)最新的代码。GitHub还提供了许多额外的功能,如问题追踪、代码审查、团队协作和持续集成等,使得开发者可以更方便地进行协作和项目管理。

既然github是使用git的,使用最广泛的,代码托管平台,那肯定不止它一个,使用git的代码托管平台还有(常用):

1.41 GitLab

gitlab应该是大家都听过,但很多人都没有实际使用过的平台。

GitLab是一个基于Git的开源的代码托管和协作平台。它提供了强大的版本控制功能、代码管理工具和协作功能,可用于个人开发、团队协作和企业级项目管理。

以下是GitLab的主要特点和功能:

  1. 代码托管:GitLab提供了Git仓库托管服务,开发者可以轻松地创建、克隆、推送和拉取Git仓库。它支持分支管理、标签管理和合并请求等功能,使得团队能够方便地进行代码版本控制和协作开发。
  2. 持续集成与持续交付:GitLab集成了强大的持续集成和持续交付功能。它允许开发者配置自动化的构建、测试和部署流程,以确保代码质量和快速交付。通过与Docker等容器技术的集成,GitLab还能够实现高效的容器化部署。
  3. 项目管理:GitLab提供了项目管理工具,如问题跟踪、看板、里程碑和活动流。开发者可以创建任务、分配责任人、跟踪问题状态,并与代码托管和协作功能紧密集成,实现全面的项目管理。
  4. 代码审查:GitLab内置了代码审查功能,团队成员可以在提交合并请求时进行代码评审和讨论。这有助于提高代码质量、发现潜在问题,并促进团队间的知识共享和协作。
  5. 安全性和权限控制:GitLab重视代码和数据的安全性。它提供了访问控制、权限管理和用户身份验证等功能,以确保只有授权人员可以访问和修改代码。此外,GitLab还提供了强大的安全扫描和漏洞检测工具,帮助开发者及时发现和修复安全漏洞。
  6. 自托管和部署:除了GitLab提供的云端托管服务,GitLab还允许用户自行部署和管理GitLab实例。这意味着企业可以在自己的服务器或云平台上搭建和管理GitLab,实现更高的数据安全和自定义配置。

GitLab以其开源性质、丰富的功能和灵活的部署选项,在开发者社区和企业中受到广泛使用。它提供了一个全面的代码托管、协作和项目管理平台,为团队提供了高效、安全和可靠的软件开发和交付环境。

和github相比:

  1. 托管模式:GitLab提供了自托管选项,用户可以在自己的服务器上部署和管理GitLab实例,以获得更高的数据安全性和自定义性。而GitHub则是一个云端托管服务,所有的仓库和数据都存储在GitHub的服务器上。这意味着用户无需关心服务器的管理和维护,可以专注于代码开发和协作。
  2. 许可方式:GitLab是开源的,用户可以自由地访问、修改和分发GitLab的源代码。这使得用户可以根据自己的需求进行自定义配置和扩展功能。而GitHub则是闭源的,用户无法访问和修改其核心代码。
  3. 定价模式:GitLab提供了免费的开源版本和商业版本,商业版本提供了更多高级功能和支持服务。相比之下,GitHub在2019年改变了其定价策略,将一些高级功能(如私有仓库)从免费计划中移除,并提供了更多的高级功能和团队协作工具作为付费服务。
  4. 功能差异:GitLab和GitHub在功能上有一些细微差异。例如,GitLab内置了持续集成和持续交付功能,而GitHub则与其子公司Actions合作提供了类似的功能。此外,GitLab提供了强大的项目管理工具,如看板、里程碑和活动流,而GitHub则更专注于代码托管和协作功能。
  5. 社区和生态系统:由于GitHub的较早推出和广泛使用,它拥有更大的用户社区和更广泛的生态系统。这使得GitHub成为开源项目托管和协作的首选平台,并且许多开源项目和开发者社区都集中在GitHub上。

1.42 Gitee(码云)

俗称国内版github。是开源中国社区推出的基于Git的代码托管服务,目前已经成为国内知名的代码托管平台,致力于为国内开发者提供优质稳定的托管服务 。今年已经十周年了。

国内很多巨头的项目都托管在上面,比如OpenHarmony

很多国外开发者的项目并不会发布到gitee,有些国内开源项目也不会发布到github,所以,至少应当熟悉这2个平台。

和github相比:

  1. 用户定位:Gitee主要面向中国用户,提供了更多的针对中国用户的服务和功能。相比之下,GitHub是全球范围内使用最广泛的代码托管平台,具有更大的国际用户社区。
  2. 服务器位置和访问速度:Gitee在中国设有服务器,提供了国内加速服务,以确保用户能够更快地访问和下载代码。而GitHub的服务器位于全球各地,对国内用户可能存在访问速度上的差异(这个我反正没感觉)。
  3. 开源社区和生态系统:GitHub是全球最大的开源社区之一,聚集了大量的开源项目和开发者。许多知名的开源项目都托管在GitHub上,并且在开源社区中享有很高的声誉。相比之下,Gitee虽然也有一定规模的开源社区,但在国际开源社区中的影响力相对较小。
  4. 功能和工具:Gitee和GitHub在基本的代码托管、版本控制和协作功能上非常相似。然而,两者在一些高级功能和工具方面可能存在差异。例如,GitHub在持续集成、代码审查和团队协作方面提供了更多的内置工具和集成选项。而Gitee则提供了一些特定于中国用户的功能和服务,如国内部署选项、企业版服务和国内社交媒体集成等。
  5. 商业模式和定价:GitHub提供了免费和付费的服务计划,免费计划包含了许多基本功能,而付费计划则提供了更多高级功能和支持服务。Gitee也提供了免费和付费服务,但具体的功能和定价模式可能与GitHub有所不同。

1.43 其他

腾讯、华为、阿里等都有各自的代码托管平台,有的也支持git,有的则是自家的工具。

另外,csdn的gitcode也支持git,入口就在本页面的左上方。

二、git配置及git仓库概念

本文使用的代码托管平台是github

2.1 安装和配置

2.11 windows 下配置git

▶ 安装和更新

安装不讲了,环境变量什么的都老生常谈了。

不会的看我以前的文章:https://blog.csdn/weixin_43764974/article/details/122258533

推荐使用git bash,可以用一些Linux下的命令,你喜欢cmd、powershell也可以。

查看版本:git --version

更新一下:去官网下载windows版的,重新安装(不用卸载之前安装的,安装的时候会自动卸载,并且安装在原来的位置):https://git-scm/download/win

已经更新到最新版:

▶ 配置文件与凭据

windows下git的配置文件位于用户主目录下(注意打开“显示隐藏文件”):

这是一个文本文件,随便用编辑器打开就能编辑了,配置git即修改该文件的一些参数值,不过推荐使用命令行命令配置。

一下是一个基本的.gitconfig文件的内容:

[core]
	editor = \"E:\\VS Code\\bin\\code\" --wait
[filter "lfs"]
	smudge = git-lfs smudge -- %f
	process = git-lfs filter-process
	required = true
	clean = git-lfs clean -- %f
[user]
	name = xxxxx
	email = xxxxxx
[credential "https://gitee"]
	provider = generic
[http]
	sslVerify = true

各个配置项的含义:

  • [core]:可选项,一般不用手动配置,上面的配置中,指定了Visual Studio Code作为默认的文本编辑器,你在安装git的时候就会让你选择默认编辑器。这个选项主要指定需要编辑git相关文件时默认打开哪个编辑器,比如你双击.gitconfig,就会默认打开vs code来编辑(你右键选择其他编辑器也是一样的)。
  • [filter "lfs"]:可选项,这是Git LFS过滤器的配置部分,其中 “lfs” 是过滤器的名称,即Large File Storage,用于管理大型文件的存储和版本控制。上面的配置文件中:
    • smudge = git-lfs smudge – %f:这个配置定义了Git LFS的"smudge"操作。当从远程仓库拉取文件时,Git LFS会使用smudge命令对文件进行转换。具体地,git-lfs smudge – %f将应用于从远程仓库拉取的文件(%f 是文件的占位符)。这个命令会将存储在Git LFS中的文件还原为其原始形式,使其在本地工作区可用。
    • process = git-lfs filter-process:这个配置定义了Git LFS的"filter-process"操作。它在Git操作期间作为过滤器应用于文件。git-lfs filter-process命令会在Git操作期间处理文件,以确保它们符合Git LFS的要求。
    • required = true:这个配置指定Git LFS是必需的,即要求仓库中的大型文件必须使用Git LFS进行管理。
    • clean = git-lfs clean – %f:这个配置定义了Git LFS的"clean"操作。当执行Git提交操作时,Git LFS会使用clean命令对文件进行处理。具体地,git-lfs clean – %f将应用于要提交的文件(%f 是文件的占位符)。该命令会将大型文件替换为Git LFS所需的占位符,以便将其存储在远程Git LFS服务器上。
  • [user]:用户信息。自定义即可,这个用于标识你的身份,即你提交时的身份信息。(不是一定要设置为github账户的用户名和邮件)
    • name:用户名
    • email:邮件。
  • [credential xxx]:用于配置与特定主机相关的凭据提供程序。
    • [credential “https://gitee”]
      provider = generic
      ,在这个配置中,[credential “https://gitee”]指定了针对https://gitee主机(gitee)的凭据提供程序为"generic"。这意味着当Git需要在与https://gitee进行交互的操作(例如克隆、推送等)中进行身份验证时,它将使用"generic"凭据提供程序来获取必要的凭据。
  • [http]:[http]部分的sslVerify选项用于控制对HTTPS连接的SSL证书验证。

上面的.gitconfig文件中的配置是全局配置,如果项目没有进行特殊配置,则默认使用上面的配置。单独某个项目的配置,后面讲。

上面配置中的凭据含义:

“凭据”(Credentials)是用于进行身份验证和访问受保护资源的信息,通常包括用户名和密码、访问令牌或SSH密钥等。与远程仓库进行交互时,例如克隆、推送或拉取操作,Git可能需要验证身份以获得访问权限。

凭据用来证明自己是合法用户,并获得对特定资源的访问权限。这样,Git可以识别用户的身份,并根据用户的权限级别来控制对仓库和其他资源的操作。


常见的凭据类型包括:

  1. 用户名和密码:这是最常见的凭据类型,用于验证用户身份。用户名和密码将用于与远程仓库进行身份验证。这里的用户名和密码是你代码托管平台账户的用户名和密码
  2. 访问令牌:访问令牌是一种特殊的凭据,通常是生成的字符串。可以使用访问令牌来代替密码进行身份验证,以增加安全性。访问令牌具有特定的权限范围,可以在需要访问受保护资源时提供给Git。
  3. SSH密钥:SSH密钥是一对加密密钥,包括私钥和公钥。私钥存储在你的计算机上,而公钥则存储在远程仓库上。Git使用SSH密钥来进行身份验证和安全通信,而无需每次交互时输入密码。

    建议使用令牌或者SSH秘钥。

在命令行设置git用户名和邮箱:

git config --global user.name "your name"
git config --global user.email "email@xx"

设置SSH秘钥作为凭据:

(1)生成ssh密钥对

使用更安全的 ED25519 密钥, 在git bash中输入:

ssh-keygen -t ed25519 -C "your_email@example"

不要使用老版本的rsa加密算法了,否则push、clone可能会出现permission denied 的错误。

你可以其github的帮助页面(help)看更多信息:https://docs.github/zh/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

生成的秘钥对在用户主目录的.ssh目录下面:

.pub是公钥,另一个是私钥。私钥自己保存,公钥部署到github。

ssh公私钥不仅仅可以用于git,其他使用ssh协议的地方也可以使用,比如ssh远程登录。

(2)部署公钥到github

在github点击自己的头像,settings, 选择SSH and GPG keys,添加新的,然后吧公钥内容复制进去就行了。

gitee也是一样的,都是中文,不用讲了。

验证OK:

GPG(GNU Privacy Guard)是一个免费、开源的加密软件套件,用于进行数据加密、签名和认证。它是对OpenPGP标准的实现,OpenPGP是一种公钥加密和电子签名协议。github和gitee都支持的。

2.12 Linux

▶ 安装和更新

一般都默认安装的,没有的拿包管理器安装一下就行了。

apt install git

查看已安装的版本:

root@CQUPTLEI:~# git --version
git version 2.25.1

更新一下:

# 添加PPA源
add-apt-repository ppa:git-core/ppa
# 更新软件版本信息
apt update
# 升级git
apt upgrade git

▶ 配置文件与凭据

Linux下的git配置文件同样在用户主目录下。普通用户就是对应的home目录,root用户就是root目录下。下面的示例中,我是用root用户身份操作的。

如果没有使用过git,是默认没有配置文件的。可以使用:

git config --global user.name "your name"
git config --global user.email "email@xx"

配置用户名和邮箱,这样会自动创建.gitconfig文件;也可以手动创建。

这里就配置一个用户名和邮箱吧。需要其他配置的可以参考上面windows的配置文件进行设置。

同样使用SSH秘钥来进行身份验证。

生成密钥对已经密钥对的存储位置和windows是一样的,把公钥复制到github即可。如图,我添加了3台设备的公钥:

2.2 git 仓库与工作区

先看一些简单概念,具体的要和命令结合起来介绍。

仓库(Repository)是用于存储和管理项目代码的地方。仓库包含了项目的所有版本历史、分支、标签以及与远程仓库的连接。它是Git的核心概念之一,提供了版本控制和协作开发的功能。

Git仓库可以分为两种类型:本地仓库和远程仓库。

  • 本地仓库(Local Repository):本地仓库是存储在本地计算机上的Git仓库副本。它是开发者在开发过程中与之交互的主要仓库。通过在本地仓库中保存所有版本的代码,可以随时回溯到以前的提交并进行修改。可以在本地仓库中执行各种Git操作,例如提交代码、创建分支、合并分支等。本地仓库通常位于项目根目录下的.git文件夹中。
  • 远程仓库(Remote Repository):远程仓库是位于远程服务器上的Git仓库。它充当了代码的中央存储库,用于团队成员之间的协作和代码共享。远程仓库可以托管在各种Git托管平台(如GitHub、GitLab、Bitbucket等)或自己搭建的服务器上。可以将本地仓库中的代码推送(push)到远程仓库,或者从远程仓库拉取(pull)最新的代码到本地仓库。

使用Git开发时,本地工作区由3部分组成:

可能有的叫法不一样,比如暂存区有的人也叫index或者HEAD,理解即可。

  1. 工作目录Working Directory):工作目录是在本地计算机上存储项目文件的目录。它是进行实际代码编辑和开发的地方。工作目录中包含项目的源代码、配置文件、图像文件等。可以在工作目录中创建、修改和删除文件,以进行项目开发。
  2. 暂存区Staging Area):暂存区是位于本地仓库中的一个缓冲区域。它用于暂时存放将要提交的文件修改。在工作目录中对文件进行修改后,可以使用git add命令将修改的文件添加到暂存区。通过暂存区,可以选择性地准备要提交的文件。(注意,文件本身还是存储在工作目录的,暂存区只是存储文件快照,后面会结合命令仔细讲解)
  3. 本地仓库Local Repository):本地仓库是存储在本地计算机上的Git仓库,它包含了项目的完整版本历史和元数据。本地仓库通常是一个隐藏的.git目录,位于项目的根目录下。它存储了Git的版本历史、分支、标签等信息。您可以在本地仓库中查看提交历史、创建和切换分支、合并分支等操作。(同样的,它也不存储实际的项目文件)

三、Git 单分支

只有一个分支时的简单操作。

本小节,我会在github新建一个仓库,并将Windows下的一个本地仓库与之关联,Windows端本地仓库只有一个分支,名称是master,github的远程仓库也只有一个分支,默认名称是main。远程仓库在Windows本地仓库的别名我自定义为 Remote

Windows端推送完成后,在一台Linux上克隆这个仓库。克隆后的仓库别名默认为origin

3.1 从本地到远程

本小节,我将在Linux上创建git本地仓库,并提交到远程。windows下的命令都是一样的。

3.11 创建本地git仓库:git init

这是还没有创建本地git仓库时的工作目录:LinuxC

使用命令:git init 创建本地git仓库。

会多出一个.git目录:

它的内容如下:

来详细看看每一项都是什么东东:

  1. branches目录:此目录保存了每个分支的具体信息,每个分支都对应一个文件,文件名就是分支的名称。这些文件记录了分支的最新提交(commit)的哈希值。
  2. config文件:此文件包含了Git仓库的配置信息,例如远程仓库的URL、分支的跟踪配置、用户信息等。通过修改此文件,可以自定义Git的行为。
  3. description文件:此文件包含了对仓库的描述信息。通常情况下,该文件内容为空,但在一些Git服务商(如GitLab)中,可以使用此文件为仓库添加描述。
  4. HEAD文件:此文件指示当前活动的分支。它包含一个引用,指向当前所在分支或者直接指向某个具体的提交(commit)。
  5. hooks目录:此目录包含了Git的钩子脚本。钩子是在特定Git事件发生时执行的脚本,可以用于自定义和控制Git的行为。
  6. info目录:此目录包含了一些包含Git仓库信息的文件,例如exclude文件,用于指定应该被忽略的文件和目录。
  7. objects目录:此目录保存了Git仓库中的所有对象,包括提交(commit)、树(tree)和文件的内容。Git使用哈希算法来对这些对象进行索引和存储。
  8. refs目录:此目录保存了Git仓库中的引用(references),包括分支、标签和远程跟踪分支的信息。这些引用文件存储了指向具体提交(commit)的哈希值。

OK,这就是 git init后创建的文件。再返回来看看git init:

他还给出了这些提示信息:

  1. git init使用master作为本地初始分支的名称,这是这个默认分支的默认名称;
  2. master这个默认名称并不是固定不变的;
  3. 通过运行git config --global init.defaultBranch <name>,你可以将替换为你想要的默认分支名称,当然了,不配置的话,就是默认的master
  4. 建议一些常用于替代’master’的分支名称,包括’main’、'trunk’和’development’等。
  5. 通过运行git branch -m <name>,你可以将替换为你想要的新分支名称,从而更改当前分支的名称。

执行git init后,Git会开始跟踪当前目录下的文件变化。你可以使用git add命令将文件添加到暂存区,然后使用git commit命令将文件提交到版本历史记录中。

继续来看下一个命令:git add

3.12 将文件添加到暂存区:git add

git add用于将文件或目录添加到Git的暂存区(staging area)。它是将文件纳入版本控制的第一步。

常用的命令格式有:

(注意命令中的<>表示这是个参数,并不是要把参数放在<>里面,不要误会了)

① 添加单个文件:

git add <文件名>

② 添加多个文件:使用空格分隔多个文件名

git add <文件名1> <文件名2> ...

添加所有文件:使用点号(.)来表示当前目录下的所有文件,并将它们添加到暂存区:

git add .

注意:点号后面的空格是必需的。

④ 添加目录:

git add <目录名>

这将递归地将目录下的所有文件和子目录都添加到暂存区。

⑤ 添加文件内容的部分更改:如果只想将文件的部分更改添加到暂存区,可以使用交互式模式(git add -p)或分段添加模式(git add --patch)。这样可以逐个检查文件的更改,并选择性地将更改添加到暂存区。(会一个一个地问你是否要将修改过的文件添加到暂存区,,更精细)

git add -p

⑥ 添加文件模式的更改:除了文件内容的更改,Git还跟踪文件的模式更改(例如,添加新文件、删除文件等)。可以使用以下命令将文件模式更改添加到暂存区:

git add -A

这会添加所有文件内容和模式的更改。

如果文件没有发生修改,执行git add命令时,Git会忽略该文件,并保持其未跟踪状态,不会将其添加到暂存区。只有修改过的文件才需要通过git add命令将其添加到暂存区,以便将这些更改提交到版本控制。

Git使用文件的状态来判断是否需要将文件添加到暂存区。当你执行git status命令时,会显示文件的状态信息,主要包括以下几种状态:

  1. 未跟踪(Untracked):表示文件尚未被Git跟踪。这些文件不会被自动添加到暂存区,需要使用git add命令将其添加进去。
  2. 已修改(Modified):表示文件已经被修改过,但尚未添加到暂存区。只有执行了git add命令后,修改过的文件才会被添加到暂存区。
  3. 已暂存(Staged):表示文件已经被添加到暂存区,等待下一次提交。这些文件的更改会在执行git commit命令时被纳入版本历史。

如图所示,还没有提交过:

添加到暂存区后:

前面讲了.git目录,我们再来看看git add后,该目录下文件的变化:

  • index: 这是 Git 的索引文件,也称为暂存区。当你执行 git add 命令后,该文件会记录被添加到暂存区的文件信息和状态的更改。(里面的内容是二进制,打开应该是乱码)
  • HEAD: 这个文件指向当前所在的分支(或其他提交对象),它会随着提交的变化而更新。
  • objects/: 这个目录是 Git 用来存储对象(例如提交对象、树对象和文件对象)的地方。当你执行 git add 命令后,新的文件对象会被创建,并存储在这个目录中。

当然了,这些东西,一般你无需关心,这里只是展示一下。

取消已经执行的 git add 操作(通常不用,但是看一下):

git reset

执行 git reset 命令后,暂存区中的所有更改将被移除,但工作目录中的文件更改将保持不变。这意味着之前通过 git add 添加到暂存区的文件将回到未暂存的状态。

如果你只想取消某个特定文件的 git add 操作,可以指定该文件的路径:

git reset <文件名>

请注意,执行 git reset 命令后,之前添加到暂存区的更改将不会被保留,你需要重新添加文件到暂存区以进行提交。

另外,如果你已经执行了 git commit 提交操作,并且想要撤销最近的提交并取消暂存区的更改,可以使用 git reset 命令的 --soft 选项:

git reset --soft HEAD^

该命令会将分支的指针移动到上一个提交,同时保留暂存区的更改。这样你可以进行新的提交,或者对之前的更改进行修改后再次提交。

注意,在使用 git reset 命令时要小心,特别是在与远程仓库共享代码的情况下。如果已经将提交推送到远程仓库,撤销提交后可能需要进行协调和沟通以避免代码冲突或数据丢失。

3.13 提交:git commit

git commit 用于将更改保存到代码仓库中(local repository)。当你在 Git 项目中做出修改后,你需要使用 commit 命令将这些修改提交到代码仓库,以便它们成为项目历史的一部分。

git commit 包含了以下几个关键要素:

  1. 代码快照(Snapshot):每次提交都会创建一个代码库的快照。这个快照包含了你在提交时项目中所有文件的当前状态。因此,每次提交都会记录整个项目的状态,而不仅仅是更改的文件或行。

  2. 提交对象(Commit Object):每个提交都被视为一个独立的对象,包含了与提交相关的元数据和指向前一个提交的指针。这些提交对象形成了一个有向无环图(DAG),它展示了项目历史的演化。

  3. 唯一标识符(SHA-1 Hash):每个提交都有一个唯一的标识符,称为 SHA-1 哈希值。该哈希值由提交对象的内容计算而来,因此每次提交都有不同的哈希值。这个哈希值可以用于引用提交,例如回滚到特定的提交或者在分支之间进行合并。

  4. 作者和提交者信息:每个提交都包含了作者和提交者的信息,包括姓名和电子邮件地址。这些信息可以帮助你追踪代码的贡献者。

  5. 提交消息(Commit Message:每个提交都应该包含一个相关的提交消息,用于描述提交所做的更改。提交消息是一个文本块,可以提供有关提交的详细信息,例如为什么进行了更改、更改的目的等。良好的提交消息可以帮助项目成员理解和回顾更改的原因。

Git commit 命令的基本格式如下:

git commit -m "Commit message"

其中,-m 参数用于指定提交消息。你可以在双引号内提供有关提交的简短描述。请注意,提交消息应该清晰、简洁,并且能够准确描述你所做的更改。

除了 -m 参数外,git commit 命令还支持其他选项和参数,可以根据需要进行使用。以下是一些常用选项:

-a:自动将所有已修改或已删除的文件添加到暂存区后执行提交。这个选项可以简化提交流程,但不会包括新添加的文件。

-am:组合使用 -a 和 -m 选项,可以直接在命令中指定提交消息,并自动添加已修改或已删除的文件。

-v:在提交消息前显示更改的差异信息,用于检查即将提交的内容。

-p:逐个提示你确认是否要将每个已修改的文件添加到提交中。

3.14 关联本地仓库和远程仓库:git remote add

要将本地仓库的内容推送到远程仓库,还需要将本地仓库与远程仓库关联起来。

(1)创建远程仓库

在远程代码托管平台(如GitHub、GitLab或Bitbucket)上创建一个新的空仓库(不是空的也行,留在后面的分支部分说,这里先选public)。获取远程仓库的URL,它通常以 https:// 或 git:// 开头(比如我使用ssh)。

(2)在本地仓库中添加远程仓库

打开终端或命令提示符,导航到你的本地仓库所在的目录。运行以下命令将远程仓库添加到本地仓库:

git remote add origin <远程仓库的URL>

这里的 origin 是一个远程仓库的别名,你可以根据需要自定义。该命令将远程仓库的URL与别名关联起来。

(3)验证远程仓库的关联

运行以下命令验证是否成功将远程仓库关联到本地仓库:

git remote -v

这会显示与本地仓库关联的远程仓库列表和对应的URL。确认远程仓库正确关联后,你就可以继续进行其他 Git 操作,例如推送代码到远程仓库或拉取远程仓库的更新。

3.15 推送:git push

git push 的基本语法为:

git push <远程仓库名称> <本地分支名称>:<远程分支名称>

其中,<远程仓库名称> 是远程仓库的别名,可以使用 git remote -v 命令查看已经关联的远程仓库名称。<本地分支名称> 是你想要推送的本地分支的名称,<远程分支名称> 是你想要将本地分支推送到的远程分支的名称。

记住上面这个默认语法。

但是每次写这么一长串,有点麻烦,可以在第一次提交的时候建立追踪关联将本地的一个分支和远程的某个分支关联起来,在之后的推送中直接使用:git push就能把本地分支推送到远程相应的分支。

可以使用以下语法为本地和远程分支建立追踪关联:

git push -u <远程仓库名称> <本地分支名称>:<远程分支名称>

-u 参数会将本地分支设置为远程分支的上游(upstream)分支,这样你之后可以使用 git push 命令推送更改,而不需要指定远程和分支名称。

你可以为每一个分支都建立这样的对应关系,在本地切换到相应的分支时,使用git push 就会推送到对应的远程分支上。(后面再多分支那里还要讲)

上游分支(upstream branch)是指在当前分支的版本控制历史中,与当前分支存在直接关系的、位于更高级别的分支。通常情况下,上游分支是指当前分支的源分支或追踪的远程分支。

在一个分支关系图中,每个分支都可以有一个或多个直接上游分支。上游分支可以是当前分支的父分支,也可以是当前分支追踪的远程分支。上游分支对于协同开发和分支管理非常重要,它们用于合并、拉取最新的更改以及在多个分支之间同步代码。

举个例子,假设你在本地创建了一个特性分支(feature branch)feature,而这个特性分支是基于远程仓库的 develop分支创建的。在这种情况下,develop 分支就是 feature 分支的上游分支。

在进行版本控制操作时,可以使用上游分支来进行合并、拉取最新更改或者与其他分支同步代码。

除了基本的语法,git push 还支持其他一些选项和参数:

-f:强制推送,即使远程仓库中有更新,也会覆盖远程分支的内容。

--tags:推送本地仓库中的标签(tags)到远程仓库。

--set-upstream:设置本地分支与远程分支的关联,相当于 -u 参数的别名。

--all:推送所有分支到远程仓库。

--mirror:镜像推送,将本地仓库完全复制到远程仓库,包括所有分支和标签。

请注意,在执行 git push 之前,你需要确保先执行了 git commit 将更改提交到本地仓库。否则,git push 将不会有新的更改可以推送。

推送完成:

之后推送到远程的main分支,使用git push就可以了,本地和远程分支名称相同才行,否则使用:git push Remote HEAD:main

关于分支的关联,可以看4.3节。


出错?

可以使用http,或者在github帮助页面寻求帮助。(如SSH agent 未启动)

如果你新建仓库时,添加了license或者readme文件,需要先pull 才能push:

git pull --allow-unrelated-histories Remote main

(其中Remote是我的远程仓库的别名,main是远程仓库的默认分支)


至此,从新建本地仓库,到成功推送到远程,已经完成了。

这是最最最基本的操作,但是实际使用中远不止这些操作。

3.2 从远程到本地

3.21 克隆:git clone

git clone 用于从远程仓库克隆(复制)一个完整的仓库到本地。它的使用格式如下:

git clone <remote_repository_url> <local_directory>

其中,<remote_repository_url> 是远程仓库的 URL,可以是 HTTP、HTTPS 或 SSH 协议的地址,指向你想要克隆的仓库。<local_directory> 是要将仓库克隆到的本地目录的路径。(直接去仓库那里复制就行了)

使用 git clone 命令会创建一个与远程仓库相同的副本,并自动设置远程仓库的别名为 origin。它会下载仓库的所有历史记录、分支和标签,并在本地创建一个与远程仓库相对应的默认分支(通常是 main 或 master 分支)。通过克隆仓库,你可以开始在本地进行开发和版本控制。

注意:

默认情况下,git clone 命令只会克隆远程仓库的默认分支(通常是 main 或 master分支)。它不会克隆远程仓库中的所有分支。但是,克隆仓库时会下载整个仓库的历史记录,包括所有的分支和标签信息。

使用这里的url即可:

这里有个下载压缩包选项,他和git clone是不一样的:

  • 直接下载 ZIP 压缩包是从远程仓库下载代码的一种简单方式,不涉及 Git 工具或版本控制。
  • ZIP 压缩包只包含了当前的代码快照,没有版本控制历史记录、分支和标签等元数据。
  • 下载的 ZIP 压缩包可以用于浏览代码、查看文件内容,但无法进行版本控制或分支管理。
  • 如果需要更新代码,必须重新下载 ZIP 压缩包,而不能像使用 Git 那样轻松地获取最新更新。

如果你只是向抄个代码,下载压缩包是可以的。

我这里在Linux上克隆前面创建并且推送后的远程仓库:

来看看克隆后的仓库的名称:

  • * main:表示当前所在的分支是 main 分支。* 符号表示当前活动分支。
  • remotes/origin/HEAD -> origin/main:表示远程仓库的 HEAD 引用指向的是 origin/main 分支。这是远程仓库中默认分支的别名。
  • remotes/origin/main:表示远程仓库的 main 分支。
  • 而远程仓库的别名默认设置成了origin

3.22 拉取:git pull

git pull 命令用于从远程仓库拉取最新的代码,并将其合并到当前分支中。它实际上是两个操作的组合:git fetchgit merge

使用 git pull 命令的基本语法是:

git pull <remote> <branch>
<remote> 表示远程仓库的名称,通常是 origin,表示默认的远程仓库。
<branch> 表示要拉取的远程分支的名称。

例如,如果你想从远程仓库的 origin 中拉取 master 分支的最新代码,可以运行以下命令:

git pull origin master

git pull 命令的执行过程如下:

  • 首先,git fetch 命令会从远程仓库下载最新的提交和分支信息,但不会将其合并到当前分支。

  • 然后,git merge 命令会将远程分支的更改与当前分支进行合并。如果存在冲突,你需要解决冲突后再进行提交。

注意,git pull 命令默认会尝试将远程分支与当前分支进行合并。如果你想拉取远程分支的内容而不进行合并,可以使用 git fetch 命令。

另外,你还可以使用 git pull --rebase 命令来在拉取远程代码之前先将当前分支的提交应用到拉取的代码之上,这将使提交历史更加线性。这个过程称为变基(rebase),但请注意,在进行变基操作时可能会产生冲突,需要解决冲突后再进行提交。

现在我删除了远程仓库中的2个目录。并且提交到.gitignore文件中(5.1节),并且在Windows上重新推送。

在Linux进行git pull操作:

拉取后,会删除我之前克隆的2个文件夹,因为我在Windows侧已经为远程删除并且添加了 .gitignore文件。后面还有详细解决这部分。

四、多分支

回顾一下第三节:

在第四节,我会在Linux 1上再新建一个仓库,并于之前的远程仓库关联,Linux 1上的仓库只有一个默认分支,我将在github新建一个对应的仓库,并将Linux 1上的这个仓库内容推送到远程仓库对应的分支。

现在,远程仓库有2个分支,Windows和Linux 1各自编辑其中一个分支。

接着,在Linux 2克隆远程仓库(默认克隆远程仓库的主分支),这是只有1个默认本地分支与之关联,我将再新建一个本地分支,与远程的另一个分支关联。即在一个工作区下,通过切换分支的方式,进行2个分支的开发。

4.1 查看分支的名称

分支(branch)是Git中的一个重要概念,它允许开发人员在同一项目中同时进行多个并行开发线路。分支可以被看作是项目的独立副本,开发人员可以在分支上进行修改和实验,而不会影响主线(主分支)上的代码。

如下图所示,默认分支,即你使用git创建的第一个分支,默认名称是master(不是一定要叫这个名称),我们可以基于默认分支创建一个新的分支,也可以在某个分支的基础上再创建分支。

截止现在,我只有一个分支,查看本地所有分支名称的命令是:git branch,加上参数 -r 就是查看远程分支,参数 -a 就是查看本地和远程的所有分支。

示例:

这是我在Linux克隆的仓库,看上图第3个命令就行了,列出了所有分支:

(1)第一行:* main

列出了本地所有分支,只有一个,名称是main,星号表示这是当前所在分支。

(2)第三行:remotes/origin/main

列出了远程所有分支,只有一个,名称是main。remotes表示远程引用的命名空间,origin是远程仓库在本地的别名,main表示远程仓库的一个分支。

(3)第二行:remotes/origin/HEAD -> origin/main

其它不变,HEAD是指示当前远程仓库默认分支的引用。->箭头表示引用的指向关系。在这种情况下,它指示引用指向后面的分支,即origin/main,这是默认分支的引用。

所以这一行的意思是:远程仓库 origin 的默认分支是 main。它指示在与远程仓库进行交互时,默认应该使用的分支。当你从远程仓库获取更新或推送更改时,git会使用这个引用来确定要与之交互的默认分支。

4.2 修改分支名称

最好让本题和远程对应的分支名称相同,这样方便管理一些,并且使用git push 时可以建立追踪关联,简化命令。

修改分支名称分为修改本地分支名称和修改远程分支名称 。

(1)修改远程分支

一般来说,本地主分支默认名称是master,而在github创建一个仓库时,默认分支的名称可能是main,比如我前面的操作中,一直是这样。

直接到github去修改即可:

然后在本地拉取最新的更改:

git fetch

查看现在的远程分支名称:

git branch -r

看到虽然github已经更改了名称,这里还显示main这个分支:

删除本地仓库中不存在的远程分支的引用(Remote是我自定义的远程仓库的别名):

git remote prune Remote

现在好了:

(2)修改本地仓库名称

git branch -m <旧分支名> <新分支名>

4.3 关联本地分支和远程分支(设置上游分支)

前面已经讲过一些了。

当你在 Git 中进行远程跟踪操作时,你可以设置本地分支与远程分支之间的关联。上游分支是指与当前分支关联的远程分支,它是你拉取(pull)和推送(push)代码时的默认目标。

有至少3中方式可以完成这个操作。

(1)git push -u

这个命令前面已经讲过了。

git push -u <远程仓库名称> <本地分支名称>:<远程分支名称>

(2)git branch -u

git branch -u <远程仓库名>/<远程分支名>

(3)git branch --set-upstream-to

git branch --set-upstream-to=<远程仓库名>/<远程分支名>

第二种方式是第三种方式的简写,第一种方式建立关联的同时推送。

关联的本地分支和远程分支必须名相同才能在后面的推送中直接使用:git push,否则git会提醒你使用类似git push Remote HEAD:main之类的命令,反正会提示你,不用担心。

要查看本地分支与远程分支之间的关联情况:

git branch -vv


各项含义:本地分支 最新提交的哈希值 关联的远程分支 最新提交的message

4.4 新建与删除分支

在进行多分支开发时,本地可以只用一个工作空间(同一个项目文件夹),通过切换分支,这个工作空间的文件也会随之变化,即在一个本地工作空间下完成不同分支的开发。

当然,也可以分别为每个分支建一个工作空间,我先用这种方式操作。

  • 首先建立本地仓库:git init

  • 新建一个本地分支:git branch LinuxC

  • 切换到该分支:git checkout LinuxC
    (也可以使用 git branch -b LinuxC,创建并切换)

    现在,已经切换到新的分支了。不过还没有与远程仓库关联,远程仓库目前也只有一个main分支。

  • 关联远程仓库: git remote add origin git@github:CQUPTLei/C_Language.git(也可以放在提交后关联)

  • 添加并提交到本地仓库,git add .git commit -m "New branch LinuxC"

  • 推送:git push origin LinuxC:LinuxC,这个操作会为远程仓库新建一个名为LinuxC的新分支,并且推送到该分支。

删除分支:

git branch -d  本地分支名

如果是远程分支,可以到github删除或修改,然后使用git remote prune Remote,更新一下分支名称。

4.5 分支切换:git checkout

使用git checkout可以进行分支的切换,包括分支的历史版本(4.7节)。

这是我的分支和工作区情况:

在本地创建一个LinuxC分支并且切换,与远程的LinuxC分支关联,查看分支关联情况:

这个时候,本地工作空间还是之前的内容,因为我还没有将远程仓库中LinuxC分支的内容拉取到本地。

使用git fetch命令来获取远程仓库的最新信息;

使用git checkout -b 本地分支名称 origin/远程分支名称命令来创建一个新的本地分支并将远程分支的内容检出到本地。

再切换回master:

工作区的内容会着当前分支的变化而对应变化。

切换工作区时,Git会删除工作区中与目标分支不同的文件,并从目标分支中获取新的文件。但是,如果你在切换分支之前对工作区中的文件进行了修改,但没有提交这些修改,那么在切换分支时,Git会提示你先提交或撤销这些修改,否则这些修改将会丢失。因此,在切换分支之前,建议先使用git status命令查看当前工作区的状态,确保所有修改都已提交或撤销

当然了,也可以单独为每一个分支建立一个工作空间

4.6 拉取与合并

4.61 拉取并合并git pull

git pull 用于从远程仓库获取最新的提交,并将其合并到当前分支。

git pull 实际上执行了两个操作:git fetch 和 git merge。

  • git fetch:这个命令从远程仓库中获取最新的提交历史和文件更改,但不会自动合并到当前分支。它会更新你的本地仓库的远程分支引用,使其与远程仓库保持同步。
  • git merge:在 git pull 的第二个步骤中,Git 会自动执行合并操作。它将从远程仓库获取的最新提交合并到当前分支中。如果存在冲突,你需要解决冲突后再提交。

基本语法:

git pull [<远程仓库名>] [<远程分支名>]

关联之后,可以直接使用git pull。

适用场景举例:

A和B都对同一个分支进行开发。假设某个时刻A、B的本地仓库和远程仓库的内容相同,此后,A进行了一些开发工作,并且推送到了远程仓库,B在这个过程中什么也没做,那么B就可以直接pull。


但这不是常见情境,通常A和B都在开发,在上面的情况中,远程仓库的内容改变了,B的本地仓库也改变了,这时候,就要对这2种改变进行一些处理,比如AB对同一段代码进行了修改,A推送到远程了,这时候B如果又直接推送,那A不就白推送了,如果它先从远程仓库拉取A做的修改,那么如何处理他们2人在同一段代码上的修改呢?

直接看:4.64节。

4.62 获取更改:git fetch

git fetch 用于从远程仓库获取最新的提交历史和文件更改,但不会自动合并到当前分支。它更新你的本地仓库的远程分支引用,使其与远程仓库保持同步,但不会修改你当前所在的分支。

语法:

git fetch [<远程仓库名>] [<远程分支名>]

过程:

  1. 与远程仓库建立通信,并获取远程仓库中的提交历史和文件更改。
  2. 将远程仓库的分支引用更新到本地仓库中,以反映远程仓库的最新状态。这些远程分支引用将被存储在 .git/refs/remotes/ 目录下。
  3. 如果有新的分支或提交被拉取下来,你可以使用 git branch -v 命令查看远程分支列表,以及它们的最新提交的哈希值和提交消息

4.63 合并分支:git merge

git merge用于将一个分支的更改合并到当前分支。它会将其他分支的提交应用到当前分支,并创建一个新的合并提交来整合两个分支的更改。

基本语法:

git merge <branch>

主要步骤:

  1. 首先,确保你在要合并更改的目标分支上。你可以使用 git checkout 命令切换到目标分支。例如,如果要将 feature 分支的更改合并到 main 分支上,先切换到 main 分支:
git checkout main

然后,执行 git merge 命令并指定要合并的分支名称。例如,将 feature 分支的更改合并到当前所在的分支(main 分支):

  git merge feature
  1. Git 会尝试将目标分支与当前分支进行合并。如果两个分支的提交历史存在分叉,即它们共享一个共同的祖先,Git 将执行一次合并操作。

  2. 如果合并过程中没有冲突,Git 会自动创建一个新的合并提交。合并提交会有两个或多个父提交,以反映合并操作的历史。可以使用 git log 查看合并后的提交历史。

  3. 如果合并过程中出现冲突,Git 会暂停合并并标记冲突的文件。你需要手动解决冲突,编辑冲突的文件并选择最终的代码状态。解决冲突后,使用 git add 命令标记冲突已解决的文件。然后,执行 git merge --continue 继续合并操作。

  4. 一旦合并完成,目标分支将包含源分支的更改。你可以使用 git push 将合并后的分支推送到远程仓库。

合并操作可能会影响代码的历史记录和提交图表。因此,在对公共分支(如主分支)进行合并操作时,需要谨慎考虑和进行代码审查,以确保合并的更改是正确且符合项目需求的。

4.64 冲突解决(概述)

冲突通常发生在你本地进行了修改并推送到远程仓库后,其他人在同一分支上进行了修改并推送到远程仓库。这导致你的本地分支与远程分支产生了差异,无法简单地合并或变基。

比如:

解决这个问题的方法是,在执行下一次 git pull 命令之前,指定如何调和这些分歧。你可以运行以下命令中的一个,根据你的需求选择合适的选项:

  • 使用合并(merge)模式:git config pull.rebase false
  • 使用变基(rebase)模式:git config pull.rebase true
  • 仅使用快进(fast-forward)模式:git config pull.ff only

这些命令会设置 git pull 的默认行为,以解决分歧。你也可以在每次执行 git pull 命令时通过命令行参数覆盖默认设置,例如 git pull --rebase 或 git pull --no-rebase。

如果你想为所有的仓库设置默认行为,可以在命令中加上 --global 参数,例如 git config --global pull.rebase true。


  1. Merge(合并)
    • git merge 命令将一个分支的更改合并到另一个分支。
    • 在合并过程中,Git 会创建一个新的合并提交,该提交将两个分支的更改整合在一起。
    • 如果两个分支的提交历史有分叉,即它们共享一个共同的祖先,Git 将执行一次合并提交,将两个分支的更改合并在一起。
    • 合并提交会有两个或多个父提交,以反映合并操作的历史。
  2. Rebase(变基)
    • git rebase 命令用于将一个分支的更改基于另一个分支进行重新应用。
    • 在变基过程中,Git 会将当前分支的提交逐个应用于目标分支,产生一系列新的提交。
    • 这种操作使得分支的提交历史线性化,看起来更加干净整洁。
    • 变基操作不会创建合并提交,而是创建一系列新的提交,每个提交都是基于目标分支的最新提交。
  3. Fast-forward(快进合并)
    • fast-forward 是一种特殊的合并情况,发生在目标分支的 HEAD 指针可以直接移动到要合并的分支的最新提交上的情况。
    • 当没有分叉或冲突时,Git 将直接将目标分支的 HEAD 指针移动到要合并的分支的最新提交上,而无需创建新的合并提交。
    • 这种合并不会产生合并提交,因此合并后的提交历史是线性的。

在选择合适的合并策略时,可以考虑以下几点:

  • 如果你希望保留分支的完整历史记录并明确显示合并操作的历史,可以使用 merge
  • 如果你希望将分支的更改应用于目标分支,并使提交历史线性化,可以使用 rebase
  • 如果两个分支的提交历史是线性的且没有分叉,可以使用 fast-forward

需要注意的是,在对公共分支(如主分支)进行合并操作时,推荐使用 mergefast-forward,因为 rebase 可能会改写提交历史,而这可能会导致其他开发人员的问题。


说点人话吧,回到4.61的那个问题:当多个开发者对同一文件进行修改并推送到远程仓库时,另一个开发者在拉取(git pull)时可能会遇到冲突。在这种情况下,B需要选择适当的冲突解决方式。以下是几种常见的冲突解决方式:

  1. 手动解决冲突

    • B可以手动编辑冲突的文件,解决冲突并选择最终的代码状态。
    • 冲突的部分会被特殊标记,你需要查看并编辑这些部分以解决冲突。
    • 一旦解决了所有冲突,B需要使用 git add 命令标记冲突已解决的文件。
    • 最后,B可以使用 git commit 提交解决冲突的文件,并生成一个新的合并提交。
  2. B接受A的更改

    • 如果B更倾向于接受A的更改,可以选择直接合并A的提交。在拉取时,B可以使用 git pull--rebase 选项,即 git pull --rebase。这将使用变基(rebase)策略将B自己的提交应用于A的提交之上。
  3. 如果开发者 B 不想使用开发者 A 的修改,有几种选择:

    1. 拒绝合并(Reject the merge)
      • B可以选择拒绝合并开发者 A 的修改,即不将 A 的提交应用到自己的分支上。
      • 在拉取时,B可以使用 git pull--no-commit 选项,即 git pull --no-commit
      • 这将拉取 A 的提交,但不会自动创建合并提交。
      • 然后,B可以使用 git reset HEAD 命令取消已拉取的 A 的提交。
      • 最后,B可以手动撤销或修改 A 的更改,以满足自己的需求,并提交自己的更改。
    2. 回滚(Rollback)
      • 如果 A 的提交已经应用到 B 的分支上,而 B 希望撤销这些更改,可以选择回滚操作。
      • B可以使用 git revert 命令来撤销已经提交的 A 的提交。
      • git revert 会创建一个新的提交,该提交会撤销 A 的提交所引入的更改。
      • 这种方式可以保留 A 的修改历史,并在 B 的分支上应用撤销更改。
    3. 丢弃本地更改(Discard local changes)
      • 如果 B 在 A 的提交之前有本地的未提交更改,且不想保留这些更改,可以选择丢弃本地更改。
      • B可以使用 git stash 命令暂存本地更改,将工作目录恢复到与远程仓库一致的状态。
      • 然后,B可以执行 git pull 以拉取 A 的提交,并丢弃之前的本地更改。
      • 最后,B可以使用 git stash applygit stash pop 恢复之前暂存的本地更改。

4.7 版本回退

git的版本控制,不仅可以使用分支功能开发不同版本的应用,还可以在出问题时回退到之前的任意一个版本。

例:我添加了一个 test.c文件,并推送到了远程,现在项目不能运行了,如何回到没添加这个文件时的版本呢?

先使用 git log 查看提交记录:

可以看到历史提交的message,提交的哈希值,提交人和日期。

我么可以使用上面的哈希值回退到对应提交时的版本。

切换到历史版本可以使用:

git checkout 哈希值
# 或者

git reset 哈希值 

checkout用于浏览历史版本,当前分支的指针位置仍然在原来的最新提交处,也就是你不能在这种状态下进行提交,否则可能会被git清理掉。但你可以切换到历史版本,然后新建一个分支进行提交。

另外可以使用reset回退到历史版本,当前分支的指针也随之回到历史提交的位置,现在你可以进行提交,但是曾经在这个提交后面的提交会被删除。

git reset命令有三个常用的选项:

  • --soft:这个选项会将分支指针移动到指定的提交,但不会修改工作目录和暂存区。这意味着你可以撤销最新的提交,并且保留之前的更改,以便你可以进行新的提交。
  • --mixed(默认选项):这个选项会将分支指针移动到指定的提交,并且会重置暂存区的内容,但不会修改工作目录的文件。这意味着你会丢失暂存区中的更改,但可以通过重新添加文件来重新构建暂存区,并在需要时进行提交。
  • --hard:这个选项会将分支指针移动到指定的提交,并且会重置暂存区和工作目录的内容,使它们与指定提交完全一致。这意味着你会彻底丢失最新的更改,请谨慎使用

使用checkout做个示例吧。现在的工作区内容:

回到添加test.c之前的版本:

 git checkout e0d5605ef19f7f8007f9df59f032fd6c4dc5aec2

工作区的内容:

五、其他重要内容

5.1 .gitignore

.gitignore 是一个用于指定要忽略的文件和目录的 Git 配置文件。它的作用是告诉 Git 哪些文件和目录在版本控制中应该被忽略,不加入到 Git 仓库中。

当你在项目中创建一个 .gitignore 文件并定义了要忽略的文件和目录后,Git 在执行相关操作时会自动忽略这些文件和目录,不会将它们添加到暂存区和提交历史中。这对于排除一些不必要的或敏感的文件、编译生成的文件、临时文件、日志文件等非项目必需的文件非常有用。

以下是一些关键点和使用方法来详细介绍 .gitignore:

  1. 文件格式:.gitignore 是一个文本文件,每行包含一个规则。每个规则描述了要忽略的文件或目录的模式。规则可以使用通配符和模式匹配规则。

  2. 模式匹配规则:.gitignore 支持三种模式匹配规则:

    • *:匹配任意数量的字符,但不包括目录分隔符 /。
    • ?:匹配单个字符。
    • /:用于指定目录路径。
  3. 注释:.gitignore 文件支持使用 # 进行注释。在 # 之后的内容会被视为注释,不会被 Git 解析。
    规则示例:下面是一些 .gitignore 规则的示例:

    build/:忽略名为 build 的目录。
    *.log:忽略所有以 .log 结尾的文件。
    secret.txt:忽略名为 secret.txt 的文件。
    /docs/*.pdf:忽略 docs 目录下的所有以 .pdf 结尾的文件。

  4. 全局和局部的 .gitignore:可以在项目根目录下创建一个 .gitignore 文件来针对该项目设置忽略规则。此外,还可以在全局范围内创建一个名为 ~/.gitignore_global 的文件,其中的规则将应用于所有 Git 仓库。你可以使用 git config 命令将全局的 .gitignore 文件与 Git 关联起来。

  5. 生效时机:.gitignore 文件的规则仅在它所在的目录及其子目录下生效。如果你想要忽略整个仓库中的某个文件或目录,你需要在相应的根目录下创建 .gitignore 文件。

一般可以使用你使用的IDE来生成gitignore文件,他会忽略掉IDE 相关的不必要文件,你只需要在里面添加额外的你想忽略的文件即可。

比如Visual Studio的:

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.sln.docstates

# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/

# Visual Studio 2015/2017/2019 cache/options directory
.vs/

# Visual Studio 2017/2019 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUnit
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Coverlet is a free, cross platform Code Coverage Tool
coverage/
*.csproj.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Publish profiles
*.pubxml
*.publishproj

# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#

5.2 标签:tag

标签(Tag)是用于标记特定提交的有意义的标识符。它类似于分支,但与分支不同的是,标签通常用于标记发布版本、重要的里程碑或其他重要的提交

标签在 Git 中有两种类型:轻量标签(Lightweight Tags)和附注标签(Annotated Tags)。

  1. 轻量标签(Lightweight Tags):轻量标签只是一个指向特定提交的引用,类似于分支指针。它们是一种简单的标记,没有额外的信息,只是一个指向提交的静态引用。创建轻量标签非常简单,只需要在特定的提交上运行 git tag <tag-name> 命令即可。
git tag v1.0.0
  1. 附注标签(Annotated Tags):附注标签包含更多的信息,例如标签作者、日期、注释等。它们是独立于分支和提交的对象,可以像提交一样进行签名,并且可以包含额外的注释信息。创建附注标签时,可以使用 -a--annotate 选项,并在命令后面添加标签名称。
git tag -a v1.0.0 -m "Release version 1.0.0"

上述命令将创建一个附注标签 v1.0.0,并添加了注释信息 “Release version 1.0.0”。

标签的作用是给特定的提交赋予一个有意义的名称,方便查找和引用。标签可以用于发布版本号、里程碑、重要补丁等场景。在 Git 中,你可以使用 git tag 命令来列出现有的标签,使用 git show <tag-name> 命令来查看标签的详细信息,或者使用标签名称作为参考在特定的提交上进行操作。

推送(我这里管理分支了,是简写):

git push  --tags

github即可看见标签:

六、Github 重要操作

说一些个人常用的吧。

6.1 问题追踪:issue

Git本身并不提供问题跟踪(issue tracking)功能。但是,许多基于Git的代码托管平台(如GitHub和GitLab)都提供了问题跟踪功能。

在这些平台上,您可以使用问题(issue)来跟踪和管理项目中的任务、缺陷和需求。您可以创建问题,为其分配负责人和标签,将其添加到里程碑和项目中,并与其他贡献者进行讨论。

关于提交issue,高天的视频还不错,建议看看(记得3连):

如何正确地提github issue?开源项目作者来和你聊聊这个重要技能

6.2 拉取请求:pull requests

拉取请求(Pull Request)是一种协作开发的方式,它允许开发人员在将更改合并到官方项目之前讨论所提出的更改。拉取请求通常用于基于Git的代码托管平台(如GitHub和Bitbucket)。

在其最简单的形式中,拉取请求是一种开发人员通知团队成员他们已完成某项功能的机制。当开发人员准备好他们的功能分支时,他们通过自己的代码托管平台账户提交一个拉取请求。这会让所有相关人员知道他们需要审查代码并将其合并到主分支中。

但是,拉取请求不仅仅是一个通知——它是一个专门用于讨论建议功能的论坛。如果更改有任何问题,团队成员可以在拉取请求中发布反馈甚至通过推送后续提交来调整功能。所有这些活动都直接在拉取请求中跟踪。

与其他协作模型相比,这种共享提交的正式解决方案使工作流程更加流畅。SVN和Git都可以使用简单脚本自动发送通知电子邮件;但是,在讨论更改时,开发人员通常必须依赖电子邮件线程。这可能会变得杂乱无章,特别是当涉及后续提交时。拉取请求将所有这些功能放入一个友好的Web界面中,紧挨着您的代码托管平台存储库。

6.3 讨论:Discussion

GitHub Discussions是一种协作通信工具,它为开源或内部项目的社区提供了一个论坛。社区成员可以提问和回答问题,分享更新,进行开放式对话,并跟踪影响社区工作方式的决策

你可以使用讨论(discussion)来提问和回答问题,分享信息,发布公告,并在GitHub上的项目中进行或参与对话。您可以与社区和维护者在项目的存储库内的论坛中交流。

你可以通过标记评论为答案、锁定或解锁讨论、将问题转换为讨论以及编辑或删除不符合社区行为准则的评论、讨论和类别来促进健康的协作。

当运行一些开源项目出现问题时,可以先去这里看看有没有人遇到过相似的问题,如果没有自己再进行提问,一般都能在1天内获得回复的。

6.4 Wiki

在GitHub上,每个存储库都配备了一个用于托管文档的部分,称为wiki。可以像在GitHub的其他地方一样编写wiki内容。相当于项目说明书。

七、命令速查

  1. 配置:
    • git config --global user.name "Your Name": 设置用户姓名
    • git config --global user.email "your.email@example": 设置用户邮箱
  2. 初始化和克隆:
    • git init: 在当前目录初始化一个新的Git仓库
    • git clone <repository>: 克隆远程仓库到本地
  3. 基本操作:
    • git add <file>: 将文件添加到暂存区
    • git commit -m "Commit message": 提交暂存区的修改到本地仓库
    • git status: 查看工作区和暂存区的状态
    • git log: 查看提交历史
  4. 分支操作:
    • git branch: 查看本地分支
    • git branch <branch_name>: 创建新分支
    • git checkout <branch_name>: 切换到指定分支
    • git merge <branch_name>: 将指定分支合并到当前分支
    • git branch -d <branch_name>: 删除本地分支
  5. 远程仓库:
    • git remote -v: 查看远程仓库信息
    • git pull: 从远程仓库拉取最新代码
    • git push origin <branch_name>: 推送本地分支到远程仓库
  6. 标签操作:
    • git tag: 列出所有标签
    • git tag <tag_name>: 创建标签
    • git tag -a <tag_name> -m "Tag message": 创建带有注释的标签
    • git push origin --tags: 推送所有标签到远程仓库
  7. 撤销和修改:
    • git reset <file>: 从暂存区撤销文件
    • git checkout -- <file>: 丢弃工作区的修改
    • git revert <commit>: 撤销指定的提交
    • git reset --hard <commit>: 将HEAD指针重置到指定的提交,丢弃之后的提交
  8. 查找:
    • git grep <pattern>: 在代码库中搜索指定模式
    • git diff: 查看未暂存的修改
    • git diff --staged: 查看已暂存的修改
    • git blame <file>: 逐行显示文件的修改信息


~

本文标签: 万字Git