git基础知识

一、git简介

Git 是一个免费的开源分布式版本控制系统,旨在快速高效地处理从小到大的所有项目。Git 易于学习,占用空间小,性能快如闪电。 它优于 Subversion、CVS、Perforce 和 ClearCase 等 SCM 工具,具有便宜的本地分支、方便的暂存区和多个工作流等功能。 — https://git-scm.com/

1.1 git仓库四大区域

git四大关键区域关系
图/git四大关键区域关系

掌握了这张图里的内容,git的实用技能就掌握80%了:

  • 工作区:用来编辑保存项目文件的地方,也是用户能直接操作到的地方。例如:在任意编辑器中输入"hello git",点击保存后,内容就存于工作区中。
  • 暂存区:用来保存下次即将提交到版本库的文件列表信息,一般在git仓库中,是一个叫index的文件。例如:输入指令git add .后,工作区中的变更内容就记录到暂存区。
  • 版本库:也称为本地仓库,也就是你电脑中的它和其它无数个开发伙伴电脑中的它,组成了git的分布式版本控制系统。例如:输入指令git commit后,暂存区中记录的内容就被推送到版本库中。
  • 远程仓库:托管在因特网或其它网络中的项目的版本库,你可以理解为是版本库(本地仓库)在云端的克隆版。有了远程仓库,所有开发小伙伴就可以基于它这个桥梁,实现本地仓库的版本协同。例如:输入git push origin master则可以将版本库中的master分支内容更新至远程仓库;输入git pull origin master就可以将远程仓库中的内容更新至本地(包括工作区、暂存区、版本库)。

1.2 存储原理

注意:本节内容可以忽略(适当了解更好)。 💲 只要记住这一点就好:初始化仓库后,.git文件中的内容不要删除

  1. 初始git仓库
mkdir storage
cd storage
# 初始化git仓库
git init
# 配置user.name
git config --local user.name "Pioneer4"
# 配置user.email
git config --local user.email "electricalqzhang@gmail.com"

# 查看.git下的目录结构
tree .git/
/*
.git/
├── branches    	不这么重要,暂不用管    
├── config	 		git配置信息,包括用户名,email,remote repository的地址,本地branch
|           	 	和remote branch
├── description		该git库的描述信息,如果使用了GitWeb的话,该描述信息将会被显示在该repo的页面上
├── HEAD			工作目录当前状态对应的commit,一般来说是当前branch的head,HEAD也可以通过git 
|					checkout命令被直接设置到一个特定的commit上,这种情况被称之为 detached HEAD 
├── hooks			钩子程序,可以被用于在执行git命令时自动执行一些特定操作,例如加入changeid
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   └── update.sample
├── info			不这么重要,暂不用管
│   └── exclude
├── objects			保存git对象的目录,包括三类对象commit,tree和blob
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags
*/


  1. 进行一次提交
# 进行一次提交
echo "init project" >> readme.md
mkdir src
echo "hello git" >> src/db.log
git add .
git commit -m "init prj"

# 查看.git中objects的目录结构
tree .git/objects/
/*
.git/objects/
├── 05											commit_id的前两位
│   └── 3baa2271d8de75b8621f08ae6fc6694bf31593	文本内容的SHA-1哈希值作为校验和(取38字符)
├── 69
│   └── b0bd61b54155b5ff6ed45847e4e01767ca14db
├── 8d
│   └── 0e41234f24b6da002d962a26c2495ea16a425f
├── a1
│   └── 0a826fc98d5263841dacf5c66014bb1f7f4acd
├── d6
│   └── c639c45454a483b6f0e5fa6828d45f3ca5c7f3 
├── info
└── pack
*/

说明:Git Object目录中存储了三种对象:Commit, tree和blob。Git为对象生成一个文件,并根据文件信息生成一个 SHA-1 哈希值作为文件内容的校验和,创建以该校验和前两个字符为名称的子目录,并以 (校验和) 剩下 38 个字符为文件命名 ,将该文件保存至子目录下。

  1. 查看Git Object的内容
cat .git/HEAD
> ref: refs/heads/maste
cat .git/refs/heads/master
> 69b0bd61b54155b5ff6ed45847e4e01767ca14db

