git常用使用技巧

本文详细介绍了Git常用命令的差异,包括fetch、pull、merge与rebase的区别。此外,讲解了储藏(Stashing)、修改历史提交、撤销操作、本地与远程分支交互、cherry-pick以及打标签等高级技巧。通过实例演示了如何在Git中有效地管理你的代码历史和协同开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

  • Workspace:工作区
  • Index / Stage:暂存区
  • Repository:仓库区(或本地仓库)
  • Remote:远程仓库

最常用的几个命令的区别

git fetch/git pull/git rebase/git merge

1.git fetch && git pull

  • git fetch:将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。
  • git pull:是将远程主机的最新内容拉下来后直接合并,即:git pull = git fetch + git merge,这样可能会产生冲突,需要手动解决。
git fetch origin master:tmp //从远程仓库master分支获取最新,在本地建立tmp分支

2.git merge和git rebase区别

假设你在master的C2提交上创建了分支experiment,并完成开发,而此时master有了新的提交C4。怎么将experiment合并到master?

在这里插入图片描述

git merge的情况下

它会把两个分支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)进行三方合并,合并的结果是产生一个新的提交对象(C5)。

在这里插入图片描述

git rebase的情况下
在experiment分支下,执行

$ git rebase master

回到两个分支最近的共同祖先,根据当前分支(也就是要进行变基的分支 experiment)后续的历次提交对象(这里只有一个 C3),生成一系列更改,然后以基底分支(也就是主干分支 master)最后一个提交对象(C4)为新的出发点,逐个应用之前的更改,最后会生成一个新的合并提交对象(C3’),从而改写 experiment 的提交历史,使它成为 master 分支的直接下游。

在这里插入图片描述

然后再到master分支下执行merge操作,这将会是一次快进合并。
在这里插入图片描述

虽然merge 和 rebase 整合得到的结果没有任何区别,但变基能产生一个更为整洁的提交历史。如果视察一个变基过的分支的历史记录,看起来会更清楚:仿佛所有修改都是在一根线上先后进行的,尽管实际上它们原本是同时并行发生的。

1 . 储藏(Stashing)

现在需要切换分支,但还不想提交正在进行中的工作;那么可以储藏这些变更。为了往堆栈推送一个新的储藏:

$ git stash

现在工作目录下就干净了,可以切换到其他分支
查看当前的储藏

$ git stash list
 stash@{0}: WIP on fixbug: 29b039d i am fixbugtest
 stash@{1}: WIP on fixbugtest: 29b039d i am fixbugtest 
 stash@{2}: WIP on fixbug: 29b039d i am fixbugtest

可以看到,在所有分支上执行的git stash,都存储在这里了,如果在多个分支上都执行了git stash,那应用时就需要注意了。

应用储藏
git stash apply ,应用后(应用刚刚实施的储藏,即stash@{0}),stash的记录仍然在
git stash pop,应用后,最新stash记录弹出

$ git stash apply

如果想要应用更早的储藏,可以指定stash

$  git stash apply stash@{2}

丢掉储藏

$ git stash drop 

取消apply了的stash

(注意如果用的git stash pop,则不能取消了)

$ git stash show -p stash@{0} | git apply -R

如果不指定stash@{index},就会取消最新的stash

从储藏中创建分支,这会创建一个新的分支,检出你储藏工作时的所处的提交,重新应用你的工作,如果成功,将会丢弃储藏。

$ git stash branch testchanges

修改历史提交

1.修改最近一次的提交

有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操作,可以使用 --amend 选项重新提交,如果需要新加入一些文件,需要先git add,再:

$ git commit --amend

2.修改多个提交

git rebase -i HEAD~3

pick 7c8f69d add 1

pick 05389d1 add 2

pick ed7925c add 3

注意这个次序和git log的次序是相反的,也就是说该脚本会从旧到新的重新演绎每次提交中引入的变更。

注意:HEAD~3…HEAD范围内的每一次提交都会被重写,无论是否修改说明。不要涵盖已经推送到中心服务器的提交——这么做会使其他开发者产生混乱,因为你提供了同样变更的不同版本。

 Commands:
 p, pick <commit> = use commit
 r, reword <commit> = use commit, but edit the commit message
 e, edit <commit> = use commit, but stop for amending
 s, squash <commit> = use commit, but meld into previous commit
 f, fixup <commit> = like "squash", but discard this commit's log message
 x, exec <command> = run command (the rest of the line) using shell
 b, break = stop here (continue rebase later with 'git rebase --continue')
 d, drop <commit> = remove commit
 l, label <label> = label current HEAD with a name
 t, reset <label> = reset HEAD to a label
 m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
 .       create a merge commit using the original merge commit's
 .       message (or the oneline, if no original merge commit was
 .       specified). Use -c <commit> to reword the commit message.

 These lines can be re-ordered; they are executed from top to bottom.

 If you remove a line here THAT COMMIT WILL BE LOST.

 However, if you remove everything, the rebase will be aborted.

 Note that empty commits are commented out

