Git 常用命令

287 阅读10分钟

1 分支

  • 可以把分支名理解为指针,比如 **master,test,origin/master **等,都是一个指针,指向某次提交快照
  • 特殊指针HEAD:Git有一个名为 HEAD 的特殊指针,它是一个指针,指向当前所在的本地分支
  • git branch:列出本地所有分支,当前分支高亮并且前面有*号
  • git branch -d:删除分支,注意不能删除当前分支
    • 因为你把当前分支删除了,HEAD指针就不知道指到哪去了
  • git branch -D:大写的『D』,表示强制删除分支,慎用这个命令
  • git branch name:创建名为『name』的分支,注意,并不会切换到该分支
  • git checkout -b feature:从当前分支新建feature分支,并切换到 feature 分支
  • git branch -v:查看每一个分支的最后一次提交
  • git branch -vv:查看本地分支和远程分支的跟踪关系

1.1 分支合并

当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧—这就叫做 “快进(fast-forward)”。

1.2 查看分支合并情况

  • git branch --merged:查看哪些分支已经合并到当前分支
    • 在这个列表中分支名字前没有*号的分支通常可以使用gitbranch-d删除掉;你已经将它们的工作整合到了另一个分支,所以并不会失去任何东西。
  • git branch --no-merged:查看哪些分支还没有合并到当前分支
    • git branch -d 删除会失败,因为这些分支还没有被合并,删除会丢失东西。

2 远程分支

2.1 origin/master 与master

  • origin/master:本地指针,指向最后一次同步时远程分支的提交快照
    • 只要你不与origin服务器同步(如 git fetch/pull等),你的origin/master指针就不会移动。详见Git远程分支
  • git fetch origin:** **将origin远程数据同步到本地
    • 包括所有的分支,不只是当前分支,并且更新本地数据库(但是不会合并到当前master分支)
    • git fetch 会**移动 origin/master 指针,**指向更新后的位置。详见git远程分支

2.2 本地分支跟踪远程分支

首先可以用 **git branch -vv **查看本地分支和远程分支的跟踪关系。 分两种情况:

  • 第一种情况是,新建分支时,以远程分支为基础来新建:
# 第一种写法,指定本地分支名
git checkout -b test origin/test

# 第二种写法,用 --track 参数,省略分支名,默认会用origin的分支名
git checkout --track origin/serverfix
  • 第二种,新建分时没有以远程分支为基础,这时候如果想跟踪远程分支,可以用 **-u 或 --set-upstream-to **来设置跟踪:
git branch -u origin/test

# 或者
git branch --set-upstream-to origin/test

# 输出以下内容:
# Branch test set up to track remote branch test from origin.

2.3 删除远程分支

git push origin --delete test

3 变基

3.1 变基与合并

如果想把master分支的改动『整合』到experiment分支,通常我们用 git merge 命令,如下:

# 1. 先切换到experiment分支
git checkout experiment

# 2. 合并master分支到当前分支
git merge master

同样,还是想把master分支的改动『整合』到experiment分支,我们还可以使用『变基』,操作如下:

# 1. 先切换到experiment分支
git checkout experiment

# 2. 变基master
git rebase master

# 3. 会有如下输出
# First, rewinding head to replay your work on top of it...
# Applying: added staged command 

变基的操作,跟合并一样,只需要把『merge』关键字替换为『rebase』;无论是通过变基,还是通过三方合并,整合的最终结果是一样的,只不过提交历史不同罢了,变基的提交历史是一条直线没有分叉。

3.2 rebase理解

在当前分支(假设为experiment) git rebase master,相当于说再一次以最新的master分支为基础,新建experiment分支,然后再把当前experiment分支的commit续上去。

更多变基,详见Git变基。

3.3 git pull --rebase

Your branch and 'origin/master' have diverged,
and have 1 and 11 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

如果 git status 遇到上面的输出,用 git pull --rebase 命令即可

4 日志

显示分支的最近3条commit hash,倒序输出

git log -3 --pretty=format:"%h" data-privilege --reverse | xargs git cherry-pick

4.1 git reflog

reflog,即引用日志,引用日志记录了最近几个月你的HEAD和分支引用所指向的历史。

值得注意的是,引用日志只存在于本地仓库,一个记录你在你自己的仓库里做过什么的日志。其他人拷贝的仓库里的引用日志不会和你的相同;而你新克隆一个仓库的时候,引用日志是空的,因为你在仓库里还没有操作。 可以将引用日志看作是Git版的shell历史记录。

4.2 git log since..until

基本格式为:git log since..until

  • git log master..experiment:可以用来查看『在experiment分支而不在master分支』的提交
    • 反过来,git log experiment..master 就是用来查看『在master分支而不在experiment分支』的提交

这个命令,通常用于解决『这个分支还有哪些提交没有合并到master分支』

跟下面的命令: git log experiment --not master 是等价的,但是感觉加 --not 表意更准确。 git log master..experiment 到底是在哪个不在哪个呢?

4.3 日志搜索

  • git log -Sstr --oneline:使用-S选项来显示新增和删除字符串str的提交
  • git log -Gegrep --oneline:使用-G选项来使用正则表达式搜索

5 git stash

在当前分支作了一些改动,想要切换到另一个分支做,却又不想丢失当前改动也不想做一次提交(可能是工作做到一半,不想创建一次没有意义的提交),针对这种问题的答案是git stash命令。

