Skip to content

Git

一、Git 基础

基础配置

配置 user.name 和 user.email

js
git config --global user.name 'your_name'
git config --global user.email 'your_email'
git config --global user.name 'your_name'
git config --global user.email 'your_email'

config 的三个作用域

缺省等同于 local

js
git config --local // local 只对某个仓库有效
git config --global // global 只对当前用户所在仓库有效
git config --system // system 对系统所有登录的用户有效
git config --local // local 只对某个仓库有效
git config --global // global 只对当前用户所在仓库有效
git config --system // system 对系统所有登录的用户有效

显示 config 的配置,加 --list

js
git config --list --local
git config --list --global
git config --list --system
git config --list --local
git config --list --global
git config --list --system

创建 git 仓库

已存在项目

js
cd [your_project]
git init
cd [your_project]
git init

新建项目

js
git init [your_project]
git init [your_project]

提交信息

js
git commit -m "init"
git commit -m "init"
js
git add [file_name]
git add [file_name]
js
git log
git log

文件重命名

基础操作

js
mv readme readme.md
mv readme readme.md
js
git status

git add readme.md
git rm readme

git status
git status

git add readme.md
git rm readme

git status

简化操作

js
git reset --hard // 清理暂存区所有工作信息
git reset --hard // 清理暂存区所有工作信息
js
git mv readme readme.md // 等同于上述操作,简化操作
git mv readme readme.md // 等同于上述操作,简化操作
js
git commit -m "perf: move reame to readme.md"
git commit -m "perf: move reame to readme.md"

查看版本演变历史

js
git log
git log
js
git log --oneline // 查看只包含提交信息的记录(一行简洁方式)
git log --oneline // 查看只包含提交信息的记录(一行简洁方式)
js
git log -n4	--oneline // 最近的 n 次
git log -n4	--oneline // 最近的 n 次
js
git branch -v // 查看本地分支
git branch -v // 查看本地分支
js
git log --all // 查看所有分支的日志信息
git log --all // 查看所有分支的日志信息
js
git log --all --graph // 图形化的日志信息
git log --all --graph // 图形化的日志信息
js
git log --oneline --all -n4 // 所有分支最近的 4 次提交记录
git log --oneline --all -n4 // 所有分支最近的 4 次提交记录
js
git log --oneline --all -n4 --graph // 所有分支最近的 4 次图形化提交记录
git log --oneline --all -n4 --graph // 所有分支最近的 4 次图形化提交记录
js
git help --web log // 查看 web 界面的 git log文档
git help --web log // 查看 web 界面的 git log文档

图形界面工具

js
gitk // 弹出 git 自带的图形化工具

gitk --all // 查看所有信息
gitk // 弹出 git 自带的图形化工具

gitk --all // 查看所有信息

.git 目录

文本文件,指向当前分支。

js
ref: refs/heads/master
ref: refs/heads/master

config

js
[core]
	repositoryformatversion = 0
	filemode = false
	bare = false
	logallrefupdates = true
	symlinks = false
	ignorecase = true
[remote "origin"]
	url = git@github.com:iheora/notes.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master
[gui]
	wmstate = normal
	geometry = 1061x563+96+96 233 255
[core]
	repositoryformatversion = 0
	filemode = false
	bare = false
	logallrefupdates = true
	symlinks = false
	ignorecase = true
[remote "origin"]
	url = git@github.com:iheora/notes.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master
[gui]
	wmstate = normal
	geometry = 1061x563+96+96 233 255

refs

refs/tags 针对重要版本,可以打标签

refs/heads 本地分支文件夹

refs/remotes 远程分支文件夹

git cat-file -t 查看对象类型 git cat-file -p 查看对象内容 git cat-file -s 查看对象大小

js
cd refs/heads

cat .\master // aa4854071ab2fec2a08c9fcc58dfe939b09d6e9a

git cat-file -t aa4854071ab2fec2a08c9fcc58dfe939b09d6e9a // commit 类型

git branch -av // * master aa48540 [ahead 1] docs: update
cd refs/heads

cat .\master // aa4854071ab2fec2a08c9fcc58dfe939b09d6e9a

git cat-file -t aa4854071ab2fec2a08c9fcc58dfe939b09d6e9a // commit 类型

