进阶学习资料

本文主要介绍Git的一些进阶技巧命令的使用。

定制专属快捷命令

Git 提供了别名功能,可以简化命令输入。 通过git config,您可以为每个命令设置别名。以下是一些示例:

# 如设置co别名用于checkout
git config --global alias.co checkout 
git config --global alias.br branch 
git config --global alias.ci "commit -s"
git config --global alias.st status

例如,输入git ci即可代替冗长的git commit -s。随着使用 Git 的频率增加,您可能会经常使用其他命令,因此创建别名时不要犹豫。例如,为了解决取消暂存文件的问题,可以添加取消暂存别名:

git config --global alias.unstage 'reset HEAD --'

以下是两种常用的方法,主要解决将文件从暂存区移除:

# 方法一: 适用于单个文件或多文件的情况。
git reset HEAD -- fileA
# 方法二: 虽然Git没有直接的unstage命令,可通过别名来简化操作。
git unstage fileA 

您可以使用git last来代替git log -1 HEAD命令,以查看最近一次的提交记录。

git config --global alias.last 'log -1 HEAD'

这样,可以轻松地看到最后一次提交信息:

# 这个命令是为Git配置一个全局别名last,使每次输入git last就能显示最近一次的提交信息。
git last   
commit 66938dae3329c7aebe598c2246a8e6af90d04646 Author: Josh Goebel  <dreamer3@example.com> Date:   Tue Aug 26 19:48:51 2008 +0800      test for current head      Signed-off-by: Scott Chacon <schacon@example.com>

代码合并与变基

在 Git 中整合来自不同分支的修改主要有两种方法:合并(merge) 以及变基(rebase)。

合并(merge)

对于两个分支,如下图所示:

使用 Merge 命令可以将两个分支的最新快照(C3C4)及其共同祖先(C2)进行三方合并,生成一个新的快照并提交。

变基(rebase)

通过提取在 C4 中引入的补丁和修改,然后在 C3的基础上应用一次。 在 Git 中,这种操作就叫做变基。 您可以使用 rebase 命令将提交到某一分支上的所有修改都移至另一分支上。

在上面这个例子中,运行:

git checkout experiment 
git rebase master 
First, rewinding head to replay your work on top of it...
Applying: added staged command

它的原理是首先找到这两个分支(即当前分支 Experiment、变基操作的目标基底分支 Master)的最近共同祖先 C2,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向目标基底 C3, 最后应用这些修改。

现在回到master分支,进行一次快进合并。

# 切换到Git仓库的master分支。
git checkout master 

# 合并当前分支到已检出的分支。在执行git merge之前,确保您已经切换到了想要合并到的目标分支。
git merge experiment

优雅地合并分支代码并提交

对比两种合并方法,rebase 能让提交历史更整洁,呈现串行趋势,因此备受青睐。它不仅能优雅地合并代码,还能修改提交历史。通过rebase,您可以将本地多个开发提交合并,再整合到目标分支,使历史记录更加清晰。

代码暂存

当你在项目中工作时,可能需要切换分支但不想提交未完成的工作,这时可以使用git stash命令。

保存修改

git stash命令将未完成的修改保存到一个栈上,稍后可以重新应用。以下为例,进入项目查看之前改动的文件。

git status 
Changes to be committed:   
(use "git reset HEAD <file>..." to unstage)  	
modified:   index.html  
Changes not staged for commit:   
(use "git add <file>..." to update what will be committed)   
(use "git checkout -- <file>..." to discard changes in working directory)  	
modified:   lib/simplegit.rb

现在想要切换分支但不想提交,运行git stashgit stash save

git stash 
Saved working directory and index state \   
"WIP on master: 049d078 added the index file" 
HEAD is now at 049d078 added the index file 
(To restore them type "git stash apply")

工作目录变干净了,并且刚刚的修改都不存在了:

git status 
# On branch master nothing to commit, working directory clean

查看暂存列表

要查看暂存的内容,可以使用git stash list

git stash list 
stash@{0}: WIP on master: 049d078 added the index file 
stash@{1}: WIP on master: c264051 Revert "added file_size" 
stash@{2}: WIP on master: 21d80a5 added number to log

应用暂存