举例说明下如何操作:

    1. 在当前分支(假设为experiment)作了一些改动
    1. git stash 将当前分支的改动放到『储藏栈』里
    1. git checkout master 切换到master分支,做别的改动、提交
    1. git checkout experiment 切回experiment分支
    1. git stash apply 将最近保存到『储藏栈』里的改动内容重新放回到当前分支,继续之前的工作

git stash 的一些常用命令:

  • **git stash:**将当前改动保存到『储藏栈』里(工作区和暂存区里就没有当前改动了)
  • **git stash list:**查看『储藏栈』里保存哪些东西
  • **git stash apply:**将最近保存到『储藏栈』里的改动内容重新放回到当前分支
    • 注意,git stash apply 命令不一定说要在原来的分支执行,可以在任意分支执行,结果都是将『储藏栈』里的改动内容放回到当前分支。也就说我可以在 experiment 分支stash,在master分支apply,那么就可以把experiment分支的改动直接带到master分支,而无需经过commit在merge来实现。这在有时候很有用:你忘记了当前在哪个分支,噼里啪啦改了半天,结果一看,卧槽居然在master分支直接改了(通常master分支不能直接改),你本来是想在dev分支改的,这时候用 git stash就很有用
  • **git stash pop:**跟git stash apply基本一样,区别在于git stash apply之后,『储藏栈』中还保留储藏的内容,而pop会『应用并弹出』储藏内容,即储藏的内容被删除了。
  • **git stash drop stash引用:**删除『储藏栈』中对应的储藏内容

6 重写历史

6.1 修改最后一次提交

  • git commit --amend:修改最后一次提交并修改提交信息
  • git commit --amend --no-edit:修改最后一次提交,但是不改提交信息

6.2 修改多个提交

使用 git commit --amend 只能修改最后一次提交,如果想要修改最近三次提交,或者最近三次提交中的任意一个提交,可以使用交互式变基: git rebase -i HEAD~3

运行这个命令会在文本编辑器上给你一个提交的列表,看起来像下面这样:

pick 93b2809 Initial commit
pick 76da34e macair add 1.txt
pick 3c7f123 macair add 4.txt

 # Rebase 73b2649..3c7f123 onto 73b2649 (3 command(s))
 #
 # 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
# d, drop = remove commit

会从旧到新展示最近3次提交信息(最老提交的在最上面,与git log 命令的输出顺序正好相反),如果想要修改某一次提交,就将这次提交前面的『pick』改为『edit』或者『e』。 举例来说,假设说我们想要修改『76da34e』这次提交,那么就将前面的『pick』改为『e』,保存退出后,会有如下输出:

Stopped at 76da34e1331826c594351381279f4bb99f14e9fc... macair add 1.txt
You can amend the commit now, with
	git commit --amend
Once you are satisfied with your changes, run
	git rebase --continue

可以看到提示,现在你可以对这次提交做改动,改完以后,运行『git commit --amend』来修改这一次提交,即『76da34e』。修改这次提交后,使用『git rebase --continue』来完成交互式变基。步骤总结如下:

    1. 修改内容
    1. git commit --amend
    1. git rebase --continue

假设我们现在修改下文件,然后将commit msg改为『rebase test』。完成之后,我们用git log来看下:

* 132e4b3 - (HEAD -> macair) macair add 4.txt (4 seconds ago) <wxweven>
* 0750efb - rebase test (16 seconds ago) <wxweven>
* 93b2809 - Initial commit (22 minutes ago) <wxweven>

可以看到,我们已经对历史提交做了修改,需要注意的是,交互式rebase,会产生新的commit(可以看到提交前面的哈希变成了0750efb)。

更多交互式变基的demo如下:

  • 合并多次提交
# 合并多次提交
pick f7f3f6d v1.1 changed my name a bit 
pick 310154e v1.2 updated README formatting and added blame 
pick a5f4a0d v1.3 added cat-file

# 改为下面的:
pick f7f3f6d v1.1 changed my name a bit
squash 310154e v1.2 updated README formatting and added blame 
squash a5f4a0d v1.3 added cat-file
  • 调整提交顺序
# 调整提交顺序,本来是 v1.1 -> v1.2 -> v1.3,想要调整为  v1.1 -> v1.3 -> v1.2
pick f7f3f6d v1.1 changed my name a bit 
pick 310154e v1.2 updated README formatting and added blame 
pick a5f4a0d v1.3 added cat-file

# 改为下面的:(即调整顺序,其他不变)
pick f7f3f6d v1.1 changed my name a bit 
pick a5f4a0d v1.3 added cat-file
pick 310154e v1.2 updated README formatting and added blame

7 cherry-pick

7.1 用法

git cherry-pick commitSha

7.2 demo

假设现在的分支和提交情况如下(用abcd等字母代替commit哈希):

a - b - c - d     Master
     \
       e - f - g  Feature

假设我们现在想要把提交『f』整合到master分支,即部分『合并』feature分支到master,而不是把整个feature分支合并到master分支。那么这时候就可以用 cherry-pick。

# 1. 切换到master分支
git checkout master

# cherry-pick 提交 f
git cherry-pick f

现在分支的提交情况如下:

a - b - c - d - f' Master
     \
       e - f - g    Feature

现在,feature分支的提交『f』的内容,已经合并到master分支上了,不过需要注意的是,master上的『f'』是一次新的提交,跟feature分支的『f』提交内容一样,但是commit哈希不一样。

更多cherry-pick,参见:[www.atlassian.com/git/tutoria…]