git branch -av // * master aa48540 [ahead 1] docs: update

master 指针指向 commit 。master 后 存在一个 hash 值,如果 hash 值足够标识唯一性,会截断进行显示。

tag 包含的是 git 对象,指向 commit。

objects

objects 是 git 文件系统核心内容,存在多个目录。

2个字符的文件夹,以 e8 为例。下面存在多个文件。

-- 0b9ebd39f834f536c1aaa96df56074ad5481ae -- 3b4ce5e9a33e0b41fe922d28093460085fe12d

js
// 拼接规则:e8 + 文件名

git cat-file -t e83b4ce5e9a33e0b41fe922d28093460085fe12d 
// blob 可能存在多种文件类型 blob(文件对象),commit,tree 等,

git cat-file -p e83b4ce5e9a33e0b41fe922d28093460085fe12d // 查看文件内容
// 拼接规则:e8 + 文件名

git cat-file -t e83b4ce5e9a33e0b41fe922d28093460085fe12d 
// blob 可能存在多种文件类型 blob(文件对象),commit,tree 等,

git cat-file -p e83b4ce5e9a33e0b41fe922d28093460085fe12d // 查看文件内容

pack 目录

git 会做打包处理。

commit、tree 和 blob 关系

git commit,commit 对象,一个 commit 肯定会对应一棵树(tree)。

树相当于一个快照,存储当时 git 操作的所有信息。

js
tree
-- tree
-- blob
-- blod
-- tree  // styles


tree // styles
-- blob // logo.png,对应 blob 二进制文件
tree
-- tree
-- blob
-- blod
-- tree  // styles


tree // styles
-- blob // logo.png,对应 blob 二进制文件

blob 对应一个文件。只要文件内容相同,git 都将之视为一个 blob。可以节约内存空间。 git 文件和文件名没有关系,会根据文件内容生成 blob。

commit 可以被看做是 tree 的根节点。

文件生成 blob 对象后,再对文件进行修改,git 会把松散的 blob 做整理,把内容相近的 blob 做增量存储。

数一数 tree 的个数

新建的 Git 仓库,有且仅有 1 个 commit,仅包含 /doc/readme,请问内部含有多少个 tree,多少个 blob?

js
git init watch_git_objects

cd watch_git_objects

mkdir doc

cd doc

echo "hello world" > readme

cd ..

find .git/objects -type -f // 空目录

git add doc

git status // 添加到暂存区,没有 commit

find .git/objects -type -f
// .git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad
// 加入暂存区,git 会创建 blob 文件

git cat-file -t 3b18e512db // blob

git commit -m "add readme"

find .git/objects -type -f 
// .git/object/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad	blob
// .git/object/f5/5a12e98ffd80349f3499cc52a06b8afb93ec90  tree
// .git/object/25/0bfe7259457d05a1e29ced793fac74dc10e47f  commit
// .git/object/2c/5264370f2630da80ee38f412213d59657af6e8	tree
// 4 个
git init watch_git_objects

cd watch_git_objects

mkdir doc

cd doc

echo "hello world" > readme

cd ..

find .git/objects -type -f // 空目录

git add doc

git status // 添加到暂存区,没有 commit

find .git/objects -type -f
// .git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad
// 加入暂存区,git 会创建 blob 文件

git cat-file -t 3b18e512db // blob

git commit -m "add readme"

find .git/objects -type -f 
// .git/object/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad	blob
// .git/object/f5/5a12e98ffd80349f3499cc52a06b8afb93ec90  tree
// .git/object/25/0bfe7259457d05a1e29ced793fac74dc10e47f  commit
// .git/object/2c/5264370f2630da80ee38f412213d59657af6e8	tree
// 4 个

commit ,两个 tree 一个是 doc 目录,一个是 readme 文件,blob 是文件内容。

分离头指针情况的注意事项

detached HEAD。

js
git checkout 415c5c8 // 切换分支的时候,切换成 commit
git checkout 415c5c8 // 切换分支的时候,切换成 commit

分离头指针的情况下,可以继续做开发,然后 commit,不会影响其他分支。

js
提交 commit 时,git 会提示 HEAD detached at 415c5c8,即没有依赖分支做操作,只依赖 commit。

