Git命令从入门到精通

文章目录

一、基础操作命令

1.提交记录

Git 仓库中的提交记录保存的是你的目录下所有文件的快照,就像是把整个目录复制,然后再粘贴一样,但比复制粘贴优雅许多!

  • git commit:将修改的代码库保存成一个新的提交记录。

2.分支管理

Git 的分支也非常轻量。它们只是简单地指向某个提交纪录。这是因为即使创建再多的分支也不会造成储存或内存上的开销,并且按逻辑分解工作到不同的分支要比维护那些特别臃肿的分支简单多了。

  • git branch <name>:创建分支,分支名自定义;
  • git checkout <name>:切换分支,将原分支切换到指定分支上;
  • git switch <name>:切换分支。它将代替 checkout 命令,但仅支持 Git 2.23 以上的版本;
bash 复制代码
# 创建分支 bugfix
git branch bugfix

# 切换到新创建的分支
git checkout bugfix

# 切换到新创建的分支(有Git版本要求)
git switch bugfix

# 创建一个分支,并切换过去
git checkout -b bugfix

# 将main分支强制切换到C5这个提交记录上
git branch -f main C5

3.分支合并

新建一个分支,在其上开发某个新功能,开发完成后再合并回主线。

  • git merge <name> :将指定的分支合并到当前分支上。在 Git 中合并两个分支时会产生一个特殊的提交记录,它有两个 parent 节点;
  • git rebase <name>:Rebase 实际上就是取出一系列的提交记录,"复制"它们,然后在另外一个地方逐个的放下去。Rebase 的优势就是可以创造更线性的提交历史。如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。
bash 复制代码
# 将bugfix的提交内容合并到主分支
git merge bugfix

# 将当前分支的内容合并到main分支
git rebase main

二、HEAD操作

HEAD 是一个对当前所在分支的符号引用,也就是指向你正在其基础上进行工作的提交记录。

HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。

HEAD 通常情况下是指向分支名的(如 bugFix)。

1.相对引用

使用相对引用的话,就可以从一个易于记忆的地方(比如 bugFix 分支或 HEAD)开始计算。

相对引用的用法:

  • 使用 ^ 向上移动 1 个提交记录,可重复移动,如 ^^^^^ 将分别向上移动两次、三次;
  • 使用 ~<num> 向上移动多个提交记录,如 ~3,如果不填 num 值,则与 ^ 效果一致;
bash 复制代码
# 切换到main分支的父节点记录,^为上一条记录
git checkout main^

# 切换到main分支的前两条记录,~[num],num为前多少条记录,不填默认为1
git checkout main~2

# 强制将bugfix分支切换到原位置的前三个提交记录上
git branch -f bugFix HEAD~3

注意: 如果一个提交记录的父节点有多个,那么 ^ 将默认选择第一父节点进行移动,如果需要选择其他父节点,需要增加参数来选择父节点;

  • ^<num> :使用 ^ 加数值参数,可以选择父节点进行移动;
bash 复制代码
# 将检出到main分支的第二个父节点上
git checkout main^2
  • 链式调用:支持将相对引用操作符组合使用,完成链式调用,一条命令完成多次操作;

    bash 复制代码
    # 1.先将HEAD移动到main分支的前两个提交
    # 2.再将HEAD移动到第二父节点
    # 3.最后将HEAD移动到第二父节点的前三个提交上
    git checkout main~2^2~3
    
    # 1.创建一个新分支bugWork(但是没有切换分支过去)
    # 2.将HEAD移动到当前分支的上一个提交
    # 3.将HEAD移动到上一个提交的第二父节点
    # 4.将HEAD移动到第二父节点的上一个提交
    git branch bugWork HEAD~^2~

2.撤销变更

在 Git 里撤销变更的方法很多。和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。我们这个应用主要关注的是后者。

