Git学习笔记

本文详细介绍了Git作为分布式版本控制系统的基本原理、安装过程、配置步骤以及常见操作。从本地版本控制系统的不足出发,阐述了集中化和分布式版本控制系统的区别,重点讲解了Git的工作流程、版本库的创建、文件的添加与提交、分支管理、历史记录查看、协同工作模式等内容。此外,还深入探讨了Git的内部结构,如对象数据库、工作目录状态和Git仓库的目录结构。
摘要由CSDN通过智能技术生成

Git

Git 简介

定义:分布式版本控制系统

版本控制(VCS)是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。

本地版本控制系统

许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。

为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。

本地版本控制图解

其中最流行的一种叫做 RCS,现今许多计算机系统上都还看得到它的踪影。 RCS 的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。

集中化的版本控制系统

接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。 这类系统,诸如 CVS、Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。 多年以来,这已成为版本控制系统的标准做法。

集中化的版本控制图解

这种做法带来了许多好处,特别是相较于老式的本地 VCS 来说。 现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么。 而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。

事分两面,有好有坏。 这么做最显而易见的缺点是中央服务器的单点故障。 如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。 如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问你将丢失所有数据——包括项目的整个变更历史,只剩下人们在各自机器上保留的单独快照。 本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。

分布式版本控制系统

于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 在这类系统中,像 Git、Mercurial、Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。

分布式版本控制图解

更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。 你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。

Git设计初衷

  • 速度
  • 简单的设计
  • 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
  • 完全分布式
  • 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)

Git 安装

macOS 下 安装

下载地址:https://git-scm.com/downloads

  1. Homebrew安装

brew install git

  1. Xcode安装

苹果通过 Xcode随附了二进制Git

  1. 二进制安装

最新版本的Git安装包installer,最新版是 2.27.0

  1. 通过源代码构建

可以通过源代码构建git on kernel.org

  1. 安装 git-gui

git的commit GUI和交互式历史记录浏览器git-gui and gitk
brew install git-gui

  1. 检测是否安装完成

git --version

Git 配置

Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量。 这些变量存储在三个不同的位置:

  1. /etc/gitconfig 文件: 包含系统上每一个用户及他们仓库的通用配置。 如果在执行 git config 时带上 --system 选项,那么它就会读写该文件中的配置变量。 (由于它是系统配置文件,因此你需要管理员或超级用户权限来修改它。)
  2. ~/.gitconfig~/.config/git/config 文件:只针对当前用户。 你可以传递 --global 选项让 Git 读写此文件,这会对你系统上 所有 的仓库生效。
  3. 当前使用仓库的 Git 目录中的 config 文件(即 .git/config):针对该仓库。 你可以传递 --local 选项让 Git 强制读写此文件,虽然默认情况下用的就是它。(当然,你需要进入某个 Git 仓库中才能让该选项生效。)

每一个级别会覆盖上一级别的配置。

$ git config --list --show-origin:查看所有的配置以及它们所在的文件
$ git config --global user.name:设置用户名
$ git config --global user.email:设置邮箱地址
$ git config --list:列出所有 Git 当时能找到的配置
$ git config <key> :检查 Git 的某一项配置

创建版本库

版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

将已有项目代码纳入Git管理

一、版本库目录

创建一个版本库非常简单,首先,选择一个合适的地方,创建一个空目录

二、让你的目录成为Git可以管理的仓库

$ git init
Initialized empty Git repository in /Users/peterxu/learn/.git/

提示这个,就说明我们的git仓库创建好了。 初始化仓库后 在你的版本目录下会出现一个 .git 的隐藏文件。

新建项目用Git管理

$ git init your_project #会在当前路径下创建和项目名称同名的文件夹

获取帮助

$ git help <verb>
$ git <verb> --help
$ git <verb> --help --web
$ git <verb> -h

记录每次更新到仓库

工作目录下的每一个文件都不外乎这两种状态:已跟踪未跟踪。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后, 它们的状态可能是未修改,已修改或已放入暂存区。简而言之,已跟踪的文件就是 Git 已经知道的文件。

工作目录中除已跟踪文件外的其它所有文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有被放入暂存区。

编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复。

Git 下文件生命周期图。

三、添加文件到版本库

使用 git status 命令,来看看现在仓库的状态

$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	images/
	index.html

nothing added to commit but untracked files present (use "git add" to track)

提示很清晰的告诉了我们,提交内容为空,但是仓库里存在两个为被跟踪的文件,也提示到 使用 git add 命令来跟踪。

