本教程使用Git版本为2.42,以下内容均为我作为大学生的身份、在实际操作中常用到的命令,今日将其总结并记录。

本教程对个人开发者应该是足够用的,但是对于团队开发应该仍然不够。

所有指令一览

本教程会涉及如下指令:

常用

git init:初始化
git remote add origin <url>:添加远程仓库

git status:查看当前工作区和暂存区的状态

git add .:添加所有文件到暂存区
git commit -m "提交信息":提交修改到本地仓库
git push origin <分支名>:将本地仓库更新到远程仓库,首次推送需加-u设置上游分支

git clone <url>:克隆远程仓库到本地
git pull origin <分支名>:拉取默认远程仓库的<分支名>分支内容到本地

回档

git restore --staged <文件名>:撤回<文件名>的git add,但是保留工作区<文件名>的修改
git restore <文件名>:撤回工作区<文件名>的修改(Git 2.23引入,更推荐)
git checkout -- <文件名>:撤回工作区<文件名>的修改(旧版本)

git log --oneline:以简洁的方式查看commit历史

git reset --soft <哈希值>:软回档,回档到对应<哈希值>commit之后,同时保留下一次commit暂存区的修改
git reset --hard <哈希值>:硬回档,回档到对应<哈希值>commit之后,但是不保留下一次commit暂存区的修改

git revert <哈希值>:以merge的方式,回档到对应<哈希值>分支修改之前的状态,也就是对应<哈希值>分支的上一次commit之后的状态(更准确来说不是回档,是撤销<哈希值>对应的commit修改,但<哈希值>之后的commit修改仍然存在)
git show <哈希值>:查看<哈希值>的内容,revert到含merge节点时需要使用该命令
git revert -m <分支代号> <哈希值>:回档对应merge之前对应<哈希值>分支的状态

分支

git branch:查看本地仓库所有分支
git branch -a:查看所有分支(包括远程)
git branch -r:查看远程仓库所有分支
git branch -m <旧分支名> <新分支名>:重命名分支
git branch -d <分支名>: 删除分支
git push origin -d <分支名>: 删除远程分支

git checkout -b <新分支名>:创建并切换至新分支(旧版本)
git checkout <分支名>:切换分支(旧版本)
git switch -c <新分支名>:创建并切换至新分支(Git 2.23引入,更推荐)
git switch <分支名>:切换分支(Git 2.23引入,更推荐)

其他

git -v:查看git版本
git remote show origin:显示远程仓库的详细信息

git fetch origin <分支名>:从远程拉取对应分支的最新版本,但不合并
git fetch -a: 获取所有远程分支
git diff <本地分支名> origin/<远程分支名>:查看当前分支与远程分支的差异
git merge <分支名>:将<分支名>合并到当前分支
git fetch -p:清理在本地存在但是已删除的远程分支

git commit --amend:修改最近一次提交

第一次使用

添加github远程

详情可参照网上教程,或者我的笔记

在github创建远程仓库

1、在自己的profile页面,点击右上角的+号,然后点击New repository

Image 28

2、填写仓库名字、选public、注意不添加README文件、然后点Create repository

Image 29

3、然后就可以看到官方示例的仓库初始化代码(图里面我字打错了)

Image 30

在本地创建仓库

1、随便选一个位置,新建文件夹,进入文件夹,然后打开git bash

win11的用户,按住shift点击鼠标右键,可以直接展开折叠选项

2、在git bash中输入git init初始化,此时会发现文件夹下出现了.git文件夹,这个.git文件夹就是后面提到的本地仓库

Image 31

3、在文件夹下创建你想要上传到github仓库的文件,这里我直接新建1.txt

在git bash中输入git staus可以查看git状态,如下图可以看到红色的1.txt,且标记为Untrack未追踪

Image 32

4、git add <文件名字>将文件添加到暂存区,此时git staus可以发现,红色的1.txt变成绿色了,且标记变成了to be commited等待被提交

Image 33

5、这里我有新建了两个文件,并把它们add到暂存区,其中git add ..代表所有文件,这个会比一个一个添加文件快速

Image 34

6、git commit -m "提交信息"将文件提交到本地仓库,-m是一个参数,后面用双引号括起来你的提交信息。提交之后查看git staus,发现working tree clean工作树干净了

工单来举例的话,git add就是挂起工单,git commit就是解决工单,即修改信息已经提交到本地仓库了

Image 35

到这一步,如果你不用远程仓库,其实也是可以的

提交到远程仓库

1、git remote add origin <ssh or https>添加远程仓库地址,这个在初始化代码的时候github会自动生成

Image 36