将第3次的提交改为edit后,保存:

Stopped at 7c8f69d...  add 1
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue

3.重排提交

git rebase -i HEAD~3

pick 05389d1 add 2

pick 7c8f69d add 1

git 会将分支回退到这些分支的父提交,再依次应用05389d1和7c8f69d的更改。

4.压缩提交

使用squash,Git 会同时应用这个变更和它之前的变更并将提交说明归并。因此,如果想将这三个提交合并为单一提交,可以将脚本修改成这样:

git rebase -i HEAD~3

pick 7c8f69d add 1

squash 05389d1 add 2

squash ed7925c add 3

5.拆分提交

拆分提交就是撤销一次提交,然后多次部分地暂存或提交直到结束。例如,假设需要将三次提交中的中间一次拆分。将"add 1 and 2"拆分成两次提交:第一次为"add 1",第二次为"added 2"。可以在rebase -i脚本中修改想拆分的提交前的指令为"edit":

$ git rebase -i HEAD~3
$ pick 3b51a1f add 2
$ pick c468608 add 3 
$ edit a9f1532 add file1 and file2

Git会先应用前两次提交(3b51a1f和c468608),然后应用第三次提交(310154e),然后Git会停下来进入控制台。使用git reset HEAD^对那次提交进行一次混合的重置,这将撤销那次提交并且将修改的文件撤回。此时可以暂存并提交文件,直到拥有多次提交结束后,运行git rebase --continue。

$ git reset HEAD^
$ git add file1
$ git commit -m 'add file1'
$ git add file2
$ git commit -m 'add file2'
$ git rebase --continue

最后提交变成了这样

5732cc4 (HEAD -> master) add file2
7449073 add file1
c468608 add 3
3b51a1f add 2

5.超强命令filter-branch

如果想用脚本的方式修改大量的提交,还有一个重写历史的选项可以用——例如,全局性地修改电子邮件地址或者将一个文件从所有提交中删除,filter-branch非常有用。

从所有提交中删除一个文件
如果因为马虎不小心把一个重要文件提交了,如password.txt文件

$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD

–tree-filter选项会在每次检出项目时先执行指定的命令然后重新提交结果。如果想要删除所有分支上的该文件,则需要加–all。

撤销操作

取消已经暂存的文件
如果不小心把需要本次提交的文件放进了暂存区,可以执行以下命令取消暂存(执行git status时会有这个命令的提示)

$ git reset HEAD <file>

取消对文件的修改

$ git checkout -- file1

(我试了这个好像并不行啊)

git reset

  1. 文件从暂存区回退到工作区,撤销add
  2. 版本回退

1. 文件从暂存区回退到工作区,撤销add

如果想取消某个add的文件,可以使用该命令来进行撤销操作

  • 撤消add:git reset 文件名

  • 撤消所有add的文件:git reset HEAD .

  • 撤消某个文件或文件夹:git reset HEAD 文件(夹)名

  • 把从cache中删除的文件,重新添加到cache中: git add -f 文件名

2. 版本回退

  • git reset --mixed commit的id
    还原到commit的id,(git reset 默认是mixed )此commit之后的文件变成modified红色(即还没add的状态)
  • git reset --soft commit的id
    还原到commit的id,此commit之后的文件变成modified绿色(即add(勾选中)的状态)
  • git reset --hard commit的id
    还原到commit的id,此commit之后的文件都被还原,即所有更改都被丢弃。

3. 撤销reset操作

睡了一觉,第二天醒来,我们后悔了,又想回到reset之前的状态,该怎么做呢?
Git提供了一个命令git reflog用来记录每一次的操作,即使是reset掉的commit记录,reflog都会有记录,所以只需找到对应的commitId,以便确定要回到的哪个版本。

4. 另一种撤销操作git revert

git revert HEAD~3

丢弃最近的三个commit,把状态恢复到最近的第四个commit,并且提交一个新的commit来记录这次改变。
git reset 是把HEAD向后移动了一下,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容。
设想一个以下的应用场景
在这里插入图片描述

这种情况下,小C同学在合并master时,就会把C2,C3,C4的更改都合进去。

在这里插入图片描述

这种情况下,小C同学在合并master时,也会应用C5的更改,相当于也执行了一次撤销C2,C3的操作。

在这里插入图片描述

这么看的话,好像如果非得要撤销已经push到远程master的提交,使用revert更合理一些?

本地分支和远程分支的交互

本地分支强制覆盖远程分支的提交

$ git push origin master --force

如果你后悔了已经push到远程分支master的提交,那么执行下面脚本:

$ git reset --hard 7449073 # 将本地master的提交重置到你想要的提交
$ git push origin master --force

把本地分支推送到远程不存在的一个分支

$ git push origin fixbug:fixbug

创建一个本地分支跟踪远程分支

$ git checkout --track -b dev origin/dev

远程仓库覆盖本地仓库

git fetch --all
git reset --hard origin/dev
git pull   # 拉取代码
git status  # 查看下状态

清空本地环境再拉取线上代码

git reset --hard
git clean -fd
git pull   # 拉取代码
git status  # 查看下状态

3. cherry-pick

cherry-pick的作用:
应用已经存在的提交的更改

假设小Z现在正在开发一个项目,有一个功能分支 feature,开发分支 develop。 feature 有3个提交,分别是 A ,B ,C 。develop 分支只想加入 C 功能, 此时合并操作无法满足,因为直接合并 feature,会将3个提交都合并上。此时就需要cherry pick!

具体的做法:

切换到 develop 分支。
通过 git log feature,找到 C 的 SHA1 值。
通过 git cherry-pick <C的SHA1> ,将 C 的修改内容合并到当前内容分支 develop 中。
若无冲突,过程就已经完成了。如果有冲突,按正常冲突解决流程即可。

git cherry-pick [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]
		  [-S[<keyid>]] <commit>…​
git cherry-pick --continue
git cherry-pick --quit
git cherry-pick --abort
$ git cherry-pick master

将master上最新的提交应用到当前分支并在当前分支提交一个新的提交。

$ git cherry-pick ..master 
or  $ git cherry-pick ^HEAD master

应用所有提交引入的更改,这些提交是master的祖先但不是HEAD的祖先,以生成新的提交。

分离Head

1、移动HEAD指针

HEAD指针是指向当前所在的操作分支,比如现在是在master分支,那么HEAD就指向master分支,然后master分支指向的commitID。

比如你提交了之后,发现代码有问题,想看看之前某个提交的情况。

$ git checkout <commitId>
Note: checking out 'f3cb73a'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at f3cb73a add file1 and file2

上面的提示可以看到:如果要在当前提交上创建新的分支:

$ git checkout -b <new-branch-name>

可以用git diff master 来查看当前分支和master的不同。

将分离的HEAD指针进行上移

$ git checkout HEAD^

2、在提交树上移动分支

将分支移动到提交树上的其他提交

$ git branch -f experiment <commitID>

(注意:需要切换到其他分支来对experiment分支执行该命令)

打标签

Git 可以对某一时间点上的版本打上标签。人们在发布某个软件版本(比如 v1.0 等等)的时候,经常这么做。

1.列显已有的标签

$ git tag
v0.1
v1.3

如果只对 1.4.2 系列的版本感兴趣,可以运行下面的命令:

$ git tag -l 'v1.4.2.*'
v1.4.2.1
v1.4.2.2

2.新建标签

Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。

  • 轻量级标签:不会变化的分支,实际上它就是个指向特定提交对象的引用。
  • 含附注标签:实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。

3.含附注的标签

创建一个含附注类型的标签非常简单,用 -a (译注:取 annotated 的首字母)指定标签名字即可:

$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
  v1.3
  v1.4

-m 选项则指定了对应的标签说明,Git 会将此说明一同保存在标签对象中。如果没有给出该选项,Git 会启动文本编辑软件供你输入标签说明。

可以使用 git show 命令查看相应标签的版本信息,并连同显示打标签时的提交对象。

4.签署标签

如果有自己的私钥,还可以用 GPG 来签署标签,只需要把之前的 -a 改为 -s (译注: 取 signed 的首字母)即可:

$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gee-mail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09

现在再运行 git show 会看到对应的 GPG 签名也附在其内:

5.轻量级标签

轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。要创建这样的标签,一个 -a,-s 或 -m 选项都不用,直接给出标签名字即可:

$ git tag v1.4-lw
$ git tag
v1.4
v1.4-lw

现在运行 git show 查看此标签信息,就只有相应的提交对象摘要.

6.后期加注标签

可以在后期对早先的某次提交加注标签。比如在下面展示的提交历史中:

$ git log --pretty=oneline
166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile
8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme

我们忘了在提交 “updated rakefile” 后为此项目打上版本号 v1.2,没关系,现在也能做。只要在打标签的时候跟上对应提交对象的校验和(或前几位字符)即可:

$ git tag -a v1.2 9fceb02

7.分享标签

默认情况下,git push 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。其命令格式如同推送分支,运行 git push origin [tagname] 即可:

$ git push origin v1.5

如果要一次推送所有本地新增的标签上去,可以使用 --tags 选项:

$ git push origin --tags

现在,其他人克隆共享仓库或拉取数据同步后,也会看到这些标签。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值