$ git add .        //. 当前文件目录下
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   images/git-logo.png
	new file:   index.html

查看下当前的仓库状态,可以看到初始提交到一个文件,但是这提交的文件是暂时存放在暂存区里,并没有真正的提交上去.

接下来,真正的把文件提交到仓库

$ git commit -m'Add index.html and images'
[master (root-commit) 973feeb] Add index.html and images
 2 files changed, 49 insertions(+)
 create mode 100644 images/git-logo.png
 create mode 100644 index.html

commit可以一次提交很多文件,所以你可以多次add不同的文件,只做一次提交。

$ git add -u

文件重命名

$ mv readme readme.md
$ git add readme.md
$ git rm readme
rm 'readme'
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    readme -> readme.md
$ git reset --hard
$ git mv readme readme.md

查看Git版本历史

$ git log

不传入任何参数的默认情况下,git log 会按时间先后顺序列出所有的提交,最近的更新排在最上面。 正如你所看到的,这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明。

$ git log -p -2:仅显示2次提交所引入的差异
$ git log --oneline master
$ git log -n2
$ git log --all --graph
$ git checkout -b temp 47242dc1
$ git commit -am'fix readme'
$ git branch -av

.git 目录

Git 是内容寻址文件系统,其核心部分是一个简单的键值对数据库(key-value data store)。 向 Git 仓库中插入任意类型的内容,它会返回一个唯一的键,通过该键可以在任意时刻再次取回该内容。objects 目录存储所有数据内容;refs 目录存储指向数据(分支、远程仓库和标签等)的提交对象的指针; HEAD 文件指向目前被检出的分支;index 文件保存暂存区信息。

$ cd .git
$ ls -al
total 56
drwxr-xr-x  14 peterxu  staff  448 Dec  7 22:42 .
drwxr-xr-x   9 peterxu  staff  288 Dec  7 22:30 ..
-rw-r--r--   1 peterxu  staff   11 Dec  7 22:31 COMMIT_EDITMSG
-rw-r--r--   1 peterxu  staff   21 Dec  7 22:30 HEAD
-rw-r--r--   1 peterxu  staff   41 Dec  7 22:21 ORIG_HEAD
-rw-r--r--   1 peterxu  staff  137 Dec  7 21:25 config
-rw-r--r--   1 peterxu  staff   73 Dec  7 21:25 description
-rw-r--r--   1 peterxu  staff  502 Dec  7 22:36 gitk.cache
drwxr-xr-x  13 peterxu  staff  416 Dec  7 21:25 hooks
-rw-r--r--   1 peterxu  staff  554 Dec  7 22:31 index
drwxr-xr-x   3 peterxu  staff   96 Dec  7 21:25 info
drwxr-xr-x   4 peterxu  staff  128 Dec  7 21:59 logs
drwxr-xr-x  28 peterxu  staff  896 Dec  7 22:31 objects
drwxr-xr-x   4 peterxu  staff  128 Dec  7 21:25 refs
$ cat HEAD
ref: refs/heads/temp
$ cat config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[user]
	name = peter
$ cd refs
$ ls -al
total 0
drwxr-xr-x   4 peterxu  staff  128 Dec  7 21:25 .
drwxr-xr-x  14 peterxu  staff  448 Dec  7 22:45 ..
drwxr-xr-x   4 peterxu  staff  128 Dec  7 22:31 heads
drwxr-xr-x   2 peterxu  staff   64 Dec  7 21:25 tags
$ cd heads
$ ls -al
total 16
drwxr-xr-x  4 peterxu  staff  128 Dec  7 22:31 .
drwxr-xr-x  4 peterxu  staff  128 Dec  7 21:25 ..
-rw-r--r--  1 peterxu  staff   41 Dec  7 22:23 master
-rw-r--r--  1 peterxu  staff   41 Dec  7 22:31 temp
  
$ git diff HEAD HEAD^^
$ git diff HEAD HEAD~2
$ cat master
3a1c6365c4b70a8a24d083cb5e60b0f2066a3a69
$ git cat-file -t 3a1c636
commit
$ git branch -av
  master 3a1c636 Move readme to readme.md
* temp   9f33712 fix readme
$ git tag -a v1.0 d6f11da7
$ cd tags
$ ls -al
total 8
drwxr-xr-x  3 peterxu  staff   96 Dec  7 22:53 .
drwxr-xr-x  4 peterxu  staff  128 Dec  7 21:25 ..
-rw-r--r--  1 peterxu  staff   41 Dec  7 22:53 v1.0
  