2、git branch -M main将本分支强制重命名为main,-M就是强制的意思。你会注意到,github新创建的仓库的默认分支名是main,但是git bash默认是master(不过我不清楚最新版git bash有没有改过来啊),如果最后是从master push到main的话,会出问题

3、git push -u origin main,-u是第一次上传需要加的参数,后面就不用加了,origin算是一个默认的参数,一般不会改变,后面的main代表的是你要push的分支

此时刷新你github仓库的界面,发现文件已经上传了

Image 37

小结

除第一次上传是按照以上的步骤,以后的上传可按照下面三个步骤

1、git add .添加所有文件到暂存区

2、git commit -m "提交信息"将所有暂存区的更改提交到本地仓库

3、git push origin main将本地仓库的内容push到远程仓库

Image 87

注:

1、善用git status查看git状态

2、用vscode的源代码管理会更加方便

git pull与冲突解决

git clone

现在我们到github上复制仓库的url,如下图所示,复制https

Image 38

然后回到刚才放代码的地方,把存代码的文件夹删了,然后重新在文件夹里使用git clone命令,发现没有报错

Image 39 Image 40

然后重新创建新的文件并上传(4.txt),均未报错。这说明这个本地的文件夹我们是可以删除和变动的

Image 41

然后我们在这个clone不删的情况下,又换一个目录位置,继续clone然后上传新文件(5.txt),发现也没有报错,这说明一台电脑里我们可以有多个clone的文件夹(当然这个没什么意义)

Image 42 Image 43

git pull

但是这时候,我们回到刚才的那个文件夹,重新创建一个新的文件(6.txt),就会出现如下报错

Image 44

因为你刚才在另外一个文件夹上传了,但是这个原来的文件夹没有同步,所以出现了冲突,这时候应使用git pull origin + <分支名>进行拉取

在你执行git pull的命令之后,会出现如下界面,这是进入到默认的vim编辑器中了,关于vim编辑器的使用可以参考其他的教程

Image 45

这是git拉取了远程仓库到本地自动merge合并时,你需要提交的merge信息,但我们也可以不用管。只需记住,按一下键盘左上角的ESC,然后输出:q!(注意命令会在窗口左下角显示),回车,即可退出vim编辑器

Image 46

之后就会发现,当前目录下多了远程仓库里的5.txt

Image 47

这时候我们查看git status,发现比远程仓库多了两个commit,这是因为我们有一个新建6.txt的commit,然后再加上刚才merge的commit,一共有两个

Image 48

回到github的仓库界面我们也能看到是多了两个commit,和我上述说的一致

Image 49 Image 50

冲突解决

我们直接在github上面打开1.txt,然后修改文件内容为aaaaaaaa

Image 51 Image 52

然后回到本地打开1.txt,修改文件内容为bbbbbbb

Image 53

此时你查看status的时候会发现1.txt的状态是modified,已修改

Image 54

然后执行一套上传流程,发现报错

Image 55

按照要求git pull之后,提示在1.txt中有冲突

Image 56

我们进到1.txt中会发现两个版本文件的冲突用<<<====>>>符号进行了分隔,上面的是本地分支,标记为HEAD,下面的是远程分支,后面的是这个分支的哈希值

Image 57

我们修改冲突,假设我们同时保留aaaaa和bbbb,如下图

Image 58

此时你不能直接git push,不然会报错。而是应该用git add+commit的方式标记冲突解决,如下图所示

Image 59

且注意到commit之后我们的main后面的MERGING状态消失了。这时候我们再git push,发现上传成功了

Image 60

回到github上面也可以看到我们的两次commit,一次本地修改1.txtbbbbb的提交,一次git pull之后的merge

Image 61

如何回档

撤销git add

现在我们在本地目录下删除6.txt,这时候git status会发现6.txt的状态为deleted

Image 62

然后我们git add一下再git status,发现6.txt的颜色从红色变成了绿色,这是未来我们可以用来区分哪些文件没有提交到暂存区的方式

Image 63

撤销git add的文件,使用git restore --staged <文件名>,如下图,此时git status发现6.txt的颜色又变回了红色,这说明“6.txt被删除的消息”被撤销“提交到暂存区”了

Image 64

但是工作目录中的6.txt并没有回来,要想恢复删除的文件的话,再执行git restore <文件名>(不用加–staged参数)即可

小结

对于已经删除但没有git add的文件:直接git restore <文件名>

对于已经删除且已经git add的文件:先git restore --staged <文件名>撤销add,然后再git restore <文件名>恢复文件

撤销git commit

使用git log可以查看commit历史(如果信息太多,键入q退出显示),加上--oneline参数可以更简洁,如下图所示

这里我们先把删除6.txt的消息提交了,再git log --oneline,发现历史已经更新了,且每次提交都有自己的哈希值

Image 65