在本例中,有两个之前做的暂存,所以接触到了三个不同的暂存工作。 可以通过原来stash命令的帮助提示将刚刚暂存的工作重新应用:git stash apply。 

如果想要应用其中一个更旧的暂存,可以通过名字指定它,比如:git stash apply stash@{2}, 如果不指定一个暂存,Git认为指定的是最近的暂存。

git stash apply 
# On branch master 
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed) 
# 
#      modified:   index.html 
#      modified:   lib/simplegit.rb #

可以看到Git重新修改了当您暂存时撤消的文件。 也可以运行git stash pop来应用暂存并从栈上移除它。

丢弃暂存

可以运行git stash drop加上将要移除的暂存的名字来移除它。

git stash list 
stash@{0}: WIP on master: 049d078 added the index file 
stash@{1}: WIP on master: c264051 Revert "added file_size" 
stash@{2}: WIP on master: 21d80a5 added number to log 
git stash drop stash@{0} 
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

从暂存创建分支

如果暂存了一些工作,在后续重新应用时可能会有冲突。可以运行git stash branch创建一个新分支,检出暂存工作时的提交,重新应用工作,然后在应用成功后扔掉。

git stash branch testchanges 
Switched to a new branch "testchanges" 
# On branch testchanges
# Changes to be committed: 
#   (use "git reset HEAD <file>..." to unstage) 
#      modified:   index.html 

# Changed but not updated: 
#   (use "git add <file>..." to update what will be committed) 
#      modified:   lib/simplegit.rb # Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)

这是在新分支轻松恢复暂存工作并继续工作的一个很不错的途径。

修改代码历史

在使用Git时,您可以修正提交历史,包括改变提交顺序、信息、文件,压缩或拆分提交,甚至移除提交。

修改最后一次提交

最常见的操作是修改最近一次提交,您可能想更改提交信息或更新文件。如果只是想修改最近一次提交的提交信息,那么很简单。

git commit --amend

想要精简提交信息或修改已提交的快照,只需进入文本编辑器修改最近一条提交信息,然后保存关闭即可。若已提交却忘记添加文件,可通过git addgit rm修改文件,再运行git commit --amend更新快照,但请注意,修正会改变提交的SHA-1校验和,类似小变基,推送后请勿修正。

修改多个提交信息

要修改历史中较早的提交,需使用更复杂的工具。Git无直接改变历史工具,但可用交互式变基。通过git rebase -i,可在任意提交后停止,修改信息、添加文件等。要修改最后三次提交,使用HEAD~3作为参数传递给git rebase -i命令。记住,这实际上指定了以前的四次提交中的父提交。

git rebase -i HEAD~3

需要重点注意的是相对于正常使用的 log 命令,这些提交显示的顺序是相反的。 运行一次 log 命令,会看到类似这样的东西。

git log --pretty=format:"%h %s" HEAD~3..HEAD 
a5f4a0d added cat-file 
310154e updated README formatting and added blame 
f7f3f6d changed my name a bit

反序变基脚本, 它从指定提交(如HEAD~3)逆序重演每个变更,要停在某次变更上修改,编辑脚本,将想改的pick换成edit

例如,只调第三次提交

edit f7f3f6d changed my name a bit 
pick 310154e updated README formatting and added blame 
pick a5f4a0d added cat-file

当保存并退出编辑器时,Git将您带回到列表中的最后一次提交,把送回命令行并提示以下信息。

git rebase -i HEAD~3 
Stopped at f7f3f6d... changed my name a bit 
You can amend the commit now, with
         git commit --amend  
Once you’re satisfied with your changes, run
         git rebase --continue

这些指令准确地告诉您该做什么。 

git commit --amend

修改提交信息,然后退出编辑器。 然后,运行:

git rebase --continue

这个命令将会自动地应用另外两个提交,然后就完成了。 如果需要将不止一处的 pick 改为 edit,需要在每一个修改为 edit 的提交上重复这些步骤。 每一次,Git 将会停止,让您修正提交,然后继续直到完成。

想重塑您的提交历史,可以通过变基命令操作,但需注意,此操作针对的是HEAD~3..HEAD范围内的提交,且会彻底重写它们,关键要点如下:

  • ‌重写提交‌:变基不仅修改内容,还重写提交信息。

  • ‌避免冲突‌:别碰已推送的提交,否则会造成版本混乱。

  • ‌编辑器操作‌:运行命令后,文本编辑器将展示待重写的提交列表。