$ cat v1.0
4aa1d3ff0751380fa7041175acceb1507089567f
  
$ git cat-file -t 4aa1d3ff
tag
  
$ git cat-file -p 4aa1d3ff
object d6f11da75e7b7a9f77a7d4d6bb0d835d08076500
type commit
tag v1.0
tagger peter <xusiwei@ebaolife.com> 1607352766 +0800

test tag
 
$ git cat-file -t d6f11da75e7b7
commit
$ cd objects
$ cd d3
$ ls -al
total 16
drwxr-xr-x   4 peterxu  staff  128 Dec  7 22:16 .
drwxr-xr-x  29 peterxu  staff  928 Dec  7 22:53 ..
-r--r--r--   1 peterxu  staff  383 Dec  7 22:16 42052a6b5100babad3dc9e3f1e46182fbcdcbe
-r--r--r--   1 peterxu  staff  148 Dec  7 22:05 e1fe3a989bb478e0fcdb822e1073f7dd0a5cf0
$ git cat-file -t d3e1fe3a9
tree
$ git cat-file -p d3e1fe3a9
040000 tree 96b67e399c8496ec36cbbbcb776eb924fad7f9a7	images
100644 blob 6ad4c68d567a1a5b415dcfce2010fce1a60b245f	index.html
100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f	readme
040000 tree aee37060401d19e7bd9f80b7b33920a000e96b5b	styles
$ git cat-file -t 6ad4c
blob

commit、tree和blob三个对象之间的关系

$ git cat-file -p d6f11da7
tree d3e1fe3a989bb478e0fcdb822e1073f7dd0a5cf0
parent db01d2b164bd0eed1a49aba6094e76e95fdbab21
author Peter Xu <xusiwei@ebaolife.com> 1607349946 +0800
committer Peter Xu <xusiwei@ebaolife.com> 1607349946 +0800

Add styles
  
$ git cat-file -p d3e1fe3a989
040000 tree 96b67e399c8496ec36cbbbcb776eb924fad7f9a7	images
100644 blob 6ad4c68d567a1a5b415dcfce2010fce1a60b245f	index.html
100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f	readme
040000 tree aee37060401d19e7bd9f80b7b33920a000e96b5b	styles

新建git版本库,有且仅有一个commit,仅仅包含/doc/readme,请问内含多少个tree,多少个blob?

分离头指针

$ git checkout d6f11da75
Note: switching to 'd6f11da75'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at d6f11da Add styles
    
$  vi styles/style.css
$ git status
HEAD detached at d6f11da
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   styles/style.css

no changes added to commit (use "git add" and/or "git commit -a")
   
$ git log
commit 1a58fc33ffc43f4a184f0224f4ea5e96f52cca74 (HEAD)
Author: peter <xusiwei@ebaolife.com>
Date:   Mon Dec 7 23:14:50 2020 +0800

    Change color

$ git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  1a58fc3 Change color

If you want to keep it by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> 1a58fc3

Switched to branch 'master'
   
$ git branch fix_css 1a58fc3
   
$ git checkout -b fix_readme fix_css
Switched to a new branch 'fix_readme'