主要有两种方法用来撤销变更:

  • git reset: 通过把分支记录回退几个提交记录来实现撤销改动,git reset 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。 仅对本地分支有效,不影响远程提交记录。
  • git revert: 通过新增一个新提交(当前记录为 C2,撤销后记录为 C2' )来撤销的原始的提交记录,这是因为新提交记录 C2' 引入了更改,这些更改刚好是用来撤销 C2 这个提交的。也就是说 C2' 的状态与 C1 是相同的。
bash 复制代码
# 将提交记录回退到前两次的记录上
git reset HEAD~2

# 将当前版本C2,回退到上一个版本C1,并新增一个提交记录C2',新的提交C2'与回退前的那个版本C1是相同的
git revert HEAD

三、提交记录整理

1.选择提交cherry-pick(知道版本号)

如果你想将一些提交复制到当前所在的位置(HEAD)下面的话, cherry-pick 是最直接的方式了。

  • git cherry-pick <版本号>...:选择指定的版本号添加到当前分支的下方,参数的位置就是添加的顺序;
bash 复制代码
# 将C2、C3、C6的提交内容添加到当前分支最下方
git cherry-pick C2 C3 C6

2.交互式rebase(不知道版本号)

交互式 rebase 指的是使用带参数 --interactive 的 rebase 命令,简写为 -i

如果你在命令后增加了这个选项,Git 会打开一个 UI 界面并列出将要被复制到目标分支的备选提交记录,它还会显示每个提交记录的哈希值和提交说明,提交说明有助于你理解这个提交进行了哪些更改。

当 rebase UI界面打开时,你能做3件事:

  • 调整提交记录的顺序(通过鼠标拖放来完成);
  • 删除你不想要的提交(通过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录);
  • 合并提交。 简而言之,它允许你把多个提交记录合并成一个。
bash 复制代码
# 针对当前分支的前四个提交记录进行排序(或删除提交)
git rebase -i HEAD~4

四、其他高级技巧

1.本地栈式提交

对多个提交记录进行选择推送,使用 cherry-pick 或 交互式rebase 来指定你要推送的记录。对于不需要的提交进行删除处理。

2.修改提交记录

  • git commit --amend:对当前的提交记录进行一些小小的调整;
bash 复制代码
# 对前三次的提交记录的顺序做调整
git rebase -i HEAD~3

# 修改最新HEAD的提交记录内容
git commit --amend

# 将顺序还原
git rebase -i HEAD~3

注意: 使用 rebase 进行两次排序,可能造成由 rebase 而导致的冲突。

所以推荐使用 cherry-pick 完成,它只要提交记录不是在 HEAD 上游就没问题。

bash 复制代码
# 切换到main分支
git checkout main

# 选择一个提交记录添加到main分支最下方
git cherry-pick C2

# 修改最新HEAD(即C2')的提交记录内容,修改后新提交记录为C2''
git commit --amend

# 选择C2后面的提交记录添加到main分支最下方(即C2''的后面)
git cherry-pick C3

3.标签Git tags

Git 的 tag 可以永久地将某个特定的提交命名为里程碑,然后就可以像分支一样引用了。更难得的是,它们并不会随着新的提交而移动。你也不能切换到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。

  • git tag <标签号> <提交记录>:将为指定的提交记录标记一个标签,明确地让它指向提交记录,如果不指定提交记录,Git 会用 HEAD 所指向的位置。
bash 复制代码
# 将C1的记录添加一个标签,命名为v1
git tag v1 C1

# 将当前HEAD添加一个标签,命名为v2
git tag v2

4.描述Git Describe

由于标签在代码库中起着"锚点"的作用,Git 还为此专门设计了一个命令用来描述离你最近的锚点(也就是标签),它就是 git describe。Git Describe 能帮你在提交历史中移动了多次以后找到方向;

  • git describe <ref>:ref 可以是任何能被 Git 识别成提交记录的引用,如果没有指定的话,将使用目前位置HEAD;

注意:

  • 输出格式:<tag>_<numCommits>_g<hash>
    • tag 表示的是离 ref 最近的标签;
    • numCommits 是表示这个 reftag 相差有多少个提交记录;
    • hash 表示的是你所给定的 ref 所表示的提交记录哈希值的前几位。
  • ref 提交记录上有某个标签时,则只输出标签名称。
bash 复制代码
# 为C0创建一个标签v1
git tag v1 C0

# 获取当前分支的描述,输出结果举例:v1_2_gC2
git describe main

# 获取指定提交记录的描述,输出结果举例:v1_5_gC5
git describe C5

五、远程操作

Git 远程仓库相当的操作实际可以归纳为两点:

  • 向远程仓库传输数据;
  • 从远程仓库获取数据。

远程仓库有一系列强大的特性:

  • 首先也是最重要的的点,远程仓库是一个强大的备份。本地仓库也有恢复文件到指定版本的能力,但所有的信息都是保存在本地的。有了远程仓库以后,即使丢失了本地所有数据,你仍可以通过远程仓库拿回你丢失的数据。
  • 还有就是,远程让代码社交化了!既然你的项目被托管到别的地方了,你的朋友可以更容易地为你的项目做贡献(或者拉取最新的变更)。

1.拉取远程

  • git clone <URL>:在本地创建一个远程仓库的拷贝(比如从 github.com);
  • git clone -b <branchName> <URL>:在本地创建一个远程仓库的拷贝,可以指定你想要拉取的远程分支名称。
bash 复制代码
# 拉取远程仓库到本地,默认拉取的是主分支
git clone http://github.com/***.git

# 拉取远程仓库的bugfix分支到本地
git clone -b bugfix http://github.com/***.git

2.远程分支

  • 远程分支:使用 clone 命令后,本地仓库多了一个名为 origin/main 的分支,这种类型的分支就叫远程分支。
  • 远程分支有一个特别的属性,在你切换到远程分支时,自动进入分离 HEAD 状态。
  • 远程分支命名规范:<remote name>/<branch name>
    • remote name:远程仓库的名称。Git 默认将远程仓库设置为 origin
    • branch name:分支名称。一般来说主分支叫 main,或 master

3.获取远程

  • git fetch <URL>:从远程仓库获取数据,同时远程分支也会更新反映最新的远程仓库。

git fetch 做了些什么

git fetch 完成了仅有的但是很重要的两步:

  • 从远程仓库下载本地仓库中缺失的提交记录
  • 更新远程分支指针(如 origin/main)

git fetch 实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。git fetch 通常通过互联网(使用 http://git:// 协议) 与远程仓库通信。

git fetch 不会做的事

git fetch 并不会改变你本地仓库的状态。它不会更新你的 main 分支,也不会修改你磁盘上的文件。所以,你可以将 git fetch 的理解为单纯的下载操作。

4.拉取远程最新记录

当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支。由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。它就是我们要讲的 git pull

  • git pull:拉取最新远程提交记录并合并到本地分支。它就是 git fetch 和 git merge 的缩写!

5.推送本地到远程

  • git push:负责将本地变更记录上传到远程仓库,并在远程仓库上合并你的新提交记录。

注意: git push 不带任何参数时的行为与 Git 的一个名为 push.default 的配置有关。它的默认值取决于你正使用的 Git 的版本。在项目中进行推送之前,最好检查一下这个配置。

6.合并远程到本地

假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。

这种情况下, git push 就不知道该如何操作了。如果你执行 git push,Git 应该让远程仓库回到星期一那天的状态吗?还是直接在新代码的基础上添加你的代码,亦或由于你的提交已经过时而直接忽略你的提交?

因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push 变更的。实际上它会强制你先合并远程最新的代码,然后才能分享你的工作。

解决方式

  • rebase:在 push 之前,先使用 fetch 命令拉取远程仓库的更新,之后将本地记录 rebase 到最新的提交记录下,最后就可以执行 push 命令了。

    bash 复制代码
    # 更新远程记录到本地
    git fetch
    
    # 将本地记录移动到远程记录的后面
    git rebase origin/main
    
    # 推送远程
    git push
  • merge:与 rebase 类似,仅需要将 rebase 命令替换为 merge 命令即可。

    bash 复制代码
    # 更新远程记录到本地
    git fetch
    
    # 将本地记录合并到远程记录上
    git merge origin/main
    
    # 推送远程
    git push

    尽管 git merge 不会移动你的工作(它会创建新的合并提交),但是它会告诉 Git 你已经合并了远程仓库的所有变更。这是因为远程分支现在是你本地分支的祖先,也就是说你的提交已经包含了远程分支的所有变化。

  • git pull:是 fetch 和 merge 的简写;

    bash 复制代码
    # 更新远程记录到本地,并将本地提交和远程提交进行合并
    git pull
    
    # 推送远程
    git push
  • git pull --rebase:是 fetch 和 rebase 的简写;

    bash 复制代码
    # 更新远程记录到本地,并将本地提交移动到远程提交后面
    git pull --rebase
    
    # 推送远程
    git push

7.远程服务器拒绝推送

如果你是在一个大的合作团队中工作,很可能是main被锁定了,需要一些Pull Request流程来合并修改。如果你直接提交到本地 main,然后试图推送修改,你将会收到这样类似的信息:

! [远程服务器拒绝] main -> main (TF402455: 不允许推送(push)这个分支; 你必须使用pull request来更新这个分支.)

为什么会被拒绝?

远程服务器拒绝直接推送提交到 main,因为策略配置要求 pull requests 来提交更新。你应该按照流程,新建一个分支,推送(push)这个分支并申请pull request,但是你忘记并直接提交给了main。

解决办法

新建一个分支 feature,推送到远程服务器。然后 reset 你的 main 分支和远程服务器保持一致,否则下次你 pull 并且他人的提交和你冲突的时候就会有问题。

bash 复制代码
# 将main分支强制回退到远程记录(--hard表示硬重置,git默认是--mixed)
git reset --hard origin/main

# 创建新分支feature并检出到C2的提交记录上
git checkout -b feature C2

# origin feature可以省略不写
git push origin feature

或者这样:

bash 复制代码
# 创建新分支并推送到远程
git checkout -b feature

# origin feature可以省略不写
git push origin feature

# 回退main分支的提交,保持与远程仓库一致
git branch -f main HEAD^

六、高级远程操作

1.合并特性分支

在大型项目中开发人员通常会在(从 main 上分出来的)特性分支上工作,工作完成后只做一次集成。这跟前面课程的描述很相像(把 side 分支推送到远程仓库),不过本节我们会深入一些。

但是有些开发人员只在 main 上做 push、pull ------ 这样的话 main 总是最新的,始终与远程分支 (origin/main) 保持一致。

对于接下来这个工作流,我们集成了两个步骤:

  • 将特性分支集成到 main 上;
  • 推送并更新远程分支;
bash 复制代码
# rebase 到远程分支的最新提交记录
git pull --rabase

# 推送远程
git push

2.远程分支跟踪

在前几节课程中有件事儿挺神奇的,Git 好像知道 mainorigin/main 是相关的。当然这些分支的名字是相似的,可能会让你觉得是依此将远程分支 main 和本地的 main 分支进行了关联。这种关联在以下两种情况下可以清楚地得到展示:

  • pull 操作时,提交记录会被先下载到 origin/main 上,之后再合并到本地的 main 分支。隐含的合并目标由这个关联确定的。
  • push 操作时,我们把工作从 main 推到远程仓库中的 main 分支(同时会更新远程分支 origin/main) 。这个推送的目的地也是由这种关联确定的!

直接了当地讲,mainorigin/main 的关联关系就是由分支的"remote tracking"属性决定的。main 被设定为跟踪 origin/main ------ 这意味着为 main 分支指定了推送的目的地以及拉取后合并的目标。

远程跟踪

你可能想知道 main 分支上这个属性是怎么被设定的,你并没有用任何命令指定过这个属性呀!好吧,当你克隆仓库的时候,Git 就自动帮你把这个属性设置好了。

当你克隆时,Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支(比如 origin/main)。然后再创建一个跟踪远程仓库中活动分支的本地分支,默认情况下这个本地分支会被命名为 main

克隆完成后,你会得到一个本地分支(如果没有这个本地分支的话,你的目录就是"空白"的),但是可以查看远程仓库中所有的分支(如果你好奇心很强的话)。这样做对于本地仓库和远程仓库来说,都是最佳选择。

这也解释了为什么会在克隆的时候会看到下面的输出:

  • local branch "main" set to track remote branch "origin/main"

更改远程追踪属性

两种方法设置这个属性:

  • git checkout -b totallyNotMain origin/main:通过远程分支切换到一个新的分支,就可以创建一个名为 totallyNotMain 的分支,它跟踪远程分支 origin/main

    bash 复制代码
    # 切换到一个名叫foo的新分支,让其跟踪远程仓库中的main
    git checkout -b foo origin/main
    
    # pull将拉取的是远程的main,到本地foo分支
    git pull
    
    # push将推送的是本地foo分支到远程main
    git push
  • git branch -u origin/main foo:这样 foo 就会跟踪 origin/main 了。如果当前就在 foo 分支上,还可以省略 foo。

    bash 复制代码
    # 切换到一个名叫foo的新分支,让其跟踪远程仓库中的main
    git branch -u origin/main foo
    
    # pull将拉取的是远程的main,到本地foo分支
    git pull
    
    # push将推送的是本地foo分支到远程main
    git push

3.Push/Fetch/Pull的命令参数

Push的参数

  • git push <remote> <place>

    • remote:远程仓库地址,一般就是 origin;
    • place:推送的远程仓库的目的地,即远程的分支名称。它不受HEAD的影响,直接将当前分支全部推送;
    bash 复制代码
    # 将HEAD移动到C0的记录上
    git checkout C0
    
    # 推送当前分支到远程main分支
    git push origin main
    
    # 不指定参数,推送失败(因为当前HEAD没有跟踪任何提交,没有可提交的记录)
    git push
  • git push <remote> <source>:<destination>:这里 place 参数拆分为了两个参数;

    • source:推送源,可以是任何 Git 能识别的位置,例如:HEAD^C5~2 等(C5为提交版本号);
    • destination:目的地,要推送的远程仓库分支名称;
    bash 复制代码
    # 1.Git 将 foo^ 解析为一个位置,上传所有未被包含到远程仓库里 main 分支中的提交记录
    git push origin foo^:main
    
    # 2.如果远程仓库不存在,将创建这个分支并推送
    git push origin main:newBranch
    
    # 3.如果省略了推送源,Git将删除远程仓库的bar分支
    git push origin :bar
    
    # 4.如果不传参数,默认推送当前分支到远程的同名分支上
    git push

Fetch的参数

git fetch 的参数和 git push 极其相似。他们的概念是相同的,只是方向相反罢了。

  • git fetch <remote> <place>

    • remote:远程仓库地址,一般就是 origin;
    • place:推送的远程仓库的目的地,即远程的分支名称。它不受HEAD的影响,直接将当前分支全部推送;
    bash 复制代码
    # 将只下载了foo远程分支中的最新提交记录,并更新了本地 origin/foo
    git fetch origin foo
  • git fetch <remote> <source>:<destination>:这里 place 参数拆分为了两个参数。

    • source:下载源,可以是任何 Git 能识别的位置,例如:HEAD^C5~2 等(C5为提交版本号);
    • destination:目的地,要推送的远程仓库分支名称;

    注意:

    • source 现在指的是远程仓库中的位置,而 destination 才是要放置提交的本地仓库的位置。它与 git push 刚好相反,这是可以讲的通的,因为我们在往相反的方向传送数据。
    • 理论上虽然行的通,但开发人员很少这么做。我在这里介绍它主要是为了从概念上说明 fetchpush 的相似性,只是方向相反罢了。
    bash 复制代码
    # 1.将C2解析成一个远程仓库的位置,然后将提交记录下载到了本地bar分支
    git fetch origin c2:bar
    
    # 2.如果本地分支newBranch不存在,那么将创建一个本地分支并下载
    git fetch origin c2:newBranch
    
    # 3.如果省略了下载源,Git将删除本地仓库的bar分支
    git fetch origin :bar
    
    # 4.如果不传参数,将默认下载所有分支的记录
    git fetch

Pull的参数

git pull 到头来就是 fetch 后跟 merge 的缩写。你可以理解为用同样的参数执行 git fetch,然后再 merge 你所抓取到的提交记录。

以下命令在 Git 中是等效的:

  • git pull origin foo :相当于 git fetch origin foo; git merge o/foo
  • git pull origin bar:bugFix:相当于 git fetch origin bar:bugFix; git merge bugFix

git pull 实际上就是 fetch + merge 的缩写,git pull 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数)。

bash 复制代码
# 1.通过指定 main 更新 origin/main。
# 2.然后将 origin/main merge 到所在的分支,无论当前所在的位置是哪
git pull origin main

# 1.先在本地创建了一个叫 foo 的分支
# 2.从远程仓库中的 main 分支中下载提交记录,并合并到 foo
# 3.然后再 merge 到我们的当前所在的分支 bar 上
git pull origin main:foo

七、命令小结

可以满足日常 90% 的基本操作。

bash 复制代码
git clone <url>				# 拉取远程分支
git fetch					# 刷新提交记录
git pull					# 拉取远程分支
git add .					# 添加所有修改的文件到git管理
git add <filename>			# 添加指定文件到git管理
git commit -m <msg>			# 提交到本地
git push					# 推送远程分支
git push --force origin master		# 强制推送,覆盖原分支
git log						# 查看提交日志记录
git log -n <nums>			# 查看近几次提交记录
git reflog					# 查看所有分支的所有操作记录(包含已删除的提交)
git status					# 查看文件的提交状态
git reset --hard <version>	# 将本地分支回退到指定版本
git revert <version>		# 将远程分支回退到指定版本,并产生一个新的提交记录
git stash					# 把暂存区和工作区的改动保存起来
git stash clear				# 清除所有存储进度
git remote -v				# 查看版本仓库地址
git remote set-url origin <url>	# 修改仓库地址为指定新地址

GIT压缩Zip文件

shell 复制代码
git archive --format=zip --output=filename.zip branchname

# filename 为压缩包的文件名称
# branchname 为需要压缩的分支名称
# 注意:需要在本地仓库的目录内,再进行压缩
相关推荐
binishuaio1 小时前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
会发光的猪。2 小时前
如何在vscode中安装git详细新手教程
前端·ide·git·vscode
stewie64 小时前
在IDEA中使用Git
java·git
晓理紫12 小时前
使用git lfs向huggingface提交较大的数据或者权重
git
我不是程序猿儿14 小时前
【GIT】sourceTree的“当前分支“,“合并分支“与“检出分支的区别
git
_OLi_21 小时前
IDEA中新建与切换Git分支
java·spring boot·git
PyAIGCMaster1 天前
ubuntu下安装 git 及部署cosyvoice(1)
git
维__kxs76671 天前
小程序 + AI 自动直播:一部手机开启抖音挂载小程序流量主变现之旅
git·eclipse
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 天前
GIT GUI和 GIT bash区别
开发语言·git·bash
zhuyan1082 天前
【git】使用记录
git