pick f7f3f6d changed my name a bit  # 使用这个提交。
pick 310154e updated README formatting and added blame 
pick a5f4a0d added cat-file  
# Rebase 710f0f8..a5f4a0d onto 710f0f8 
# 
# Commands: 
#  p, pick = use commit 
#  r, reword = use commit, but edit the commit message 
#  e, edit = use commit, but stop for amending 
#  s, squash = use commit, but meld into previous commit 
#  f, fixup = like "squash", but discard this commit's log message 
#  x, exec = run command (the rest of the line) using shell 
# 
# 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

重新排序提交

也可以使用交互式变基来重新排序或完全移除提交。 如果想要移除 “added cat-file” 提交然后修改另外两个提交引入的顺序,可以将变基脚本从这样:

pick f7f3f6d changed my name a bit 
pick 310154e updated README formatting and added blame pick 
a5f4a0d added cat-file

改为这样:

pick 310154e updated README formatting and added blame 
pick f7f3f6d changed my name a bit

当保存并退出编辑器时,Git 将您的分支带回这些提交的父提交,应用 310154e,然后应用 f7f3f6d,最后停止。 事实修改了那些提交的顺序并完全地移除了 added cat-file 提交。

压缩提交

通过交互式变基工具,也可以将一连串提交压缩成一个单独的提交。 在变基信息中脚本给出了有用的指令。

# Commands: 
#  p, pick = use commit 
#  r, reword = use commit, but edit the commit message 
#  e, edit = use commit, but stop for amending 
#  s, squash = use commit, but meld into previous commit 
#  f, fixup = like "squash", but discard this commit's log message 
#  x, exec = run command (the rest of the line) using shell 
# 
# 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

如果,指定 squash 而不是 pick 或 edit,Git将应用两者的修改并合并提交信息在一起。 所以,如果想要这三次提交变为一个提交,可以这样修改脚本。

pick f7f3f6d changed my name a bit squash 
310154e updated README formatting and added blame squash 
a5f4a0d added cat-file

当保存并退出编辑器时,Git 应用所有的三次修改然后将你放到编辑器中来合并三次提交信息。

# This is a combination of 3 commits. 
# The first commit's message is: changed my name a bit  
# This is the 2nd commit message:  updated README formatting and added blame  
# This is the 3rd commit message:  added cat-file

当你保存之后,你就拥有了一个包含前三次提交的全部变更的提交。

拆分提交

拆分一个提交会撤消这个提交,然后多次地部分地暂存与提交直到完成你所需次数的提交。 例如,假设想要拆分三次提交的中间那次提交。 想要将它拆分为两次提交:第一个 updated README formatting,第二个 added blame 来代替原来的 updated README formatting and added blame。 可以通过修改rebase -i的脚本来做到这点,将要拆分的提交的指令修改为 edit

pick f7f3f6d changed my name a bit edit 
310154e updated README formatting and added blame pick 
a5f4a0d added cat-file

然后,当脚本将你进入到命令行时,重置那个提交,拿到被重置的修改,从中创建几次提交。 当保存并退出编辑器时,Git带您到列表中第一个提交的父提交,应用第一个提交(f7f3f6d),应用第二个提交(310154e),然后让你进入命令行。 那里,可以通过git reset HEAD^ 做一次针对那个提交的混合重置,实际上将会撤消那次提交并将修改的文件未暂存。 现在可以暂存并提交文件直到有几个提交,然后当完成时运行git rebase --continue

git reset HEAD^ 
git add README 
git commit -m 'updated README formatting' 
git add lib/simplegit.rb 
git commit -m 'added blame' 
git rebase --continue

Git在脚本中应用最后一次提交(a5f4a0d),历史记录看起来像这样:

git log -4 --pretty=format:"%h %s" 
1c002dd added cat-file 
9b29157 added blame 35cfb2b updated README formatting 
f3cc40e changed my name a bit

再次强调,如果您正打算推送提交到共享仓库,请先暂停。我们有一个重要更新:所有在列表中的提交的SHA-1校验和已经更改。为确保数据的完整性和避免冲突,请务必确认这些提交尚未被推送到共享仓库。