Git 基础语法--详细图文讲解

359 阅读10分钟

Git 基础语法

1 Git 仓库

1.1 获取 Git 仓库

要获取 Git 仓库,有两种方法:

1.1.1 克隆远程仓库

git clone 你要的地址 
  1. 在 GitHub 上找到要克隆的仓库,复制其地址

1.png

  1. 在本地文件夹,右键,点击git bash here

3.png

  1. 输入git clone 你要的地址

2.png

  1. 克隆成功

4.png

5.png

1.2.1 初始化本地仓库

git init
  1. 在本地文件夹,右键,点击git bash here

  2. 输入git init

6.png

  1. 初始化完成

把隐藏项目打开,可以看到这里有一个 .git 文件,代表初始化成功

7.png

1.2 远程仓库

为了能在任意 Git 项目上协作,你需要知道如何管理自己的远程仓库。

远程仓库是指托管在因特网或其他网络中的你的项目的版本库。

1.2.1 查看远程仓库

$ git remote # 查看远程仓库
  1. 克隆远程仓库后

我们在上面(1.1.1 克隆远程仓库)克隆下来的仓库,查看远程仓库,输出origin

22.png

  1. 初始化本地仓库后

我们在上面(1.2.1 初始化本地仓库)创建的本地仓库,查看远程仓库,可以看到什么都没有输出,也就是这个并没有推送到远程

23.png

1.2.2 把本地仓库推送到远程

(可以把提交修改这章看完再来看这个)

下面我们把本地仓库推送到远程

将本地仓库推送到远程仓库的步骤如下:

  1. 在 GitHub 上新建仓库

指路 ☞创建一个 GitHub 仓库

  1. 将远程仓库添加到本地仓库
$ git remote add origin https://github.com/xxx/xxx.git

其中,origin是远程仓库的别名,https://github.com/xxx/xxx.git是远程仓库的 URL。

这个时候再运行 git remote 就会输出 origin

  1. 将本地仓库的代码推送到远程仓库

例如,如果您想要将master分支推送到远程仓库,可以使用以下命令:

$ git push -u origin master

其中,-u选项将本地分支与远程分支关联起来,origin是远程仓库的别名,master是要推送的本地分支名称。

24.png

这时再去 GitHub 上看就发现有文件了

2 提交修改

2.1 提交修改到暂存区

$ git add readme.md    # 提交指定文件 readme.py 的修改到暂存区
$ git add .            # 添加所有修改到暂存区

2.2 提交暂存区的修改到本地仓库

$ git commit                       # 提交暂存区的修改, 调用文本编辑器输入该次提交的描述信息
$ git commit -m "提交的描述信息"     # 提交暂存区的修改, 添加描述信息

2.3 查看当前文件状态

$ git status

状态简览

$ git status -s
$ git status -short
  1. 新建 readme.md 文件后,工作区出现新增:

9.png

  1. git add .readme.md 文件提交到暂存区

10.png

  1. git commitreadme.md 文件提交到本地仓库

11.png