git commit -am "update" // 不经过暂存区,直接提交
提交 commit 时,git 会提示 HEAD detached at 415c5c8,即没有依赖分支做操作,只依赖 commit。

git commit -am "update" // 不经过暂存区,直接提交

分支头指针其实就是说正工作在一个没有分支的状态下,如果这时候切换其他分支而不保存,就会丢失已变更信息。

当你想做一些变更,只是尝试性的一些变更,如果发现效果不好,可以随时丢弃掉。

这时就可以使用分离头指针的现象,直接 checkout 切换到新的分支即可。

js
gitk --all 

// 图形化工具不会显示任何关于分离头指针情况的信息记录
// git 眼中如果 commit 没和 branch 或者 tag 绑定,就是无效的提交信息,很可能会被 git 当作垃圾清理
// 可以根据切换分支时的提示,对 commit 记录建立新分支
gitk --all 

// 图形化工具不会显示任何关于分离头指针情况的信息记录
// git 眼中如果 commit 没和 branch 或者 tag 绑定,就是无效的提交信息,很可能会被 git 当作垃圾清理
// 可以根据切换分支时的提示,对 commit 记录建立新分支

HEAD 和 branch

js
git checkout -b fix_readme master // 基于 master 分支创建新分支并切换

git log -n1 // HEAD -> fix_readme
cat .git/HEAD // ref: refs/heads/fix_readme
git checkout -b fix_readme master // 基于 master 分支创建新分支并切换

git log -n1 // HEAD -> fix_readme
cat .git/HEAD // ref: refs/heads/fix_readme

HEAD 可以指代新分支的最后一次提交,也可以不和分支挂钩,和 commit 挂钩,即分离头指针的状态。

当分支切换时,HEAD 会指定新的分支。

HEAD 最终还是指向 commit,HEAD 指向分支的时候,分支里面的内容指向的还是 commit,即最后一次 commit。 不管是处于分离头状态,还是正常状态,HEAD 最后都会指向最新一次提交的 commit。

比较 commit 差异

js
git diff 3d4731 415c5c8 // 比较两个 commit
git diff 3d4731 415c5c8 // 比较两个 commit
js
git diff HEAD HEAD~1 // 比较当前及前一次提交
git diff HEAD HEAD^^ // 比较当前及 HEAD 的前两次提交

// ~ 和 ^ 两种用法都可以
git diff HEAD HEAD~1 // 比较当前及前一次提交
git diff HEAD HEAD^^ // 比较当前及 HEAD 的前两次提交

// ~ 和 ^ 两种用法都可以

二、Git 个人开发使用场景

删除不需要的分支

js
gitk --all
gitk --all
js
git branch -d [branch_name] // 删除本地分支
git branch -D [branch_name] // 强制删除本地分支
git branch -d [branch_name] // 删除本地分支
git branch -D [branch_name] // 强制删除本地分支
js
git branch -a // 显示全部分支
git branch -v // 本地分支 详细信息
git branch -a // 显示全部分支
git branch -v // 本地分支 详细信息

修改最新 commit 的 message

js
git commit --amend // 允许修改最新 commit 的 message
git commit --amend // 允许修改最新 commit 的 message

修改旧的 commit 的 message

js
git rebase -i [commit parent hash] 

git rebase -i 50234f8e848555b4995849e1c7564scd6b32b506d
// 团队协作时禁止使用,如果代码已经提交到远程分支,代码合并时会产生新的提交记录
git rebase -i [commit parent hash] 

git rebase -i 50234f8e848555b4995849e1c7564scd6b32b506d
// 团队协作时禁止使用,如果代码已经提交到远程分支,代码合并时会产生新的提交记录
bash
pick bb4b896 docs: update words
pick 1ae4b2e perf: 二分查找的实现及特性
pick 6e3f289 perf: git 修改最新提交信息

# Rebase 50234f8..6e3f289 onto 50234f8 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.


->

r bb4b896 docs: update words
pick 1ae4b2e perf: 二分查找的实现及特性
pick 6e3f289 perf: git 修改最新提交信息
pick bb4b896 docs: update words
pick 1ae4b2e perf: 二分查找的实现及特性
pick 6e3f289 perf: git 修改最新提交信息