# 查看文件类型
git cat-file -t 69b0bd
> commit
# 查看文件内容
git cat-file -p 69b0bd
> tree d6c639c45454a483b6f0e5fa6828d45f3ca5c7f3
> author Pioneer4 <electricalqzhang@gmail.com> 1636025068 +0800
> committer Pioneer4 <electricalqzhang@gmail.com> 1636025068 +0800
>
> init prj
# 查看tree中的内容
git cat-file -p d6c639
> 100644 blob 053baa2271d8de75b8621f08ae6fc6694bf31593    readme.md
> 040000 tree a10a826fc98d5263841dacf5c66014bb1f7f4acd    src
# 查看readme.md中的内容
git cat-file -p 053baa
> init project

由此可以分析,Git Object中存储的对象和关系如下:

HEAD---> refs/heads/master--> 69b0bd(commit)
                                    +
                                    |
                                    v
                                d6c639(tree)
                                    +
                                    |
                          +---------+----------+
                          |                    |
                          v                    v
                     053baa(blob)         a10a82(tree)
                       readme.md              src
                                               +
                                               |
                                               v
                                          8d0e41(blob)
                                             db.log   
  1. 创建分支dev,并在dev上提交内容
# 基于master分支创建dev分支
git branch dev
# 切换至dev分支
git checkout dev
# 查看此时.git/refs的目录结构
tree .git/refs
>
.git/refs/
├── heads
│   ├── dev
│   └── master
└── tags
# 查看commit对象的id,由于dev分支刚从master分支copy出来,所以commit_id相等
cat .git/refs/heads/master  .git/refs/heads/dev
> 69b0bd61b54155b5ff6ed45847e4e01767ca14db
> 69b0bd61b54155b5ff6ed45847e4e01767ca14db

# 在dev分支添加内容
echo "electrical" >> readme.md 
echo "gcc -o main main.c" >> Makefile
git add .
git commit -m "add Makefile"
cat .git/refs/heads/master  .git/refs/heads/dev
> 69b0bd61b54155b5ff6ed45847e4e01767ca14db
> 3d64d5b0bf15a2e59a68854011ac9ee1771f164f

此时,Git Object中存储的对象和关系如下:

                                          (parent)
HEAD--> refs/heads/work--> 3d64d5(commit) +------> 69b0bd(commit)<--refs/heads/master
                              +                             +
                              |                             |
                              v                             v
                         ecf0a4(tree)                   d6c639(tree)
                              +                             +
                              |                             |
               +-----------------------------+     +--------+-----------+
               |              |              |     |                    |
               v              v              v     v                    v
           501a93(blob)    98c9be(blob)    a10a82(tree)            053baa(blob)
      readme.md (version 2)  Makefile         src               readme.md (version 1)
											   +
											   |
                                               v
                                          8d0e41(blob)
											 db.log 

二、本地仓库

2.1 必会操作

2.1.1 初始化

如果要管理文件demo,则通过命令行进入demo文件里,输入

git init

2.1.2 基础配置

2.1.2.1 用户信息

--system--global--local三者的区别:

# 1、系统层面的配置,配置信息写入git安装目录下的etc/gitconfig
git config --system
# 2、全局层面(当前用户层面)的配置,配置信息写入~/.config
git config --global 
# 3、仓库层面的配置,配置信息写入当前仓库下的.git/config,是默认的配置方式
git config --local

配置生效的优先级:仓库层面(local) > 用户层面(global) > 系统层面(system)

因此,只要通过git config --local对仓库A进行了配置,仓库A就会使用该配置,而忽略用户层面、和系统层面的配置。

最佳实践:每次git init初始化完本地仓库后,通过git config --local配置用户名和邮箱信息,例如:

git config --local user.name 'Pioneer4'
git config --local user.email 'electricalqzhang@gmail.com'
2.1.2.2 资源范围管理(.gitignore)

.gitignore是一个文本文件,用于管理仓库的资源范围,它告诉Git仓库要忽略项目中的哪些文件或文件夹。换句话说,如果xxx被记录到.gitignore,那仓库就不会管xxx,把你当做不存在。

下面是go语言项目开发时,.gitignore的基础配置示例:

# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
2.1.2.3 密码设置

git config --system --unset credential.helper 重置

git config --global credential.helper store 设置

2.1.3 内容提交

git status 查看工作区状态。也就是说,工作区中任何的内容变更,通过此指令都会被查询出来

git add . 将工作区变更的内容添加到index(暂存区)

git commit -m "优化质检事件ES查询性能" 将暂存区中的内容提交到本地仓库

2.1.4 临时贮藏工作区