(ps: 这里推荐一个 vscode 插件 Git Graph 可直观地看到每次提交,分支情况

各阶段文件状态如下:

8.png

2.4 查看提交历史

$ git log # 查看历史提交日志
  • commit xxxx (HEAD -> 分支名称)
  • Author 提交者:用户名 <邮箱>
  • Date 提交时间,+0800 是格林尼治时间,代表当前是以哪儿的时间地作为基准,这是世界时间,用它来作为基数与当前所在地时差进行计算,包括地球自转等公式。
  • 注释

12.png

3 撤销修改

3.1 修改上次提交

$ git commit --amend # 修改上次提交

这个命令有两个功能

3.1.1 修改上次提交描述

  1. 输入 git commit --amend,回车

13.png

  1. 出现以下内容

14.png

  • a 或者 i 或者 o 进入编辑模式
  • 更新提交描述
  • Esc 健退出编辑模式
  • 然后输入 :wq 保存我们编辑的描述

15.png

16.png

17.png

可以看到上次的提交描述已经修改了

3.1.2 修改上次提交文件

如果发现上次提交忘了文件,可以这样

  1. 把忘提交的文件加入暂存区

18.png

  1. git commit --amend,回车

19.png

  1. 出现以下内容

同上,可以修改描述,当然也可以不改

20.png

  1. 保存描述后,回车

21.png

可以看到上次的提交已经被更新了

3.2 撤销提交

先讲最重要的,这个先跳过,之后再补 😀

4 分支管理

分支管理可以说是 Git 中最重要的功能。

有人把 Git 的分支模型称为它的“必杀技特性”,也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。

关于分支,可以在Learn Git branching网站试试,能很直观地感受分支提交的变化

当使用 git commit 进行提交操作后,Git 便会创建一个提交对象,它除了包含提交信息外,还包含指向这个对象(项目根目录)的指针。 如此一来,Git 就可以在需要的时候重现此次保存的快照

Git 的分支,其实本质上仅仅是指向提交对象的可变指针。

Git 的默认分支名字是 master (Github 的上是 main)。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。 master 分支会在每次提交时自动向前移动。

Git 的 master 分支并不是一个特殊分支。它就跟其它分支完全没有区别。之所以几乎每一个仓库都有 master 分支,是因为 git init 命令默认创建它,并且大多数人都懒得去改动它。

4.1 分支创建和切换

$ git branch 分支名称1 # 创建分支,分支名称为 分支名称1
  • 创建分支会在当前所在的提交对象上创建一个指针
  • 有一个名为 HEAD 的特殊指针,指向当前所在的本地分支
$ git checkout 分支名称1 # 切换分支到 分支名称1,即 本地 HEAD 指向 分支名称1

通常会创建分支同时切换

$ git checkout -b 分支名称1 # 创建分支, 并切换分支

推荐一个 vscode 插件,git graph 点击底部的 git graph,就可以出现 git 提交的分支图 25.png

示例:

  1. 当前项目只有一个分支 main,main 分支下只有一个 readme 文件

26.png

  1. 新建 develop 分支

27.png

可以看到现在这两个分支都指向同一个提交

  1. 切换到 develop 分支

28.png

可以看到命令行后缀这里的分支也变了

  1. 在 develop 分支进行一些提交

29.png

现在 develop 分支比 main 分支多了两个提交

  1. 再返回 main 分支看看

30.png

可以看到 main 分支只有 readme 文件,没有我们刚刚添加的 demo01、demo02 文件

这也是分支的作用,我们敲代码的时候,有时需要返回之前的版本,又或者需要试试别的方法但需保留原来的版本,比起复制一个副本,使用分支明显更高效。

4.2 合并分支

4.2.1 合并分支 git merge

参考☞Git:合并分支--git merge 命令应用的三种情景

$ git merge 分支名称2 # 把 分支名称2 分支合并到当前分支里

4.2.1.1 快进

  • 当前分支所指向的提交是当前提交的直接上游,所以 Git 只是简单的将当前分支指针向前移动。
  • 换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进,因为这种情况下的合并操作没有需要解决的分歧——这就叫做 快进(fast-forward)

3.jpg

4.jpg

4.2.1.2 非快进,无冲突

两个分支分别提交修改,且无冲突,合并时自动 commit 一次

1.jpg

2.jpg

4.2.1.3 非快进,有冲突

两个分支分别提交修改,且有冲突,合并会暂停下来,等待解决合并产生的冲突

<<<<<<< HEAD
test master.  // ======= 的上半部分, HEAD 所指示的版本
=======
test dev.     // ======= 的下半部分, 待合并分支 (dev) 所指示的版本
>>>>>>> dev

修改好冲突后,继续合并动作

$ git merge --continue # 继续合并

之后会出现修改描述信息,就像上面 commit 的一样:

  • a 或者 i 或者 o 进入编辑模式
  • 更新提交描述
  • Esc 健退出编辑模式
  • 然后输入 :wq 保存我们编辑的描述

5.jpg

6.jpg

示例:

  1. main 分支增加 demo.py,内容为
print('Hello World!')

31.png

  1. main 分支上分出 develop 分支,内容为
print('Hello ,this is develop!')

32.png

  1. main 分支上分出 feature 分支,内容为
print('Hello ,this is feature!')

33.png

分支图如下

34.png

  1. 现在把 feature 合并到 develop 上,出现
<<<<<<< HEAD
print('Hello ,this is develop!')
=======
print('Hello ,this is feature')
>>>>>>> feature

35.png

可以直接在这里改,也可以点击在合并编辑器中解析

36.png

  1. 选择你要的修改,这里选 feature 的,点击完成合并

37.png

现在 develop 分支就合并了来自 feature 分支的提交,修改的内容也出现在暂存区

38.png

输入 git merge --continue

39.png

通过分支图可以看到,现在 feature 的这个提交已经合并到 develop 上来了

4.2.2 合并分支 git rebase

Git:合并分支--git rebase 命令的使用

  • 第二种合并分支的方法是 git rebase
  • Rebase 实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。
  • Rebase 的优势就是可以创造更线性的提交历史,这听上去有些难以理解。
  • 如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。
$ git rebase 分支名称2            # 将提交到 分支名称2 上的所有修改都移至 当前分支  上
$ git rebase 分支名称2 分支名称1   # 将提交到 分支名称2 上的所有修改都移至 分支名称1 上
  • git rebase 比较提交记录,合并修改
  • git merge 比较文件,合并分支

示例:

  1. 来到 develop 分支

40.png

  1. git rebasefeature 分支上的提交 合并到 develop

41.png

4.2.2.1 合并分支的提交 git rebase -i

将两个提交合并为一个

  1. 输入
$ git rebase -i HEAD~2  // 合并两个提交
  1. 出现提交信息,把第二个 pick 改为 s,也就是 squash(挤压合并)

出现

pick abc123 page: xxxxx
pick abc345 page: yyyyy

改为

pick abc123 page: xxxxx
s abc345 page: yyyyy

点击 esc、输入 :、输入 wq 退出

  1. 修改 commit

# 开头的为注释,不会被运行

出现

## This is a combination of 2 commits.
## This is the 1st commit message:

page: xxxxx

## This is the commit message #2:

page: yyyyy

改为

## This is a combination of 2 commits.
## This is the 1st commit message:

page: xxxxx、yyyyy

点击 esc、输入 :、输入 wq 退出

  1. 提交

这时候直接 push 会出现冲突,需要强制提交

$ git push -f # 强制提交

4.2.2.2 git rebase 后再 push 的问题

$ git push
To http://xxxgit
 ! [rejected]            branch1 -> branch1 (non-fast-forward)
error: failed to push some refs to 'http://xxxgit'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
  1. 这时候若是先 pullpush,会拉取该分支的远端形式
  • 原来:

    • 本地 master:a-b-d
    • 本地 branch1:a-b-c
    • 远程 branch1:a-b-c
  • 本地分支 branch1 把本地 master 分支 rebase 过来:

    • 本地 master:a-b-d
    • 本地 branch1:a-b-d-c
    • 远程 branch1:a-b-c
  • git push 报错,先 pullpush

    • 本地 master:a-b-d
    • 本地 branch1:a-b-d-c'-c-merge
    • 远程 branch1:a-b-d-c'-c-merge

远程的提交 c,与 rebase 后的提交 c 的 hash 值不同,push 的时候认为是两个,所以会多一个 c',但实际提交内容是一样的。

pull 首先会执行 git fetch,然后执行 git merge,把获取的分支的 HEAD 合并到当前分支。

所以会有一条 merge 的提交。

  1. 若直接强制推送 git push -f ,会覆盖远端分支
  • 原来:

    • 本地 master:a-b-d
    • 本地 branch1:a-b-c
    • 远程 branch1:a-b-c
  • 本地分支 branch1 把本地 master 分支 rebase 过来:

    • 本地 master:a-b-d
    • 本地 branch1:a-b-d-c
    • 远程 branch1:a-b-c
  • git push 报错,直接强制推送 git push -f

    • 本地 master:a-b-d
    • 本地 branch1:a-b-d-c
    • 远程 branch1:a-b-d-c

4.2.3 复制提交 git cherry-pick

提交 adevelop 复制到 master

$ git checkout develop       # 来到分支 develop
$ git pull                   # 拉取当前分支的所有远程提交
$ git log                    # 获取到 提交 a 的 hash
$ git checkout master        # 来到分支 master
$ git cherry-pick <hash>     # 把 提交 a 从 develop 复制到 master

# 冲突
$ git cherry-pick --abort    # 放弃本次复制

$ git add .
$ git cherry-pick --continue # 修改冲突并继续

阮一峰 git cherry-pick

复制一系列连续的提交

# 复制 A 到 B 连续的提交到本分支 不包括A,包括 B (A先提交,B后提交)
$ git cherry-pick <Ahash>..<Bhash>

示例:

  1. 来到 develop 分支

42.png

  1. feature 分支的提交的 hash值

43.png

  1. 把该提交复制到 develop 分支

44.png

4.3 分支管理

4.3.1 查看分支 git branch

$ git branch                             # 查看本地分支
$ git branch -r                          # 查看远程分支
$ git branch -a                          # 查看所有分支
$ git branch 分支名称                     # 新建分支
$ git branch -d 分支名称                  # 删除本地分支
$ git branch -m 分支名 新的分支名          # 修改本地分支名称
$ git checkout 分支名称1                  # 来到 分支名称1 分支
$ git checkout -b 分支名称                # 从当前分支拉取出新的开发分支并命名

从之前的提交切出一个分支

$ git checkout hash值                     # 跳到某个提交(hash值为该提交的hash值)
$ git checkout hash值 -b 分支名1           # 跳到某个提交,并取名

4.3.2 推送分支 git push

$ git push --set-upstream origin 分支名称 # 本地分支推送到远程
$ git push origin --delete 分支名称       # 删除远程分支
$ git remote prune origin                # 清理本地无效远程追踪分支,(远端分支已经删除,本地依旧能看到的情况)
$ git push # 把当前分支推送到远程

git push -u origin master 与 git push --set-upstream origin master

$ git push -u origin 分支名称1  # 将当前分支推送到 origin 主机的对应分支, 同时指定origin为默认主机,后面就可以不加任何参数使用git push了。
$ git push --set-upstream origin 分支名称1
  1. 先把 本地当前分支 推送到 远程仓库 origin分支名称 1 分支
  2. 然后把 本地当前分支 关联到 远程仓库 origin分支名称 1 分支

git push -ugit push --set-upstream 的缩写版本

4.3.2 拉取分支 git pull

$ git pull # 拉取当前分支的所有远程提交

git pull 是一个 Git 命令,用于从远程仓库拉取最新的代码并合并到本地仓库。

它会执行 git fetch 命令,将远程仓库的最新代码下载到本地仓库,然后再执行 git merge 命令,将本地仓库的当前分支与远程仓库的对应分支进行合并。

如果本地仓库和远程仓库有冲突,需要手动解决冲突后再提交代码。

4.3.2.1 git pull 错误

git pull时遇到error: cannot lock ref ‘xxx’: ref xxx is at (一个commitID) but expected的解决办法

$ git pull

error: cannot lock ref 'refs/remotes/origin/xxxbranch': is at hash1 but expected hash2
From http://xxxgit
 ! hash2...hash3 xxxbranch -> origin/xxxbranch  (unable to update local ref)
$ git update-ref -d refs/remotes/origin/xxxbranch # 使用git命令删除相应refs文件
$ git pull                                        # 再次拉取

4.3.3 修改分支名称

4.3.3.1 分支仅在本地

$ git branch -m 旧分支名 新分支名          # 修改本地分支名称

4.3.3.2 分支已推到远程

  1. 在本地创建 aaa 分支并推到远程
  2. aaa 在本地改名为 bbb
  3. 删除远程 aaa
  4. push 本地 bbb
$ git branch -m 旧分支名 新分支名          # 修改本地分支名称
$ git push --delete origin 旧分支名       # 将远程分支删除
$ git push --set-upstream origin 新分支名 # 将改名后的本地分支推送到远程,并将本地分支与之关联
  1. 从远程分支拉取 aaa 分支
  2. aaa 在本地改名为 bbb
  3. 删除远程 aaa
  4. push 本地 bbb

报错:

$ git push
fatal: The upstream branch of your current branch does not match
the name of your current branch.  To push to the upstream branch
on the remote, use

    git push origin HEAD:aaa

To push to the branch of the same name on the remote, use

    git push origin HEAD

To choose either option permanently, see push.default in 'git help config'.

git push 错误(fatal: The upstream branch of your current branch does not match)解决方案

根本原因在于本地分支 bbb 是从远程分支 aaa 拉取的。

在执行 git push 命令时,不知道应该与远程哪个分支进行同步,就会出现上面那个错误。

应该:

  1. 从远程分支拉取 aaa 分支
  2. 在本地从 aaa 切出一个分支 bbb
  3. 删除远程 aaa
  4. push 本地 bbb

4.3.4 更改默认分支

更改默认分支 在 GitHub 上直接操作,不容易出错,指路 ☞更改默认分支

在 github 上将默认分支修改成 master

1.png

4.3.5 比较两个分支差异

$ git diff branch1 branch2 --stat          # 显示出所有有差异的文件列表
$ git diff branch1 branch2 文件名(带路径)   # 显示指定文件的详细差异
$ git diff branch1 branch2                 # 显示出所有有差异的文件的详细差异

4.4 回滚代码仓库

4.4.1 版本回退

Git reset

git reset --soft  // 头指针恢复, 已经 add 的暂存区不变,工作空间的不变
git reset --mixed // 头指针恢复,已经 add 的暂存区丢失,工作空间的不变
git reset --hard  // 头指针恢复,已经 add 的暂存区丢失,工作空间的恢复
git reset --soft HEAD^ //回退上个版本,将上次 commit 回到暂存区

两种方法用来撤销变更 —— 一是 git reset,还有就是 git revert

git 远程分支版本回退

git push origin HEAD --force // 远程提交回退,并推送本地提交

4.4.2 撤销中间某次提交 git revert

git revert 撤销中间的某次提交

git revert 某次提交 // 撤销某次提交

4.4.3 删除文件 git rm

git rm 与 git rm --cached git rm 命令--cached 参数的作用

git rm 文件路径            // 删除文件,该文件在工作区   、暂存区都删除
git rm --cached 文件路径   // 删除文件,该文件在工作区保留、暂存区删除
git rm 文件路径
git commit -m 'delete'

7.jpg

git rm --cached 文件路径
git commit -m 'delete'

8.jpg

4.4.4 恢复 git 删除的本地分支

如何恢复 Git 被删除的本地分支

git reflog show --date=iso

git checkout -b 分支名称 提交的7位哈希值