# Rebase 50234f8..6e3f289 onto 50234f8 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.


->

r bb4b896 docs: update words
pick 1ae4b2e perf: 二分查找的实现及特性
pick 6e3f289 perf: git 修改最新提交信息
bash
docs: update words

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Mon Jan 17 08:00:10 2022 +0800
#
# interactive rebase in progress; onto 50234f8
# Last command done (1 command done):
#    reword bb4b896 docs: update words
# Next commands to do (4 remaining commands):
#    pick 1ae4b2e perf: 二分查找的实现及特性
#    pick 6e3f289 perf: git 修改最新提交信息
# You are currently editing a commit while rebasing branch 'master' on '50234f8'.
#
# Changes to be committed:
#       modified:   alg/training/README.md
#

->

docs: update word # 保存即可
docs: update words

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Mon Jan 17 08:00:10 2022 +0800
#
# interactive rebase in progress; onto 50234f8
# Last command done (1 command done):
#    reword bb4b896 docs: update words
# Next commands to do (4 remaining commands):
#    pick 1ae4b2e perf: 二分查找的实现及特性
#    pick 6e3f289 perf: git 修改最新提交信息
# You are currently editing a commit while rebasing branch 'master' on '50234f8'.
#
# Changes to be committed:
#       modified:   alg/training/README.md
#

->

docs: update word # 保存即可

变基操作实际上也使用分离头指针操作。

合并连续多个 commit

js
git rebase -i [parent_commit]
git rebase -i [parent_commit]

使用 s,squash,meld into pervious commit。

bash
pick 992e00c docs: update docs
pick d94225d perf: merge two update:
pick 442ccb0 perf: update

# Rebase 1d556bc..442ccb0 onto 1d556bc (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
pick 992e00c docs: update docs
pick d94225d perf: merge two update:
pick 442ccb0 perf: update

# Rebase 1d556bc..442ccb0 onto 1d556bc (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.

=>

bash
pick 992e00c docs: update docs
s d94225d perf: merge two update:
s 442ccb0 perf: update

# Rebase 1d556bc..442ccb0 onto 1d556bc (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
pick 992e00c docs: update docs
s d94225d perf: merge two update:
s 442ccb0 perf: update

# Rebase 1d556bc..442ccb0 onto 1d556bc (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#

=>

bash
# This is a combination of 3 commits.
perf: git merge commit test
# This is the 1st commit message:

docs: update docs

# This is the commit message #2:

perf: merge two update:

perf: update

perf: update

# This is the commit message #3:

perf: update

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Mon Jan 17 22:02:12 2022 +0800
#
# interactive rebase in progress; onto 1d556bc
# Last commands done (3 commands done):
#    squash d94225d perf: merge two update:
#    squash 442ccb0 perf: update
# No commands remaining.
# You are currently rebasing branch 'master' on '1d556bc'.
#
# Changes to be committed:
#       modified:   git/README.md
#
# This is a combination of 3 commits.
perf: git merge commit test
# This is the 1st commit message:

docs: update docs

# This is the commit message #2:

perf: merge two update:

perf: update

perf: update

# This is the commit message #3:

perf: update

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Mon Jan 17 22:02:12 2022 +0800
#
# interactive rebase in progress; onto 1d556bc
# Last commands done (3 commands done):
#    squash d94225d perf: merge two update:
#    squash 442ccb0 perf: update
# No commands remaining.
# You are currently rebasing branch 'master' on '1d556bc'.
#
# Changes to be committed:
#       modified:   git/README.md
#

=>

