📖Git
全局信息配置 config
安装 Git 后,使用以下全局配置命令设置你的用户名和电子邮件地址。
1 | $ git config --global user.name "用户名" |
--global
表示配置适用于该电脑上的所有仓库,如果移除该选项,配置只对当前仓库生效。
初始化仓库 init
使用 cd
命令进入项目文件夹(工作目录),然后运行初始化命令,将当前文件夹转换为 Git 可管理的仓库。此时 Git 会在当前文件夹中创建一个隐藏的文件夹 .git/
用于监视内容更改。
1 | $ git init |
新创建的
.git/
文件夹又称之为版本库,包括暂存区、默认分支master
、指向master
分支的指针HEAD
文件状态 status
- 未跟踪文件:工作目录中的文件没有被 Git 管理。
- 未修改文件:工作目录中的文件内容与 Git 版本库中的文件内容相同。
- 已修改文件:工作目录中的文件内容与 Git 版本库中的文件内容不同。
- 已暂存文件:文件已被修改,并且这些修改已被暂存到 Git 暂存区,但尚未提交到 Git 版本库。
工作目录 & 仓库:在 Git 中,工作目录是你实际操作和编辑文件的地方,而仓库是 Git 存储和管理文件版本历史的地方。工作目录包含当前的项目文件,而仓库包含这些文件的版本控制信息。
当文件第一次被添加到工作目录(Current Directory)时,其状态为未跟踪(Untracked)。此时,文件虽然存在于工作目录中,但尚未被添加到仓库,因此 Git 无法监视其变化。通过将文件暂存,可以将其状态变为已跟踪(Tracked)。此时,文件被添加到仓库中,Git 可以监视其变化。
使用以下命令查看当前仓库的状态信息。
1 | $ git status |
git status
常用于显示 ① 工作目录中新增的未跟踪的文件(Untracked files) ② 已修改或删除但是未暂存的文件(Changes not staged for commit) ③ 已暂存但未提交的文件(Changes to be committed) ④ 当前分支状态
git status --short
以一种紧凑的方式查看文件做了哪些更改,对于不同的文件使用不同的符号标记其状态。??
表示未跟踪的文件,A
表示新增加的文件,M
表示修改了的文件,D
表示删除了的文件。上述两种命令用于显示工作目录和暂存环境的状态信息。
版本管理
暂存文件 add
文件只有添加到暂存环境(Staging Environment)后才能被 Git 监视,此时文件的状态为已暂存(Staged)。当我们添加、修改、删除文件时,都需要将文件添加到暂存环境。这是由于暂存的作用包括:① 将工作目录中新创建的文件添加到暂存环境,从而纳入版本控制。② 将已跟踪但是修改了的文件的当前内容添加到暂存环境。 ③ 将已跟踪但是删除了的文件的删除操作添加到暂存环境。总的来说,暂存用于将工作目录中的更改(新增文件、修改文件、删除文件)添加到暂存环境,以便在后续提交时包含这些更改。
使用以下命令暂存单个文件。
1 | $ git add <filename> |
使用以下命令暂存所有文件。
1 | $ git add -all |
1 | $ git add * |
1 | $ git add -A |
提交修改 commit
暂存(stage)的下一步就是提交(commit),前者将更改(新增、修改、删除)添加到暂存环境,后者将暂存环境的内容提交到 Git 仓库中,并形成一个新的提交。这个提交可以看作是项目的一个存档点(save point),通过提交记录可以实现版本回退和管理。
使用以下命令提交暂存环境的内容并包含描述文字。
1 | $ git commit -m "描述文字" |
使用以下命令提交已跟踪文件的所有更改并包含描述文字。该操作会自动将已跟踪文件的所有更改暂存,然后提交。注意:新增文件无法通过这种方式直接提交;因为可能会涉及到不必要的暂存操作(有些更改我们并不需要暂存),不建议使用这种提交方式。
1 | $ git commit -a -m "描述文字" |
查看差异 diff
使用以下命令查看指定文件工作区和暂存区的差异。
1 | $ git diff <filename> |
查看提交日志 log
可以使用以下命令查看从近到远的提交日志,其中每个提交都有自己的 commit id
。
1 | $ git log |
1 | $ git log --pretty=oneline |
--pretty=oneline
可以让每个提交日志以一行输出。
1 | $ git log --graph --pretty=oneline --abbrev-commit |
--graph
可以以图的形式显示分支和版本合并历史。
--abbrev-commit
表示显示简短的提交哈希(commit id)。
版本回退 reset|reflog
使用以下命令回退到指定版本,可以使用 commit id
,也可以使用 HEAD^
、HEAD^^
、HEAD~100
来指定要回退的版本。
1 | $ git reset <version> |
1 | $ git reset --hard <version> |
在 Git 中,
HEAD
表示当前版本,HEAD^
表示上一个版本,HEAD^^
表示上上一个版本,HEAD~100
表示上 100 个版本。
--hard
:完全回退到指定的提交,丢弃所有未提交的更改。
--soft
:回退到指定的提交,但保留所有更改在暂存区中,可以直接重新提交。
--mixed
:回退到指定的提交,保留工作目录中的更改,但将暂存区清空,需要重新暂存更改。(默认)
在回退到历史版本后,如果想要恢复到未来某个版本,那么就需要对应版本的 commit id
,此时可以通过以下命令查看命令历史,从而找到对应版本的 commit id
。
1 | $ git reflog |
撤销修改 checkout|reset
使用以下命令将文件恢复到暂存区的状态,即撤销在工作目录的修改。
1 | $ git checkout -- <filename> |
如果文件修改后未暂存,撤销修改将使工作目录恢复到最新提交的状态;如果文件在暂存后又被修改,撤销修改将使工作目录恢复到暂存时的状态。简而言之,撤销修改会将文件恢复到最近一次
git commit
或git add
后的状态。
使用以下命令将文件从暂存区移除,保留修改在工作目录,即撤销在暂存区的修改。
1 | $ git reset HEAD <filename> |
注意,此时修改被保留在工作目录,但是未暂存。
文件删除 rm
使用以下命令同时从工作目录和暂存区中移除指定文件,相当于从工作目录删除文件后,再将文件的删除操作添加到暂存区。
1 | $ git rm <filename> |
撤销删除 checkout|reset
使用以下命令撤销工作目录误删的文件为最新提交的版本。
1 | $ git checkout -- <filename> |
使用以下命令撤销误删且已被暂存的文件为最新提交的版本。
1 | $ git reset HEAD <filename> |
远程仓库
创建 SSH
使用以下命令创建 SSH key,生成一对公钥 id_rsa.pub
和私钥 is_rsa
,将公钥添加到 Github 后,此时本地版本库便可以和 Github 上的远程仓库进行加密通信了。
1 | $ ssh-keygen -t rsa -C "youremail@example.com" |
添加远程库 remote add
在 Github 创建好一个空的远程库后,使用以下命令为本地版本库添加远程仓库。
1 | $ git remote add origin git@github.com:<username>/<remote-repo-name>.git |
这里
origin
是远程仓库的名称,后边的xxx.git
是远程仓库的地址。
添加好远程仓库后,使用以下命令将本地库指定分支的所有内容推送到远程仓库的同名分支。
1 | $ git push -u origin <branch-name> |
这里
-u / --set-upstream
用于将本地分支与远程仓库的同名分支关联起来(设置本地分支的上游分支),origin
是远程仓库的名称,master
是本地分支的名称。
在做好上述准备后,使用以下命令将本地库指定分支的所有内容推送到远程仓库的同名分支。
1 | $ git push origin <branch-name> |
这里
origin
是远程仓库的名称,master
是本地分支的名称。
删除远程库 remote rm
在删除远程库前,使用以下命令查看远程库信息。
1 | $ git remote -v |
使用以下命令删除指定远程库。
1 | $ git remote rm <remote-repo-name> |
注意,这里的删除其实是解除本地仓库和远程仓库的关联关系,并没有在物理上删除远程仓库。
克隆远程库 clone
使用以下命令将远程库克隆到本地。
1 | $ git clone git@github.com:<username>/<remote-repo-name>.git |
远程仓库地址支持 SSH 协议,也支持 HTTPS 协议,前者更快。
分支管理
Git 版本管理实际上是对提交(commit)的管理。在 Git 中,所有的提交构成一条时间线,这条时间线即被称为一个分支(branch)。
- 在默认情况下,Git 只有一条主分支,原名为
master
,现已更名为main
。每个分支实际上是一个指针,它指向该时间线上的最新提交。同样,HEAD
也是一个指针,但它指向当前分支。 - 创建新分支实际上就是创建一个新指针,这个新指针会指向当前分支的最新提交。切换分支其实就是改变
HEAD
的指向,让HEAD
指向另一个分支,从而实现分支切换。 - 最基本的分支合并,也被称为"fast forward"合并,就是将一个分支(比如分支 A)指向另一个分支(比如分支 B)的最新提交,这样就完成了将分支 B 合并到分支 A 的操作。
创建分支 checkout -b
使用以下命令创建分支,并切换到该分支。
1 | $ git checkout -b <branch-name> |
上述命令等价于以下两条命令
- 创建分支
git branch <branch-name>
- 切换分支
git checkout <branch-name>
使用以下命令创建分支,并切换到该分支。
1 | $ git switch -c <branch-name> |
上述命令等价于以下两条命令
- 创建分支
git branch <branch-name>
- 切换分支
git switch <branch-name>
查看分支 branch
使用以下命令查看所有分支,其中当前分支使用 *
号标注。
1 | $ git branch |
使用以下命令查看所有远程分支。
1 | $ git fetch -all # 确保本地仓库的远程仓库分支信息是最新的 |
合并分支 merge
使用以下命令合并指定分支到当前分支。
1 | $ git merge <branch-name> |
git merge
操作可能会创建一个新的合并提交(commit),这个提交会包含两个父提交,分别是当前分支的最新提交和被合并分支的最新提交。
在Git中,分支合并主要有三种情况:fast-forward 合并、非 fast-forward 合并和合并冲突。
Fast-forward 合并:当当前分支是目标分支的直接祖先时,Git 只需将当前分支的指针移动到目标分支的最新提交。在这种情况下,Git 不会创建新的合并提交。删除被合并的分支后,无法在历史中看到分支的存在和合并情况!
非 Fast-forward 合并:当当前分支不是目标分支的直接祖先时,Git 需要考虑两个分支的历史。在这种情况下,Git 会创建一个新的合并提交,包含两个分支的所有变更。
合并冲突:当两个分支在同一文件的同一位置有不同的更改时,Git 无法自动决定应该使用哪个版本,此时会产生合并冲突。我们需要手动解决这些冲突,编辑并保存文件,然后使用
git add
和git commit
命令创建一个新的合并提交。
假设我们有两个分支:
master
和dev
,且当前活动分支(即HEAD
指向的分支)是master
。当我们执行git merge dev
命令时,Git 的处理方式可能会有所不同。对于下边的左图,Git 可以快速合并(fast-forward),而对于下边的右图,Git 合并时会产生合并冲突(conflict)。Git 合并产生的冲突需要我们手动解决,即编辑出现冲突的文件,将其修改为我们期望的样子,并保存。然后,我们使用git add
命令将这些修改添加到暂存区,接着使用git commit
命令创建一个新的合并提交,以完成合并操作。
1 | git merge --no-ff -m "描述文字" <branch-name> |
--no-ff
表示禁用 Fast-forward 合并,此时所有合并都会生成合并提交。
删除分支 branch -d
使用以下命令删除指定分支。
1 | $ git branch -d <branch-name> |
使用以下命令强制删除指定分支。
1 | $ git branch -D <branch-name> |
隐藏更改 stash
在切换分支之前,最好确保工作目录和暂存区是干净的,以避免潜在的冲突。你可以通过以下两种方法来实现:
- 提交所有更改:将当前分支的所有更改提交,以确保分支处于干净状态。
- 使用
git stash
:临时隐藏当前的更改,将其保存在一个栈结构中,使分支保持干净。稍后,当你切换回该分支时,可以应用这些更改。
使用以下命令隐藏当前分支在工作目录和暂存区中的更改,使分支保持干净(入栈)。
1 | $ git stash |
使用以下命令查看所有被隐藏的更改(查看栈中所有元素)。
1 | $ git stash list |
使用以下命令恢复被隐藏的更改,但不从栈中删除(使用栈顶元素或栈中指定位置元素)。
1 | $ git stash apply # 栈顶元素,最近被隐藏的更改 |
这里的
n
是栈中保存的指定更改的序号,可以通过git stash list
查看。
使用以下命令恢复被隐藏的更改,但是从栈中删除(出栈)。
1 | $ git stash pop |
使用以下命令在栈中删除被隐藏的更改(删除栈中指定位置元素)。
1 | $ git stash drop stash@{n} |
这里的
n
是栈中保存的指定更改的序号,可以通过git stash list
查看。
使用以下命令清空所有被隐藏的更改,即清空栈(清空栈)。
1 | $ git stash clear |
应用提交 cherry-pick
使用以下命令将其他分支的指定提交应用到当前分支,即将一个特定的提交复制到当前分支。
1 | $ git cherry-pick <commit-id> |
多人协作 push|pull
使用以下命令查看远程仓库的详细信息,包括拉取(fetch)和推送(push)操作的 URL。
1 | $ git remote -v |
使用以下命令将本地仓库的指定分支推送到远程仓库的同名分支。
1 | $ git push origin <branch-name> |
如果
git push
推送失败,则需要git pull
试图合并后再进行推送。
使用以下命令从远程仓库同名分支拉取到本地仓库的当前分支。
1 | $ git pull |
使用以下命令创建并切换到一个新的本地分支,同时将其与远程仓库的远程分支关联起来。(远程分支需存在)
1 | $ git checkout -b <branch-name> origin/<branch-name> |
使用以下命令创建并切换到一个新的本地分支,然后将其推送到远程仓库,并设置上游分支。
1 | $ git checkout -b <branch-name> |
使用以下命令关联本地分支和远程分支。(远程分支需存在)
1 | $ git branch --set-upstream-to=origin/<branch-name> <branch-name> |
标签管理
Git 中的标签是指向某个 commit
的指针,其意义是为某个 commit
起一个有意义的名字。
创建标签 tag
使用以下命令为当前分支的最新提交打一个标签。
1 | $ git tag <tagname> |
使用以下命令为指定提交打一个标签。
1 | $ git tag <tagname> <commit-id> |
使用以下命令为指定提交打一个标签,并附带描述性文字。
1 | $ git tag -a <tagname> -m "描述文字" <commit-id> |
查看标签 tag|show
使用以下命令查看所有标签。
1 | $ git tag |
标签按字母排序,而不是时间。
使用以下命令查看标签的具体信息。
1 | $ git show <tagname> |
推送标签 push
使用以下命令推送某个本地标签到远程仓库。
1 | $ git push origin <tagname> |
使用以下命令推送所有未推送过的本地标签到远程仓库。
1 | $ git push origin --tags |
删除标签 tag -d
使用以下命令删除未推送到远程仓库的标签。(本地删除)
1 | $ git tag -d <tagname> |
使用以下命令删除已推送到远程仓库的标签。(本地 + 远程删除)
1 | $ git tag -d <tagname> |
其他功能
忽略文件
Git 提供了 .gitignore
文件,用于编写忽略规则,符合这些规则的文件将不会被 Git 追踪。需要注意的是,.gitignore
文件本身需要被 Git 管理并存放在版本库中。一个 Git 仓库允许存在多个 .gitignore
文件,每个文件对其所在的目录及其子目录有效。
配置别名
使用以下命令为 Git 操作设置别名。
1 | $ git config --global alias.<new-name> <old-name> |
1 | $ git config --global alias.st status # 此时 git st 等价于 git status |
配置文件
使用 git config
命令对 Git 进行配置时,其实是在修改文件系统中的配置文件。
- 如果使用
--global
选项,配置将针对所有仓库生效,配置信息将被写入用户主目录下的隐藏文件.gitconfig
中。 - 否则,配置将只针对当前仓库生效,配置信息将被写入仓库跟目录下的
.git/config
文件中。