执行git reset + <哈希值>,可以撤回提交。但注意,每次提交前面的哈希值,是代表这次提交之后的状态

这里我们执行git reset --soft <哈希值>,这个是软回档,即只撤销git commit,但是不撤销git add,如下图所示,这时候会发现,6.txt变成绿色、等待commit的状态

Image 66

如果想要让文件直接回来,执行git reset --hard <哈希值>,如下图所示,此时git status发现工作树干净,因为这是回档节点刚刚commit后的状态

Image 67

这个是硬回档,会丢失回档节点之后的所有提交,慎用,如下图,我的第九次提交的信息已经没有了

Image 68

不过有一种更安全的方式回档,即使用git rever <哈希值>,revert方式会创建一个新的merge,这样的话以前的commit就都会还在,如下图所示(同样中间会进到vim里面让你填revert的信息)

(注,刚上述回档到merge,我又重新搞了第九次提交和第十次提交)

Image 69 Image 70

值得注意的是,只考虑代码状态的话,revert代码会回到commit之前的状态,reset是回到commit之后的状态

因此如果我们revert到merge的节点,如下图所示发现会报错,我们需要选择我们需要回档的分支,使用git show <哈希值>可以查看两个分支的差异

git show注意到Merge参数后面有两个哈希值,前面的代号为1,是下面红色的部分;后面的代号为2,是下面绿色的部分

Image 71

使用-m参数来选择revert的分支,执行git revert -m <代号1或者2> <哈希值>,同样需要填写revert的信息,即可回档

Image 73 Image 72

此时如果我们再选择-m 2,会提示有冲突,这就又回到前面说解决冲突的步骤了

Image 74

解决冲突后,同样是add+commit操作,注意此时右上角的状态是专门的REVERTING状态

Image 75

小结

使用git log --oneline查看提交历史

git reset --soft <哈希值>:软回档,只撤销git commit,但是不撤销git add
git reset --hard <哈希值>:硬回档,全部撤回

注意:reset会丢失撤销节点之后所有commit的信息,使用revert能够更安全的撤回

git revert <哈希值>:撤回到commit之前的状态

如果是revert到merge节点,需要git show <哈希值>查看两个分支的差别,然后git revert -m <分支代号> <哈希值>撤回

注意:reset是撤回到节点commit之后(对硬回档来说)的状态,revert是撤回到commit之前的状态

fork、分支创建与PR

如果我想给别人的仓库合并代码,我需要先fork,然后在fork的仓库上修改,最后PR过给原来的仓库等待审核

我用我的小号新建了一个仓库,如图所示,点击fork,会创建一个自己的仓库,fork相当于复制

Image 77 Image 78

可以看到fork的仓库下面会有fork专门的标识

Image 79

我们把fork仓库clone到本地来,使用git checkout -b <分支名>创建并且切换分支,-b参数就是对于创建新分支而言的,如果分支存在的话,直接checkout过去就行

然后我们走一条流程git push上去

Image 80

此时回到github仓库,发现已经可以找到我们刚才创建的新分支了

Image 81

点击Compare & pull request,可以打开一个向原仓库合并的PR

Image 82

填写你的PR标题、描述,最后创建PR

在该界面上方,可以看到你是从fork仓库的diraw分支提交PR到原仓库的main分支

Image 83

然后你就会自动跳转到原仓库下,整个PR的流程也就结束了

Image 84

回到本地工作目录,我们可以使用git branch来查看本地仓库的所有分支,在git2.23版本之后,推荐使用git switch <分支名>来切换分支,详情请见补充/过时命令板块

Image 85

查看git版本使用git -v

Image 86

补充

更多细节

HEAD概念

git reset理解

origin理解

文件状态

过时命令

这里介绍一些很容易在网上的一些古早教程看到的过时命令,主要是针对git checkout

取消暂存:Git 2.23之前是git reset HEAD <file>,现推荐使用git restore --staged <file>

取消工作区修改:Git 2.23之前是git checkout -- <文件名>,现推荐使用git restore <文件名>

创建并切换分支:Git 2.23之前是git checkout -b <new-branch>,现推荐使用git switch -c <new-branch>,切换分支同理

在Git 2.23之后,git checkout被拆分为git restore和git switch,主要是因为git checkout职责过多,既能切换分支,又能恢复文件,容易误操作。

补充命令

git branch -d <分支名>: 删除分支

git push origin --delete <分支名>: 删除远程分支

git fetch --all: 获取所有远程分支

.gitignore 文件:管理哪些文件不被 Git 跟踪

高级命令

git stash:临时保存未提交的修改
git rebase:变基
git cherry-pick:选择性地应用某个提交
git tag:管理版本标签
git bisect:二分查找定位问题提交
git submodule:管理子模块

git config