[detached HEAD c3657c8] perf: git merge commit test
 Date: Mon Jan 17 22:02:12 2022 +0800
 1 file changed, 39 insertions(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.
[detached HEAD c3657c8] perf: git merge commit test
 Date: Mon Jan 17 22:02:12 2022 +0800
 1 file changed, 39 insertions(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.

合并间隔的 commit

js
git log --graph // 查看 Git 提交日志
git log --graph // 查看 Git 提交日志
js
git rebase -i [commit_hash]	

// 如果是根 commit 不显示,可以自己添加 commit_hash,或者使用 git rebase -i --root
git rebase -i [commit_hash]	

// 如果是根 commit 不显示,可以自己添加 commit_hash,或者使用 git rebase -i --root
js
pick [commit_hash] // 
pick [commit_hash]
s [commit_hash_123]

=>

pick [commit_hash]
s [commit_hash_123] // 可以自主移动位置
pick [commit_hash]
pick [commit_hash] // 
pick [commit_hash]
s [commit_hash_123]

=>

pick [commit_hash]
s [commit_hash_123] // 可以自主移动位置
pick [commit_hash]

比较暂存区和 HEAD 文件差异

js
git diff --cached
git diff --cached

=>

bash
diff --git a/git/README.md b/git/README.md
index fbb6644..ff14cc2 100644
--- a/git/README.md
+++ b/git/README.md
@@ -604,6 +604,37 @@ Successfully rebased and updated refs/heads/master.

 ### 合并间隔的 commit

+```js
+git log --graph // 查看 Git 提交日志
+```
+
+```js
+git rebase -i [commit_hash]    
+
+// 如果是根 commit 不显示,可以自己添加 commit_hash,或者使用 git rebase -i --root
+```
+
+```js
+pick [commit_hash] // 
+pick [commit_hash]
+s [commit_hash_123]
+
+=>
+
+pick [commit_hash]
+s [commit_hash_123] // 可以自主移动位置
+pick [commit_hash]
+```
+
+### 比较暂存区和 HEAD 文件差异
+
+```js
+git diff --cached // 比较暂存区和 HEAD
+```
diff --git a/git/README.md b/git/README.md
index fbb6644..ff14cc2 100644
--- a/git/README.md
+++ b/git/README.md
@@ -604,6 +604,37 @@ Successfully rebased and updated refs/heads/master.

 ### 合并间隔的 commit

+```js
+git log --graph // 查看 Git 提交日志
+```
+
+```js
+git rebase -i [commit_hash]    
+
+// 如果是根 commit 不显示,可以自己添加 commit_hash,或者使用 git rebase -i --root
+```
+
+```js
+pick [commit_hash] // 
+pick [commit_hash]
+s [commit_hash_123]
+
+=>
+
+pick [commit_hash]
+s [commit_hash_123] // 可以自主移动位置
+pick [commit_hash]
+```
+
+### 比较暂存区和 HEAD 文件差异
+
+```js
+git diff --cached // 比较暂存区和 HEAD
+```

比较工作区和暂存区文件差异

js
git diff
git diff
bash
diff --git a/git/README.md b/git/README.md
index 17423f7..a146534 100644
--- a/git/README.md
+++ b/git/README.md
@@ -675,6 +675,7 @@ index fbb6644..ff14cc2 100644
 ### 比较工作区和暂存区文件差异

 ```js
+git diff
diff --git a/git/README.md b/git/README.md
index 17423f7..a146534 100644
--- a/git/README.md
+++ b/git/README.md
@@ -675,6 +675,7 @@ index fbb6644..ff14cc2 100644
 ### 比较工作区和暂存区文件差异

 ```js
+git diff
js
git diff -- .\git\README.md // 可以指定文件进行对比
git diff -- .\git\README.md // 可以指定文件进行对比

将暂存区恢复到 HEAD

应用场景:已经确定不想保留暂存区的变更,其次工作区还没有改好,这时又想把暂存区恢复到 HEAD。

js
git reset HEAD // 重置暂存区内容

git reset --soft // 把 HEAD 指向的 commit 恢复到你指定的 commit,暂存区、工作区不变
git reset --hard // 把 HEAD,暂存区,工作区 都修改为 你指定的 commit 的时候的文件状态
git reset --mixed // 默认参数,把 HEAD,暂存区 修改为 你指定的 commit 的时候的文件状态,工作区保持不变
git reset HEAD // 重置暂存区内容

git reset --soft // 把 HEAD 指向的 commit 恢复到你指定的 commit,暂存区、工作区不变
git reset --hard // 把 HEAD,暂存区,工作区 都修改为 你指定的 commit 的时候的文件状态
git reset --mixed // 默认参数,把 HEAD,暂存区 修改为 你指定的 commit 的时候的文件状态,工作区保持不变

将工作区恢复为暂存区

js
git checkout ./
git checkout ./

Git 2.23 之后用 git switch 和 git restore 来替代 git checkout 功能。 git switch 替换 git checkout 切换分支的功能,git restore 替换对工作区文件进行恢复的功能。

取消暂存区部分文件更改

js
git reset HEAD ./styles/style.css
git reset HEAD ./styles/style.css

消除最近的几次提交

js
git reset --hard [commit_hash] // 强制回退
git reset --hard [commit_hash] // 强制回退

比较不同提交文件差异

bash
# 比较 master、develop 分支 README 文件的差异
git diff master develop ./README.md
# 比较 master、develop 分支 README 文件的差异
git diff master develop ./README.md
bash
# 比较不同提交记录文件的差异,可以指定不同分支的 commit
git diff [commit_hash] [commit_hash]
# 比较不同提交记录文件的差异,可以指定不同分支的 commit
git diff [commit_hash] [commit_hash]

master、develop 其实也是一种 commit,指向最近的一次提交。

正确删除文件的方法

bash
git rm ./README.md
git rm ./README.md

开发中临时加塞紧急任务

bash
# 保存当前工作区和暂存区内容,此时工作区和暂存区会恢复到 HEAD
git stash 

# 查看栈中的存储列表
git stash list

# 弹出保存内容,不移除栈中信息
git stash apply

# 弹出保存内容,移除栈中信息
git stash pop
# 保存当前工作区和暂存区内容,此时工作区和暂存区会恢复到 HEAD
git stash 

# 查看栈中的存储列表
git stash list

# 弹出保存内容,不移除栈中信息
git stash apply

# 弹出保存内容,移除栈中信息
git stash pop

指定不需要 Git 管理文件

.gitignore

bash
# 不管理 js 及其子目录
*.js

# 不管理 js 子目录
*.js/
# 不管理 js 及其子目录
*.js

# 不管理 js 子目录
*.js/

提交 commit 后,想再忽略一些已经提交的文件,可以这样处理: 把想忽略的文件添加到 .gitignore ;然后通过 git rm -- cached name_of_file 的方式删除掉 git 仓库里面无需跟踪的文件。

将 git 仓库备份到本地

bash
# 备份仓库(哑协议,不存在进度条)
git clone --bare xxxxxxx/.git test.git

# 备份仓库(智能协议,存在进度条)
git clone --bare file:///xxxxxxx/.git test2.git
# 备份仓库(哑协议,不存在进度条)
git clone --bare xxxxxxx/.git test.git

# 备份仓库(智能协议,存在进度条)
git clone --bare file:///xxxxxxx/.git test2.git
bash
# 将本仓库推送到备份仓库
git remote add test2 file:///xxxxxxx/.test2.git 
git push --set-upstream test2
# 将本仓库推送到备份仓库
git remote add test2 file:///xxxxxxx/.test2.git 
git push --set-upstream test2

--bare 克隆裸仓库。 备份仓库只克隆版本库(.git)就可以了,节约空间,如果以后想恢复备份的仓库,用版本库就可以恢复出工作目录。

三、Git 多人协作使用场景

多人单分支

多人修改不同文件

bash
git clone [ssh_url] [project_name] # 克隆远端仓库,本地创建指定项目名称
git clone [ssh_url] [project_name] # 克隆远端仓库,本地创建指定项目名称
bash
git config --local user.name "heora" # 设置本地用户名称
git config --local user.email "yueluo.yang@qq.com" # 设置本地邮箱地址
git config --local --list # 查看本地配置
git config --local user.name "heora" # 设置本地用户名称
git config --local user.email "yueluo.yang@qq.com" # 设置本地邮箱地址
git config --local --list # 查看本地配置
bash
git fetch # 拉取远端分支
git fetch # 拉取远端分支
bash
git branch -av # 查看所有分支
git branch -av # 查看所有分支
bash
git merge [remote_branch] # 合并远端分支

or

git pull # 拉取远端分支内容
git merge [remote_branch] # 合并远端分支

or

git pull # 拉取远端分支内容

多人修改同文件不同区域

bash
git pull # 同步远端代码
git pull # 同步远端代码

or

bash
git fetch # 拉取远端分支
git merge # 合并远端分支
git fetch # 拉取远端分支
git merge # 合并远端分支

多人修改同文件同区域

bash
git pull # 同步远端代码,发生内容冲突

vim index.html # 根据提示直接打开文件进行修改,这里假设是 index.html

git status # 查看 merge 是否完成

git commit # 如果确实已经合并完毕
or
git merge --abort # 如果不想 merge,重新退回,这时可以丢弃 merge 的代码
git pull # 同步远端代码,发生内容冲突

vim index.html # 根据提示直接打开文件进行修改,这里假设是 index.html

git status # 查看 merge 是否完成

git commit # 如果确实已经合并完毕
or
git merge --abort # 如果不想 merge,重新退回,这时可以丢弃 merge 的代码

同时变更文件内容和文件内容

bash
git pull # git 可以感知到文件名变化,直接合并,可以很智能的处理
git pull # git 可以感知到文件名变化,直接合并,可以很智能的处理

同一文件改成不同文件

bash
git pull # 本地文件名已修改,线上也被修改,拉取代码时会发生冲突

git status # 查看 git 状态及提示信息,根据提示进行操作
git pull # 本地文件名已修改,线上也被修改,拉取代码时会发生冲突

git status # 查看 git 状态及提示信息,根据提示进行操作

集成分支使用禁忌

git push -f

强制提交,会覆盖线上已有内容。

bash
git push -f # git push --force
git push -f # git push --force

gitlab、github 都提供了禁止使用这条命令的机制。(非必要比建议使用)

git rebase

变基会修改历史记录,可能会覆盖其他成员的劳动成果(心怀不轨),会导致其他成员的代码提交报错(指针前移的情况)。

bash
git reabse
git reabse

四、Git 与 Github

Github 初识

GIt 通过引入 fork 概念使得开源项目的合作变得容易,但是 Git 仍有局限性。 Git 无法帮助开发人员寻找开源项目,许多程序员开发了优秀的开源项目,也很难被别人发现。

Github 核心功能

  • Code Review
    • comment
  • Project management 项目管理
    • issue
    • cards board
  • Integrations 项目集成
    • provided by a third companies
  • Team management 团队管理
  • Social coding 开源
    • follow projects
    • explore your intersets
    • share your achievement
  • Documentation 文档、静态站点、wikis
    • Github Pages
    • Wikis 说明手册
  • Code hosting
    • 代码托管

高效搜索项目

Advanced search 高级搜索。

https://github.com/search?q=&type=Repositories

假设搜索 Git 相关内容:

js
// 如果直接输入名称关键字,查询的只是仓库名称和描述信息。

git 学习资料
// 如果直接输入名称关键字,查询的只是仓库名称和描述信息。

git 学习资料
js
// 可以指定查询范围

git 学习资料 in:readme
git 学习资料 in:readme stars:>1000
// 可以指定查询范围

git 学习资料 in:readme
git 学习资料 in:readme stars:>1000

根据高级搜索面板可以自己组合关键字。

Github 支持 code 和 repository options 同时搜索

如何保证开源项目质量

Pull Request,可以给开源项目提交申请,也可以审查别人的申请。

Code Review。

为何需要组织类型的仓库

settings - organizations,可以创建组织。

存在以下功能:

  • Repositories
  • People manage
  • Teams:对仓库进行精细化的管控
  • Projects
  • Settings

可以对团队成员进行权限管控,组织成员可以看到所有仓库(无权限时)以及所有团队,可以自主申请加入团队。

Github 与 Git 简单同步

配置 SSH 公私钥

bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # 一直回车即可
ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # 一直回车即可
bash
cat ~/.ssh/id_rsa.pub # 查看公钥,粘贴到 Github(SSH and GPG Keys)
cat ~/.ssh/id_rsa.pub # 查看公钥,粘贴到 Github(SSH and GPG Keys)

本地仓库同步到 Github

create repository,将本地代码同步到远端仓库。

首次创建仓库

bash
git remote add [custom_name] [remote_ssh_url]	# 添加远程仓库地址
git remote -v # 查看远端地址

git pull # 如果远端仓库已存在内容,需要先拉取代码	git pull => git fetch + merge

git push --set-upstream origin master
git remote add [custom_name] [remote_ssh_url]	# 添加远程仓库地址
git remote -v # 查看远端地址

git pull # 如果远端仓库已存在内容,需要先拉取代码	git pull => git fetch + merge

git push --set-upstream origin master

拉取分支合并

bash
git remote -v # 查看远程版本库信息
git remote add githup <url> # 添加githup远程版本库
git fetch githup # 拉取远程版本库
git merge -h # 查看合并帮助信息
git merge --allow-unrelated-histories githup/master # 合并githup上的master分支(两分支不是父子关系,所以合并需要添加 --allow-unrelated-histories)
git push githup # 推送同步到githup仓库
git remote -v # 查看远程版本库信息
git remote add githup <url> # 添加githup远程版本库
git fetch githup # 拉取远程版本库
git merge -h # 查看合并帮助信息
git merge --allow-unrelated-histories githup/master # 合并githup上的master分支(两分支不是父子关系,所以合并需要添加 --allow-unrelated-histories)
git push githup # 推送同步到githup仓库

rebase 情况,不使用 merge

bash
1)先把远端的分支 fetch到本地,然后,再执行 rebase
2)直接 git pull --rebase
1)先把远端的分支 fetch到本地,然后,再执行 rebase
2)直接 git pull --rebase