# 将工作区的内容贮藏起来
git stash 
# 查看贮藏的内容
git stash list
# 之前的内容覆盖工作区 stash中的内容还在   
git stash apply
# stash中的内容不见了
git stash pop

2.1.5 回退

2.1.5.1 指令

soft、mixed、hard区别

指令本地仓库暂存区工作区
git reset --soft××
git reset --mixed×
git reset --hard
  • git reset --soft:仅在本地仓库移动指针
  • git reset --mixed:本地库移动指针,并重置暂存区
  • git reset --hard:本地库移动指针,重置暂存区,重置工作区

基于索引值commit_id
git reset --hard commit_id
基于符号^
git reset --hard HEAD^ 回退一步
基于符号~
git reset --hard HEAD~3 回退3步

checkout指令:

  • git checkout [文件名]:恢复工作区中某一个文件的内容(删除某一个文件的改动),让该文件内容变为和上次提交时一样。
  • git checkout .:恢复工作区的内容(删除所有文件的改动)

注意:比如现在有一个项目,你对项目当前版本已有的文件的内容(1)进行了修改或(2)删除,并且未添加至暂存区,则可以通过git checkout .删除这些改动。但是,一旦提交到暂存区,或者是在项目里创建了新的文件,使用git checkout .无法删除这些改动。所以最佳方式还是需要使用下面提到的两步法(先add再reset)

2.1.5.2 典型场景
  • 将暂存区的内容移除

git reset HEAD readme.md 将暂存区中的readme.md文件移除,相当于git add readme.md的逆操作

git reset HEAD . 将暂存区中所有内容移除

  • 由于错误操作,在工作区中添加了一些错误的内容,希望将工作区重置

git checkout .

  • 由于错误操作,在工作区中不仅添加了内容,而且还创建了新的文件,此时希望将工作区重置
# 第1步:将工作区的内容添加到暂存区
git add .
# 第2步:使用--hard的方式回退版本,同时修正暂存区和工作区
git reset --hard HEAD

说明:虽然说git reset --hard可以同时修正本地仓库、暂存区、工作区的指针,但目前来说,在工作区创建新的文件后,如果仅仅使用该条指令,发现新建的文件还在工作区,不能达到重置工作区的目的。所以,对于要重置工作区的最佳实践就是两步法(先add再reset):先通过git add .将所有变更内容加入暂存区,再通过git reset --hard HEAD进行版本回退。

2.1.6 版本信息

2.1.6.1 查询提交日志
  • git log 最完整的形式

  • git log --oneline 显示commit_id前7位和注释

  • git log --pretty=oneline 显示commit_id和注释

  • git log --all --graph 显示版本图

  • git reflog 显示commit_id前几位和注释以及到当前版本的步数。

git reflog的重要使用场景:

因为reflog会保存commit、checkout、reset的记录,所以有一个重要应用场景:如果由于误操作,使用git reset --hard xxx将仓库从current版本回退到xxx版本后,此时,通过git log --oneline只能查询到xxx及其之前的版本。如果想再次回到current版本,则需要通过git reflog来查询记录,找到current版本的commit_id,最终通过git reset --hard current版本的commit_id返回到current版本。

2.1.6.2 内容比较
# 比较当前版本(HEAD)与临近的第1个版本(HEAD~1)的差异
git diff HEAD~1 HEAD
# 比较当前版本(HEAD)与临近的第2个版本(HEAD~2)的差异
git diff HEAD~2 HEAD
# 比较临近的第一个版本(HEAD~1)与临近的第两个版本(HEAD~2)的差异
git diff HEAD~2 HEAD~1

# 工作区与当前版本的差异比较
git diff HEAD
# 工作区与上一版本的差异比较
git diff HEAD~1

# 工作区与暂存区的比较
git diff

# 暂存区与当前版本的比较 
git diff --cached
# 暂存区与上一个版本的比较 
git diff HEAD~1 --cached

# 比较master分支和dev分支的差异
git diff dev master

语法小提示:在比较当前版本(HEAD)与临近的第1个版本(HEAD~1)的差异时,指令为git diff HEAD~1 HEADHEAD~1在前,HEAD在后,的意思就是以HEAD~1为基准,查看HEAD这个版本发生的内容变化。

2.1.6.3 图形化工具
gitk --all
gitk界面
图/fastjson的gitk界面

2.1.7 commit message

2.1.7.1 message修改
  • 修改当前版本commit的message

git commit --amend

