本文主要介绍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 命令可以将两个分支的最新快照(C3和C4)及其共同祖先(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 stash
或git 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 add
或git 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校验和已经更改。为确保数据的完整性和避免冲突,请务必确认这些提交尚未被推送到共享仓库。