Github 团队协作

创建团队项目

Create a new repository - Owner(select organization)- private type ,其他选择可以任意。

settings - Collaborators & teams,角色权限控制,可以建一个组织进行测试。

选择适合团队的工作流

需要考虑的因素

  • 团队人员的组成
  • 研发设计能力
  • 输出产品的特征
  • 项目难易程度

主干开发

适用于开发团队系统设计和开发能力强。有一套有效的特性切换的实施机制,保证上线后无需修改代码就能修改系统行为。 适用于组件开发的团队,成员能力强,人员少,沟通顺畅。用户升级组件成本低的环境。

适用范围比较少。

Git Flow

适用于不具备主干开发能力。有预定的发布周期,需要执行严格的发布流程。

Github Flow

适用于不具备主干开发能力。随时集成随时发布:分支集成时经过代码评审和自动化测试,就可以立即发布。

Gitlab Flow

适用于不具备主干开发能力。无法控制准确的发布时间,但又要求不停地集成。

选择合适的分支集成策略

Insigts - Network 可以查看分支情况

Settings - MergeButton

  • Allow merge commits:
  • Allow squash merging:
  • Allow rebase merging :允许线性的合并、期望历史记录就是一条线

Pull requests - new pull request

=> compare -> base

=> create pull request

=> merge pull request,select merge way

  • create a merge commit
  • squash and merge
  • rebase and merge