该操作更进一层的理解是通过创建新提交来替换当前版本的提交。所以使用该指令后,当前版本的message和commit_id都将发生变化。

  • 修改老旧commit的message

git rebase -i base_commit_id

例如:

git log --oneline
> 3ff36f6 (HEAD -> temp) v3.0
> 78d7e97 v2
> 4c21794 v1
> 6d6314a 添加教程
> fcb8826 add .gitignore
> 459f866 delete log
# 第1步:若想修改4c21794的内容,则需要rebase到它的父commit,也就是6d6314a
git rebase -i 6d6314a
# 第2步:在弹出的commit message信息框(vi文本)中,将要修改版本前的pick修改为r;然后会继续弹出信息框,此时修改message,最后wq保存退出

# 修改完成后,可以看出6d6314a之后的commit的id和message都发生了变化
> ca31d40 (HEAD -> temp) v3.0
> d7ffc44 v2.0
> 3fd7822 v1.0
> 6d6314a 添加教程
> fcb8826 add .gitignore
> 459f866 delete log
2.1.7.2 commit 合并

git rebase -i base_commit_id 和修改commit message使用的指令相同,只是在提示框中输入的信息不同

# 操作前
git log --oneline
> ca31d40 (HEAD -> temp) v3.0
> d7ffc44 v2.0
> 3fd7822 v1.0
> 6d6314a 添加教程
> fcb8826 add .gitignore
> 459f866 delete log

第1步:如果想合并v1.0、v2.0、v3.0这三个请求,需要选取 6d6314a 这个版本作为基

git rebase -i 6d6314a

得到以下界面:

commit_message合并1

第2步:根据显示的Commands提示信息可以知道,如果要融合(meld)多个版本提交,需要使用squash命令(也可简写为s)替换pick,替换后如下:

注意 v1.0作为基础 它的pick不能替换

commit_message合并2

第3步:将pick改为s后,输入:wq,则会进入以下这个页面

commit_message合并3

第4步:在v1.0那个位置添加合并后的commit message信息

commit_message合并4

第5步:输入:wq保存后,得到如下反馈信息

[detached HEAD 89cccf0] feature-search complete
 Date: Fri Nov 5 15:44:46 2021 +0800
 1 file changed, 3 insertions(+)
Successfully rebased and updated refs/heads/temp.

再次通过git log --oneline查询提交日志,可看出v1.0、v2.0、v3.0成功的合并为了 “feature-search complete”

commit_message合并5

2.2 分支

2.2.1 查看、创建、切换和删除

  1. 查看

    • git branch -a 查看当前所在分支,以及所有分支的name
    • git banch -v 查看当前所在分支,以及所有本地分支的name、commit_id、message
  2. 创建

    git branch xxx 以当前分支为基,创建新的分支xxx。如:git branch dev表示以当前分支为基础,创建dev分支;

  3. 切换

    git checkout dev 切换至dev分支

  4. 删除

    • git branch -d fix 如果fix已经与当前分支merge,则可以成功删除
    • git branch -D fix 如果fix没有与当前分支merge,则使用"-D"强制删除

    提示:如果有一个fix分支,已经与dev分支合并,目前项目处于master分支,如果使用git branch -d fix是无法删除fix分支的,因为fix并未merge至master分支;如果当前处于dev分支,则可以使用git branch -d fix删除fix分支。

2.2.2 分支合并

# 如果希望将dev分支合并至master分支,则分两步走:
# 1、切换至master分支
git checkout master
# 2、合并dev分支
git merge dev
# (1)如果没有冲突,则会弹出vi信息框(第1行就是默认的message),可输入":wq"直接commit;
# (2)有冲突,则手动解决冲突后再commit;

2.3 标签

  1. 查看

    git tag -l 查看本地仓库tag列表

    git show-ref --tags 查看本地仓库tag列表

    git ls-remote --tags 查看远程仓库tag列表

  2. 创建

    git tag -a [tag_name] -m "release version v1.2" [commit_id] 加上-a参数来创建一个带备注的tag,备注信息由-m指定。如果你未传入-m则创建过程系统会自动为你打开vi编辑器让你填写备注信息

    git push origin [tag_name] 将标签名为[tag_name]的tag同步到远程仓库

  3. 删除

    git tag -d [tag_name] 删除本地仓库中标签名为[tag_name]的tag

    git push origin :refs/tags/[tagName] 删除远程仓库中标签名为[tag_name]的tag

三、远程仓库

3.1 与本地仓库协同