常用操作

  1. 删除分支

    $ git branch -d(-D) fix_css
    Deleted branch fix_css (was 1a58fc3).
    
  2. 修改最近一次commit的message

    $ git commit --amend
    [temp cbc6bdc] fix readme.md
     Author: Peter Xu <xusiwei@ebaolife.com>
     Date: Mon Dec 7 22:31:15 2020 +0800
     1 file changed, 1 insertion(+), 1 deletion(-)
    
  3. 修改老旧commit的message®

    $ git rebase(变基) -i d6f11da(父commit_id)
    [detached HEAD 22af246] Add javascript
     Author: Peter Xu <xusiwei@ebaolife.com>
     Date: Mon Dec 7 22:09:08 2020 +0800
     1 file changed, 15 insertions(+)
     create mode 100644 js/script.js
    Successfully rebased and updated refs/heads/temp.
    
  4. 把连续多个commit合并成一个(s)

    $ git rebase -i 973feeb73
    [detached HEAD b9c0179] Create a complete web page
     Author: Peter Xu <xusiwei@ebaolife.com>
     Date: Mon Dec 7 22:02:59 2020 +0800
     4 files changed, 86 insertions(+), 1 deletion(-)
     create mode 100644 js/script.js
     create mode 100644 readme
     create mode 100644 styles/style.css
    Successfully rebased and updated refs/heads/master.
    
  5. 把间隔的几个commit合并成一个

    $ git rebase -i 973feeb7385a
      
    pick 973feeb
    s 3891e60 Move readme to readme.md
    pick b9c0179 Create a complete web page
    
  6. 比较暂存区和HEAD所含文件的差异

    $ git diff --cached
    
  7. 比较暂存区和工作区所含文件的差异

    $ git diff
    $ git diff -- readme.md
    
  8. 恢复暂存区和HEAD相同

    $ git reset HEAD
    
  9. 恢复工作区和暂存区相同

    $ git checkout -- index.html
    
  10. 取消暂存区部分文件的更改

    $ git reset HEAD -- index.html
    
  11. 消除最近的几次提交

    $ git reset --hard commit_id
    
  12. 比较不同提交指定文件的差异

    $ git diff test master -- filename
    $ git diff commit_id1 commit_id2 -- filename
    
  13. 删除文件

    $ git rm filename
    
  14. 文件暂存

    $ git stash
    $ git stash list
    $ git stash apply
    $ git stash pop
    
  15. 协同工作时,不同的人修改了不同的文件或不同的人修改了同文件的不同区域

    $ git fetch
    $ git merge
    $ git pull
    

    Git有两种合并:一种是"直进式合并"(fast forward),不生成单独的合并节点;另一种是"非直进式合并"(none fast-forword),会生成单独节点。

    前者不利于保持commit信息的清晰,也不利于以后的回滚,建议总是采用后者(即使用--no-ff参数)。只要发生合并,就要有一个单独的合并节点。

  16. 协同工作时,不同的人修改了同文件的相同区域

    处理冲突后add and commit
    
  17. 同时变更了文件名和文件内容

    无需处理
    
  18. 同时变更文件名

    处理冲突
    
  19. 禁止向集成分支执行push -f 及执行变更历史的操作

工作流

功能驱动式开发(Feature-driven development,简称FDD):需求是开发的起点,先有需求再有功能分支(feature branch)或者补丁分支(hotfix branch)。完成开发后,该分支就合并到主分支,然后被删除。

git flow

img

长期分支:主分支(master)和开发分支(test)

短期分支:功能分支(feature branch)、补丁分支(hotfix branch)和预发布分支(release branch)

功能分支:它是为了开发某种特定功能,从Develop分支上面分出来的。开发完成后,要再并入Develop。功能分支的名字,可以采用feature-*的形式命名。

预发布分支:它是指发布正式版本之前(即合并到Master分支之前),我们可能需要有一个预发布的版本进行测试。预发布分支是从Develop分支上面分出来的,预发布结束以后,必须合并进Develop和Master分支。它的命名,可以采用release-*的形式。

补丁分支:软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补。修补bug分支是从Master分支上面分出来的。修补结束以后,再合并Master和Develop分支。它的命名,可以采用fixbug-*的形式。

github flow

img

第一步:根据需求,从master拉出新分支,不区分功能分支或补丁分支。

第二步:新分支开发完成后,或者需要讨论的时候,就向master发起一个pull request(简称PR)。

第三步:Pull Request既是一个通知,让别人注意到你的请求,又是一种对话机制,大家一起评审和讨论你的代码。对话过程中,你还可以不断提交代码。

第四步:你的Pull Request被接受,合并进master,重新部署后,原来你拉出来的那个分支就被删除。(先部署再合并也可。)

gitlab flow

  1. 上游优先:只存在一个主分支master,它是所有其他分支的"上游"。只有上游分支采纳的代码变化,才能应用到其他分支。

  2. 持续发布:

    img

    对于"持续发布"的项目,它建议在master分支以外,再建立不同的环境分支。比如,"开发环境"的分支是master,"预发环境"的分支是pre-production,"生产环境"的分支是production

    开发分支是预发分支的"上游",预发分支又是生产分支的"上游"。代码的变化,必须由"上游"向"下游"发展。比如,生产环境出现了bug,这时就要新建一个功能分支,先把它合并到master,确认没有问题,再cherry-pickpre-production,这一步也没有问题,才进入production

    只有紧急情况,才允许跳过上游,直接合并到下游分支。

  3. 版本发布:

    img

    对于"版本发布"的项目,建议的做法是每一个稳定版本,都要从master分支拉出一个分支,比如2-3-stable2-4-stable等等。

    以后,只有修补bug,才允许将代码合并到这些分支,并且此时要更新小版本号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值