启用 issue 跟踪需求和任务

Issues 面板

  • label 标签
  • milestones 里程碑

create issues。

vue/.github/_.md,vue 提供了很多 issue 模板,可以参考使用。

使用 project 管理 issue

Projects - New Project - Create project (可以选择不同模板)

项目内部实施 Code Review

Settings - Branches - Branch protection rules 、add rule

建立一个 Pull Request,可选中 Reviewers 人员,被添加人员会受到邮件通知。Review 通过后,也会通知开源人员。

如何保证集成质量

项目做好工程化配置,做好代码检查和提交信息审核。

Settings - Branches - Branch protection rules,配置 codereview 集成检查、CI 要求。

Marketplace 中提供了很多第三方服务,如 Travis CI、Codecov 、jenkins 等。

Settings - Integrations & services - Installed Github Apps 可以查看已安装服务。

发布产品包 release

需要借助 CI 工具,打包完毕自动添加到 releases 中。

以 Travis CI 为例。

yaml
deploy:
	provider: releases
	api_key: $github_oauth_token
	file: xxx
	skip_cleanup: true
	on:
		branch: master
deploy:
	provider: releases
	api_key: $github_oauth_token
	file: xxx
	skip_cleanup: true
	on:
		branch: master

Settings - Installed Github Apps - Travis CI - Configure。

配置完毕,重新构建即可。

travis ci 前端工程化

项目增加指导文档

Github Wiki 功能,可以用 markdown 编写。Wiki 其实也是一个仓库。

vue wiki https://github.com/vuejs/vue/wiki。