3.1.1 创建

3.1.1.1 本地仓库未创建

第1步:在远程仓库中创建项目

第2步:将远程仓库中的项目clone到本地

3.1.1.2 本地仓库已创建

第1步:在远程仓库中创建项目,(注意:远程仓库中的项目名最好与本地仓库的项目名一致)

第2步:git push [origin] --all -f 强制推送本地仓库所有的分支到远程仓库[origin]中,由于为了保证远程仓库的安全性,通常会设置禁止强制推送选项,也就是禁止使用-f参数。此时,可以通过一下步骤达到同样的目的,以main分支为例:

  1. 首先需要了解到远程仓库的分支有“default”的标识,通常就是main分支,所以,需要将default标识设置到其它分支;
  2. 在远程仓库web端中删除main分支;
  3. 本地仓库中,使用git push origin main,将本地仓库main分支推送到远程仓库;
  4. 为了main分支的安全,重新将"default"标识设置为main分支;

3.1.2 remote增删改查

git remote -v 查看保存的远程仓库地址

git remote show <远程仓库名> 查看该远程仓库的详细信息

git remote rm <远程仓库名> 用于删除远程仓库名

git remote add test https://github.com/Pioneer4/test.git 添加远程仓库名和远程仓库地址

git remote rename <原远程仓库名> <新远程仓库名> 用于远程仓库的改名

git remote prune <远程仓库名> 清理远程仓库中已经不存在的分支

3.1.3 push & pull & fetch

语法特征:分支推送或拉取顺序的写法是git push/pull/fetch [remote_repo_name] <source>:<dsetination>,所以拉取是git pull [remote_repo_name] <远程分支>:<本地分支>,而推送git push [remote_repo_name] <本地分支>:<远程分支>

  1. push

    git push origin main 推送main分支
    git push <远程仓库名> <本地分支名>:<远程分支名> 命令用于将本地仓库分支的更新,推送到远程仓库

    • 如果省略远程分支名,则表示将本地分支推送与之存在"追踪关系"的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。
      git push origin master 上面命令表示,将本地的master分支推送到origin主机的master分支。如果后者不存在,则会被新建。
    • 如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支
    # 删除远程仓库origin的man分支。注意:这里只是做演示,通常远程仓库默认是不允许用户删除main分支的,况且用户也不应该删除main分支!
    $ git push origin :man
    # 等同于
    $ git push origin --delete man
    
  2. pull

    git pull <远程仓库名> <远程分支名>:<本地分支名> 拉取远程仓库某个分支的更新,更新本地仓库,暂存区、工作区。

    提示:git pull origin main这条命令等用于fetchmerge两条命令的组合

    1. git fetch origin main
    2. git merge origin/main
  3. fetch

    git fetch <远程仓库名> 将某个远程仓库中的所有分支拉回本地仓库。更新本地仓库中<远程仓库名>/路径下的所有分支,不更新本地main、dev等分支。

    git fetch <远程仓库名> <分支名> 将某个远程仓库的某个分支拉回,相当于 <远程仓库名>/<分支名>更新,但本地仓库的<分支名>不更新。

场景:git pullgit fetch的区别演示

fetch & merge
# 1、在用户A的工作区中添加内容,然后add、commit、push。
touch battery.log && git add . && git commit -m "Add battery log" && git push rcxn main:main
# 2、在用户B的工作区中fetch更新
git fetch rcxn main
# 3、查看本地main分支提交日志,发现如任何更新
git log --oneline
# 4、切换至分支rcxn/main,查看更新日志,可发现已经收到更新日志"Add battery log"
git checkout rcxn/main && git log --oneline
# 5、切换至本地main分支,和并分支rcxn/main,最后查看日志可知,main中也更新了"Add battery log"
git checkout main && git merge rcxn/main && git log --oneline

pull
# 使用pull,就会直接拉取远程仓库分支并合并
git pull rcxn main

3.1.4 commit message修改

message修改首先在本地仓库中进行,方式前面已经介绍过。修改完成后,使用-f参数强制推送至远程仓库即可

git push rcxn dev:dev -f 将修改message后的dev分支强制推送至远程仓库rcxn

3.2 规范流程

  1. 找到问题:提出Issue;
  2. Issue讨论OK,提交PR(Pull Request)或者MR(Merge Request);
  3. CodeReview后合并;

四、参考

git 维基百科

git 官网

Pro Git (Second Edition)

git内存储管理

Git for Teams

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值