Git Notes
Git
相关介绍、常用命令总结及使用过程中的遇到的相关问题记录
Git相关知识
Git工作区域
flowchart LR work[["`Workspace 工作区`"]] stage[("`Stage 暂存区`")] repo[("`Repository 版本库`")] remote[("`Remote 远程仓库`")] work-->|git add|stage stage-->|"`git commit git commit --amend`"|repo stage-->|git restore --staged|work repo-->|git push|remote repo-->|git reset --soft|stage repo-->|git reset --mixed|work repo-->|git commit --amend|repo remote-->|git fetch|repo remote-->|git pull| work remote-->|git clone| workflowchart LR work[["`Workspace 工作区`"]] stage[("`Stage 暂存区`")] repo[("`Repository 版本库`")] remote[("`Remote 远程仓库`")] work-->|git add|stage stage-->|"`git commit git commit --amend`"|repo stage-->|git restore --staged|work repo-->|git push|remote repo-->|git reset --soft|stage repo-->|git reset --mixed|work repo-->|git commit --amend|repo remote-->|git fetch|repo remote-->|git pull| work remote-->|git clone| work
- 工作区:就是在电脑里能看到的目录。
- 版本库:工作区有一个隐藏目录
.git
,这个不算工作区,而是 Git 的版本库。 - 暂存区:英文叫stage或index。对应
.git
目录下的index
文件。 - 远程仓库:托管代码的服务器,
Github
等。
文件状态
flowchart LR untracked([untracked]) modified(["`modified tracked`"]) staged([staged]) committed([committed]) delete[Deletion] undo["`Undo changes to modified files`"] untracked & modified-->|git add|staged staged-->|git commit|committed staged-->|git restore --staged|untracked & modified modified-.->|git restore|undo untracked-.->|git clean|delete committed-.->|git rm|delete modified & staged-.->|git rm -f |delete modified & staged & committed -->|git rm --cached|untrackedflowchart LR untracked([untracked]) modified(["`modified tracked`"]) staged([staged]) committed([committed]) delete[Deletion] undo["`Undo changes to modified files`"] untracked & modified-->|git add|staged staged-->|git commit|committed staged-->|git restore --staged|untracked & modified modified-.->|git restore|undo untracked-.->|git clean|delete committed-.->|git rm|delete modified & staged-.->|git rm -f |delete modified & staged & committed -->|git rm --cached|untracked
- 未跟踪(untrack):表示文件为新增加的。
- 已修改(modified/tracked):表示修改了文件,但还没保存到git仓库中。
- 已暂存(staged):表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
- 已提交(committed):表示文件已保存在git仓库中。
Git命令
多数命令有参数-v/--verbose
,会打印更详细的信息。
相关概念
-
HEAD
代表了当前工作目录所指向的当前分支的最新提交。HEAD^
表示上一个版本,HEAD^^
表示上上一个版本,^
个数不限,几个^
表示落后HEAD
几个版本;HEAD~5
表示落后HEAD
5个版本。 -
origin
是一个远程仓库的默认名称,这个名称并不是强制的,它代表了远程仓库的地址。origin main
和origin/main
含义相同,表示远程仓库的main
分支。再比如origin/HEAD
表示远程仓库默认分支的最新提交,origin v1.0.0
表示远程仓库tag为v1.0.0
的那次提交。下文中远程仓库的名称统一用origin
代替。 -
上游分支:本地分支一般会与远程仓库的某个分支相关联,关联的这个远程分支就称作是这个本地分支的上游分支。
git config
Git配置级别主要有以下3类:
-
仓库级别local【优先级最高】,对应的文件是仓库下的
.git/config
-
用户级别global【优先级次之】,对应的文件是
~/.gitconfig
-
系统级别system【优先级最低】
|
|
注意
使用git config
设置或者删除某个配置时,不带--system
和--global
参数,配置的就是仓库级别
git init
|
|
git clone
将远程仓库克隆到本地
|
|
|
|
git clone
加了参数--depth
之后,执行git fetch --unshallow
可获取完整的仓库历史记录。
|
|
当使用git clone
命令克隆一个包含子模块的仓库时,Git会克隆主仓库,但不会自动克隆子模块目录中的内容。子模块目录会保留,但会是空的,或者包含指向提交对象的占位符(通常是一个特殊的提交,表示子模块指向的特定版本)。当在git clone
命令中加上--recursive
参数时,Git不仅会克隆主仓库,还会自动地初始化并克隆仓库中所有的子模块。
如果已经克隆了一个仓库但没有使用--recursive
参数,可以在之后通过git submodule update --init --recursive
命令来初始化并更新子模块。
git add
将文件放入暂存区
|
|
git commit
将暂存区中文件提交到版本库
|
|
git push
将版本库中推导远程仓库
|
|
|
|
这个命令会推送所有本地分支到远程仓库,如果远程仓库中已经存在同名分支,它们将被更新;如果不存在,则会在远程仓库中创建与本地分支同名的新分支。各个本地分支是否有上游分支、其上游分支是不是与本地分支同名,这些对该命令都没有影响。
|
|
将本地branch_name
分支推到远程仓库的branch_name
分支上,并设置当前分支的上游分支为远程仓库的branch_name
分支。
|
|
将本地local-branch
分支推到远程仓库的remote-branch
分支上,并设置当前分支的上游分支为远程仓库的branch_name
分支。
注意
先git branch --set-upstream-to
再git push
,与git push -u
某些情况下等效。但是git branch --set-upstream-to=origin/branch_name
要求origin/branch_name
是已存在的分支。而git push -u
对于空仓库也可以,它会为这个空仓库创建这个分支并将本地相关分支推上去,同时设置上游分支。
git remote
|
|
注意
git remote add origin
只是关联远程仓库地址,没有获取远程仓库分支的信息;此时运行git branch -r
就会发现为空(即使关联的是一个非空仓库),运行命令git branch --set-upstream-to=origin/main
也会报错(即使远程仓库存在main
分支)。
git fetch
获取远程仓库最新信息。比如远程仓库已经有一些新的提交,git pull
是直接下载最新代码到本地,git fetch
不会下载最新的代码,只是获取仓库的最新信息,git fetch
之后可通过git status
看到落后上游分支,也可通过git log origin/branch_name
查看最新log
信息。
|
|
|
|
比如git clone --depth 1
只会下载最近一次提交记录,现在需要完整的仓库历史记录,可以使用 git fetch --unshallow
使其包含完整的提交历史。
git pull
|
|
如果当前分支不存在上游分支,需要先设置一下上游分支再git pull
,或者用git pull origin main
指定远程仓库名称和远程分支名称。
%%{init: {'gitGraph': {'mainBranchName': 'origin/main'}} }%% gitGraph commit id: "C1" commit id: "C2" branch main commit id: "C3" checkout origin/main commit id: "C5" checkout main commit id: "C4" checkout origin/main commit id: "C6" checkout main%%{init: {'gitGraph': {'mainBranchName': 'origin/main'}} }%% gitGraph commit id: "C1" commit id: "C2" branch main commit id: "C3" checkout origin/main commit id: "C5" checkout main commit id: "C4" checkout origin/main commit id: "C6" checkout main
如果远程分支和本地分支处于不同的提交节点,直接git pull
不行,需指定git pull
的行为方式
|
|
|
|
git pull
过程中可能会遇到冲突。git pull --rebase
解决冲突的方法与git rebase
解决冲突的方法一样,git pull --no-rebase
解决冲突的方法与git merge
解决冲突的方法一样。
|
|
以参数--rebase, --no-rebase, --ff-only
的形式指定git pull
的行为的优先级高于配置文件指定git pull
的行为。
git log
|
|
|
|
gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" commit id: "C4" checkout main commit id: "C5" commit id: "C6"gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" commit id: "C4" checkout main commit id: "C5" commit id: "C6"
|
|
git reflog
|
|
git reflog
显示了你在过去所做的所有Git操作的日志,包括提交、分支切换、重置等。而git log
是查看提交历史。
git branch
|
|
注意
远程分支不能像本地分支一样直接重命名,可先用git push origin :branh_name
删除远程分支,再将本地分支推到远程仓库。
|
|
注意
git branch --set-upstream-to=origin/branch_name
要求远程仓库branch_name
分支必须存在,并且本地有远程仓库branch_name
分支的信息;也就是说比如通过git remote add origin
关联了一个包含branch_name
分支的远程仓库之后,还得使用git fetch
命令获取branch_name
分支的信息,这时git branch --set-upstream-to=origin/branch_name
这条命令才会成功。
git checkout
|
|
注意
git checkout branch_name
如果本地分支branch_name
不存在,但远程仓库分支branch_name
存在,则会自动创建本地分支branch_name
并设置上游分支;但如果远程仓库分支branch_name
也不存在,那么该命令就会报错。
|
|
git switch
git switch
专注于切换分支的功能,相比git checkout
在专注性和安全性方面更胜一筹。切换分支只是git checkout
的一部分功能。
|
|
注意
git switch branch_name
如果本地分支branch_name
不存在,但远程仓库分支branch_name
存在,则会自动创建本地分支branch_name
并设置上游分支;但如果远程仓库分支branch_name
也不存在,那么该命令就会报错。
|
|
git tag
|
|
注意
git tag v1.0.0
给当前分支的最新提交添加标签。添加标签时,工作区存在未跟踪的文件、已修改未暂存的文件、暂存区有未提交的文件都没有关系。
git restore
|
|
git clean
git clean
命令在 Git 版本控制系统中用于移除当前工作目录中未跟踪的文件和目录。使用 git clean
时需要谨慎,因为它会永久删除这些文件。
-f
或--force
:强制执行清理操作,不询问确认。-i
或--interactive
:以交互模式运行,让用户选择要删除的文件。-n
或--dry-run
:模拟执行,显示将要删除的文件列表,但不实际删除它们。-d
:同时删除未跟踪的目录。默认情况下,git clean
只删除文件。
|
|
git rm
git rm
用于将已跟踪的文件变成未跟踪状态或删除已跟踪的文件,未跟踪的文件不能用 git rm 删除, 可用 git clean 命令进行删除。
|
|
手动删除已跟踪的文件,删除文件的操作记录是在工作区;git rm
删除文件后,删除文件的操作记录是在暂存区;git rm
相当于是手动删除文件后并执行git add
操作。而只是想停止跟踪文件而不删除文件,就可以使用git rm --cached
。
git mv
|
|
git mv
相当于就是手动重命名文件后再git add
git reset
|
|
git reset --hard
将HEAD
指针移动到指定的提交,同时丢弃暂存区和工作目录中的所有更改。所有文件的内容与指定的提交保持一致,不会删除未跟踪的文件。
git reset --soft
将 HEAD 指针移动到指定的提交,但保留暂存区和工作目录的更改。所有文件的内容与git reset --soft
之前还是一样的,除了git reset --soft
之前的暂存区和工作目录的更改,其他文件与指定提交间的差异将放到暂存区。
git reset --mixed
将 HEAD 指针移动到指定的提交,保留工作目录的更改,但清空暂存区。所有文件的内容与git reset --mixed
之前还是一样的,所有文件与指定提交间的差异都将放到工作区。
git reset
不带--soft
、--mixed
或--hard
选项,那么默认的行为是--mixed
。
git revert
git revert
用于撤销已提交更改。与 git reset
不同,git revert
不会改变项目的提交记录,而是通过创建新的提交来回滚之前的更改。
|
|
gitGraph commit id: "C1" commit id: "C2" commit id: "C3" commit id: "C4" commit id: "C5" commit id: "C6"gitGraph commit id: "C1" commit id: "C2" commit id: "C3" commit id: "C4" commit id: "C5" commit id: "C6"
如上图,当前分支处于C6
处,执行git revert C2^...C5
后,状态如下
gitGraph commit id: "C1" commit id: "C2" commit id: "C3" commit id: "C4" commit id: "C5" commit id: "C6" commit id: "C5-1" commit id: "C4-1" commit id: "C3-1" commit id: "C2-1"gitGraph commit id: "C1" commit id: "C2" commit id: "C3" commit id: "C4" commit id: "C5" commit id: "C6" commit id: "C5-1" commit id: "C4-1" commit id: "C3-1" commit id: "C2-1"
git revert
过程中可能会遇到冲突,会提示冲突的文件,手动修改完冲突文件的内容后,再git add
和git revert --continue
就可以;或者git revert --abort
放弃git revert
操作。
git diff
|
|
注意
git diff
和git diff file_path
是工作区中的文件与暂存区或版本库的比较,不包括暂存区与版本库的比较。
git diff branch1 branch2 file_path
是版本库上该文件的比较,git diff branch_name file_path
是当前分支上的该文件(在版本库、工作区或暂存区都可以)与指定分支版本库之间的比较。
git show
|
|
git merge
gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" commit id: "C4" checkout main commit id: "C5" commit id: "C6" merge dev id: "C7" commit id: "C8" checkout dev commit id: "C9"gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" commit id: "C4" checkout main commit id: "C5" commit id: "C6" merge dev id: "C7" commit id: "C8" checkout dev commit id: "C9"
|
|
如上图,如果在main
分支上git merge dev
后,在main
分支上会生成一次新的提交C7
,该次提交包含了C3
和C4
所做的修改,新的提交C8
是基于C7
的。但切回dev
分支再提交C9
,C9
是基于C4
的而不是C7
。
git merge
遇到冲突时,会提示冲突的文件,手动修改完冲突文件的内容后,再git add
和git commit
就可以;或者git merge --abort
放弃合并。
git rebase
gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" checkout main commit id: "C5" checkout dev commit id: "C4" checkout main commit id: "C6" commit id: "C3-1" commit id: "C4-1"gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" checkout main commit id: "C5" checkout dev commit id: "C4" checkout main commit id: "C6" commit id: "C3-1" commit id: "C4-1"
|
|
如上图,当main
分支最新提交是C6
,dev
分支最新提交是C4
时,在dev
分支上执行git rebase main
后,dev
分支上的提交C3
和C4
会按顺序整合到C6
后面,变成新的提交C3-1
和C4-1
,dev
分支会来到C4-1
处,但main
分支还是会在C6
处。使用git rebase
后新的最新节点的内容和使用git merge
后生成的新的提交节点的内容是一致的。
git rebase
遇到冲突时,会提示冲突的文件,手动修改完冲突文件的内容后,再git add
和git rebase --continue
就可以;或者git rebase --abort
放弃合并。
git cherry-pick
git cherry-pick
用于将某个特定的一个或多个提交从一个分支应用到当前分支。
gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" checkout main commit id: "C5" checkout dev commit id: "C4" checkout main commit id: "C6" commit id: "C7" commit id: "C8"gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" checkout main commit id: "C5" checkout dev commit id: "C4" checkout main commit id: "C6" commit id: "C7" commit id: "C8"
比如当前提交情况如上图,当前处于dev
分支上
|
|
该命令将C5
和C7
的更改应用到当前分支上,运行完该命令状态如下,git cherry-pick
了几个提交,当前分支就会多几个对应的提交:
gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" checkout main commit id: "C5" checkout dev commit id: "C4" checkout main commit id: "C6" commit id: "C7" commit id: "C8" checkout dev commit id: "C5-1" commit id: "C7-1"gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" checkout main commit id: "C5" checkout dev commit id: "C4" checkout main commit id: "C6" commit id: "C7" commit id: "C8" checkout dev commit id: "C5-1" commit id: "C7-1"
|
|
运行该命令状态如下:
gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" checkout main commit id: "C5" checkout dev commit id: "C4" checkout main commit id: "C6" commit id: "C7" commit id: "C8" checkout dev commit id: "C5-1" commit id: "C6-1" commit id: "C7-1"gitGraph commit id: "C1" commit id: "C2" branch dev commit id: "C3" checkout main commit id: "C5" checkout dev commit id: "C4" checkout main commit id: "C6" commit id: "C7" commit id: "C8" checkout dev commit id: "C5-1" commit id: "C6-1" commit id: "C7-1"
git cherry-pick
过程中可能会遇到冲突,会提示冲突的文件,手动修改完冲突文件的内容后,再git add
和git cherry-pick --continue
就可以;或者git cherry-pick --abort
放弃git cherry-pick
操作。
git stash
|
|
注意
未跟踪的文件、已修改未暂存的文件、暂存区中的文件都会保存。但是如果当前只有未跟踪的文件,需要--include-untracked
参数才能保存,否则会提示没有要保存的文件。
|
|
一个使用场景是,当想切换分支做其它工作时,需先保存一个stash,之后切换回来再应用保存的stash即可。
git submodule
|
|
当在git仓库中添加子模块后,仓库根目录下会新增文件.gitmodules
,该文件记录了每个子模块的信息,示例如下。此外.git/config
和.git/modules
也会有相应的改变。
|
|
|
|
更新子模块也可以进入到子模块对应的目录下,执行git fetch
、git pull
等操作。
需要应用子模块指定提交,进入到子模块文件夹,执行git reset
等操作即可。
|
|
Git LFS
将大文件从本地提交到Github仓库需要使用git-lfs
,见官网:https://git-lfs.github.com
.gitignore
|
|
更多详细内容见官网:https://git-scm.com/docs/gitignore
常见报错
Failed to connect
Failed to connect to github.com port 443 after 75002 ms: Couldn’t connect to server
在使用了VPN时,配置http代理,如下,7890
是代理的端口号,打开ClashX
可以查看使用的端口号
|
|
RPC failed
RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL (err 8)
git clone
遇到该问题时,有时再运行一遍git clone
命令就可以- 指定使用
http 1.1
,git config --global http.version HTTP/1.1
RPC failed; curl 18 transfer closed with outstanding read data remaining
- 使用
git clone
时,可加参数--depth 1
- 将仓库链接由HTTPS方式改成SSH
- 增大缓冲区,
git config --global http.postBuffer 536870912
- 多尝试几遍