首页 > 技术文章 > 常用的git命令

zwd666 2020-07-29 11:49 原文

git add # 将工作区的修改提交到暂存区
git commit # 将暂存区的修改提交到当前分支
git reset # 回退到某一个版本
git stash # 保存某次修改
git pull # 从远程更新代码
git push # 将本地代码更新到远程分支上
git reflog # 查看历史命令
git status # 查看当前仓库的状态
git diff # 查看修改
git log # 查看提交历史
git revert # 回退某个修改

 

git commit用法

git commit –m “本次提交描述”

该命令会将git add .存入暂存区修改内容提交至本地仓库中,若文件未添加至暂存区,则提交时不会提交任何修改。

git commit -a

相当于运行 git add -u把所有当前目录下的文件加入缓存区域再运行git commit.
注意!对于新增的文件,并没有被commit

git commit –am “本次提交描述”
或者git commit –a –m“本次提交描述”

等同于上面的-a和-m

git commit --amend

修改最近一次提交。有时候如果提交注释书写有误或者漏提文件,可以使用此命令。对于漏提交的文件,需要git add到缓存区之后,git commit --amend才能将修改追加到最近的一次提交上。

git stash用法

$ git stash
所有未提交的修改都保存起来,用于后续恢复当前工作目录

$ git stash save “stash_name”
给每个stash加一个message,用于记录版本

$ git stash pop / git stash apply
恢复最新缓存的工作目录(第一个),并删除缓存堆栈中的那一个stash删除(pop), apply则只恢复不删除

$ git stash list
查看现有所有stash
在使用git stash pop(apply)命令时可以通过名字指定使用哪个stash,默认使用最近的stash(即stash@{0})

$ git stash drop
移除最新的stash,后面也可以跟指定stash的名字

git reset用法

git reset根据–soft –mixed –hard,会对working tree和index和HEAD进行重置

$ git reset HEAD^

回退版本,一个^表示一个版本,可以多个,另外也可以使用 git reset HEAD~n这种形式。
也可以回退到指定版本:
$ git reset commit-id

soft 参数:git reset --soft HEAD~1 意为将版本库软回退1个版本,所谓软回退表示将本地版本库的头指针全部重置到指定版本,且将这次提交之后的所有变更都移动到暂存区

默认的mixed参数:git reset HEAD~1 意为将版本库回退1个版本,将本地版本库的头指针全部重置到指定版本,且会重置暂存区,即这次提交之后的所有变更都移动到工作区

hard参数:git reset --hard HEAD~1 意为将版本库回退1个版本,但是不仅仅是将本地版本库的头指针全部重置到指定版本,也会重置暂存区,并且会将工作区代码清空(工作区是clean状态)

注意,soft参数与默认参数都不会修改工作区代码,只有hard参数才会修改工作区代码。

另外,git reset HEAD filename
回退文件,将文件从暂存区回退到工作区(unstage),此时不能带hard,soft参数

git reflog

如果在回退以后又想再次回到之前的版本,git reflog 可以查看所有分支的所有操作记录(包括commit和reset的操作),包括已经被删除的commit记录,git log则不能察看已经删除了的commit记录

615ce06 HEAD@{44}: rebase -i (finish): returning to refs/heads/my_test_branch
615ce06 HEAD@{45}: rebase -i (fixup): zancun_new
702356c HEAD@{46}: rebase -i (fixup): # This is a combination of 2 commits.
c997622 HEAD@{47}: rebase -i (reword): zancun_new
fb74ec2 (origin/master, origin/HEAD) HEAD@{48}: rebase -i (start): checkout FETCH_HEAD
f3ef592 HEAD@{49}: commit: zancun3
6b82c75 HEAD@{50}: commit: zancun2
e900fa0 HEAD@{51}: commit: zancun
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

比如说,回退到commit: zancun3,只需要:
git reset --hard f3ef592 (或者HEAD@{49}) 即可
这个命令对于找回丢失的代码非常有用。

git add

删除文件后需要 git add -A, 光 git add. 不行,区别如下:

git add -A 保存所有的修改
git add . 保存新的添加和修改,但是不包括删除
git add -u 保存修改和删除,但是不包括新建文件。
所以默认使用git add -A就行

git checkout

git checkout既可以操作分支,也可以操作文件

git checkout切换分支

$ git checkout -b newBranchName
Switched to a new branch ‘newBranchName’

这相当于执行下面这两条命令:
git branch newBranchName
git checkout newBranchName(工作区一定要是clean的)

$ git checkout -b newBranchName remote_branch_name
拉取远程分支remote_branch_name创建一个本地分支newBranchName,并切到本地分支newBranchName,采用此种方法建立的本地分支会和远程分支建立映射关系。

git checkout 回退修改

git checkout – fileName
这条命令把fileName从当前HEAD中检出,也就是回退当前工作区的这个文件的修改
–可以省略不写

如果需要回退工作区的全部文件修改,可以使用:
git checkout --hard HEAD
而不需要对每个文件进行checkout,这样太累

git revert

git revert,反转提交, 撤销一个提交的同时会创建一个新的提交,也就是用一个新提交来消除一个历史提交所做的任何修改.

git revert commit-id revert指定的一个commit
git revert HEAD~3 revert指定倒数第四个commit

revert过程有可能遇到冲突,要么git revert --abort终止此次revert操作,代码还原至revert命令前。要么手动消除冲突(同普通的冲突解决),然后add commit

reset,checkout,revert总结

下面这个表格总结了这些命令最常用的使用场景。记得经常对照这个表格,因为你使用Git时一定会经常用到。
命令 | 作用域 | 常用情景
---- | —
git reset | 提交层面 | 在私有分支上舍弃一些没有提交的更改
git reset| 文件层面 | 将文件从缓存区中移除
git checkout| 提交层面| 切换分支或查看旧版本
git checkout| 文件层面| 舍弃工作目录中的更改
git revert| 提交层面| 在公共分支上回滚更改
git revert| 文件层面| (然而并没有)

删除分支

删除分支: $ git branch -d branchName
或者, git branch -D branchName 删除分支(不管它有没有merge)
前提是先要切换到其他分支

$ git branch -d branch1
error: The branch ‘branch1’ is not fully merged.
If you are sure you want to delete it, run ‘git branch -D branch1’.

git push

git push命令用于将本地分支的更新,推送到远程主机。

$ git push <远程主机名> <本地分支名>:<远程分支名>
  • 1
$ git push origin master
  • 1

上面命令表示,将本地的master分支推送到origin主机的master分支。如果master不存在,则会被新建。

如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。

$ git push origin :master
# 等同于
$ git push origin --delete master
  • 1
  • 2
  • 3

上面命令表示删除origin主机的master分支。如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。

 $ git push origin
  • 1

上面命令表示,将当前分支推送到origin主机的对应分支。如果当前分支只有一个追踪分支,那么主机名都可以省略。

$ git push
  • 1

如果当前分支与多个主机存在追踪关系,则可以使用-u选项指定一个默认主机,这样后面就可以不加任何参数使用git push

$ git push -u origin master
  • 1

上面命令将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。

将当前分支推送到远程的同名的简单方法,如下:

$ git push origin HEAD
  • 1

将当前分支推送到源存储库中的远程引用匹配主机。 这种形式方便推送当前分支,而不考虑其本地名称。如下:

$ git push origin HEAD:master
  • 1

单独使用git push时,没有指定push的remote分支名,假如当前本地分支名称与其对应的remote分支名称不一样,则会有一下提示:

fatal: The upstream branch of your current branch does not match
the name of your current branch.  To push to the upstream branch
on the remote, use

    git push origin HEAD:my_new_test_branch

To push to the branch of the same name on the remote, use

    git push origin test

To choose either option permanently, see push.default in 'git help config'.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

当执行git push origin test时,会在远程重新创建一个新的分支,名称就是test,然后把修改同步到test分支。

git pull

git pull命令用于从另一个存储库或本地分支获取并集成(整合)。git pull命令的作用是:取回远程主机某个分支的更新,再与本地的指定分支合并,

$ git pull <远程主机名> <远程分支名>:<本地分支名>
  • 1

比如,要取回origin主机的master分支,与本地的test分支合并,需要写成下面这样

$ git pull origin master:test
  • 1

如果远程分支(master)要与当前分支合并,则冒号后面的部分可以省略。上面命令可以简写:

$ git pull origin master
  • 1

将远程存储库中的更改合并到当前分支中。在默认模式下,git pull是git fetch后跟git merge FETCH_HEAD的缩写。

更准确地说,git pull使用给定的参数运行git fetch,并调用git merge将检索到的分支头合并到当前分支中。 使用–rebase,它运行git rebase而不是git merge。也就是说

git pull = git fetch + git merge
git pull --rebase = git fetch + git rebase
  • 1
  • 2

git中都fetch命令是将远程分支的最新内容拉到了本地,但是fetch后是看不到变化的,此时本地多了一个FETCH_HEAD的指针,checkout到该指针后可以查看远程分支的最新内容。然后checkout到master分支,执行metch,选中FETCH_HEAD指针,合并后如果出现冲突则解决冲突,最后commit。

pull的作用就相当于fetch和merge,自动合并

git fetch origin master
git merge FETCH_HEAD

git fetch origin isoda-android_1.3.0_feature :branch1
使用远程isoda-android_1.3.0_feature分支在本地创建branch1分支(但不会切换到该分支)

1. git merge

将 origin 分支合并到 mywork 分支最简单的办法就是用下面这些命令

git checkout mywork
git merge origin

或者,你也可以把它们压缩在一行里:

git merge origin mywork

假设远程分支上有3次提交A,B,C:
image_1chevldmmedu4f45ur9o6rp3g.png-13.4kB

在远程分支origin的基础上创建一个名为"mywork"的本地分支并提交了修改E,同时有其他人在"origin"上做了一些修改并提交了修改D。
image_1chevnkgpshs17133n5bqrfpjt.png-20.6kB

用git merge命令把"origin"分支与本地提交合并(merge)成版本M,mywork 分支中新的合并提交(merge-commit)将两个分支的历史连在了一起,但这样会形成图中的菱形,让人很困惑。
image_1chevr3p71knutjj1lv3f4e5391a.png-26kB

Merge 好在它是一个安全的操作,比较安全,现有的分支不会被更改,避免了 rebase 潜在的缺点(后面会说)。另一方面,这同样意味着每次合并上游更改时 feature 分支都会引入一个外来的合并提交。如果 master非常活跃的话,这或多或少会污染你的分支历史。虽然高级的 git log 选项可以减轻这个问题,但对于开发者来说,还是会增加理解项目历史的难度。

2. git rebase

作为 merge 的替代选择,你可以像下面这样将 mywork 分支并入 origin 分支:

git checkout mywork
git rebase origin

它会把整个 mywork 分支移动到 origin 分支的后面,有效地把所有 master 分支上新的提交并入过来。但是,rebase为原分支上每一个提交创建一个新的提交,重写了项目历史,并且不会带来合并提交。rebase的好处是避免了菱形的产生,保持提交曲线为直线,让大家易于理解。
image_1chip8e1b1t1vo01m3t1g2bne91u.png-26.2kB

rebase最大的好处是你的项目历史会非常整洁。首先,它不像 git merge 那样引入不必要的合并提交。其次,如上图所示,rebase 导致最后的项目历史呈现出完美的线性——你可以从项目终点到起点浏览而不需要任何的 fork。这让你更容易使用 git log、git reset 和 gitk 来查看项目历史。

不过,这种简单的提交历史会带来两个后果:安全性和可跟踪性。如果你违反了 rebase 黄金法则,重写项目历史可能会给你的协作工作流带来灾难性的影响。此外,rebase 不会有合并提交中附带的信息——你看不到 mywork 分支中并入了上游的哪些更改。

在rebase的过程中,有时也会有conflict,这时Git会停止rebase并让用户去解决冲突,解决完冲突后,用git add命令去更新这些内容,然后不用执行git commit,直接执行git rebase --continue,这样git会继续apply余下的补丁。
在任何时候,都可以用git rebase --abort参数来终止rebase的行动,并且mywork分支会回到rebase开始前的状态。

官方的两张merge和rebase对比图:
merge示例图:
屏幕快照 2018-09-21 下午11.01.48.png-126kB

rebase示例图:
rebase_wrong.png-150.4kB

3. rebase的高级操作–交互式rebase

交互式的 rebase 允许你更改并入新分支的提交。这比自动的 rebase 更加强大,因为它提供了对分支上提交历史完整的控制。一般来说,这被用于将 feature 分支并入 master 分支之前,清理混乱的历史。

把 -i 传入 git rebase 选项来开始一个交互式的rebase过程:

git checkout feature
git rebase -i master

它会打开一个文本编辑器,显示所有将被移动的提交:

pick e900fa0 zancun
pick 6b82c75 zancun2
pick f3ef592 zancun3

# Rebase fb74ec2..f3ef592 onto fb74ec2 (3 commands)
#
# 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
#
# 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这个列表定义了 rebase 将被执行后分支会是什么样的。更改 pick 命令或者重新排序,这个分支的历史就能如你所愿了。比如说,如果第二个和第三个提交只是修复了第一个提交中的小问题,你可以用 fixup 命令把它们合到第一个提交中,并修改第一个的日志:

r e900fa0 zancun
f 6b82c75 zancun2
f f3ef592 zancun3
  • 1
  • 2
  • 3

这样三个提交合并成了一个提交,并可以重新修改提交日志,非常实用。
忽略不重要的提交会让你的 feature 分支的历史更清晰易读。这是 git merge 做不到的。

4. Rebase的黄金法则

当你理解rebase是什么的时候,最重要的就是什么时候不能用rebase。git rebase 的黄金法则便是,绝不要在公共的分支上使用它。

比如说,如果你把 master分支rebase到你的feature 分支上会发生什么:
rebase_wrong.png-150.4kB

这次 rebase 将 master 分支上的所有提交都移到了 feature 分支后面。问题是它只发生在你的代码仓库中,其他所有的开发者还在原来的 master 上工作。因为 rebase 引起了新的提交,Git 会认为你的 master 分支和其他人的 master 已经分叉了。

同步两个 master 分支的唯一办法是把它们 merge 到一起,导致一个额外的合并提交和两堆包含同样更改的提交。不用说,这会让人非常困惑。

所以,在你运行 git rebase 之前,一定要问问你自己「有没有别人正在这个分支上工作?」。如果答案是肯定的,那么把你的爪子放回去,重新找到一个无害的方式(如 git merge)来提交你的更改。不然的话,你可以随心所欲地重写历史。

5. rebae的本地清理功能

在你工作流中使用 rebase 最好的用法之一就是清理本地正在开发的分支。隔一段时间执行一次交互式 rebase,你可以保证你 feature 分支中的每一个提交都是专注和有意义的。

调用 git rebase 的时候,你有两个基(base)可以选择:上游分支(比如 master)或者你 feature 分支中早先的一个提交。我们在「交互式 rebase」一节看到了第一种的例子。后一种在当你只需要修改最新几次提交时也很有用。比如说,下面的命令对最新的 3 次提交进行了交互式 rebase:

git checkout feature
git rebase -i HEAD~3(或者第四个commit-id)
  • 1
  • 2

这样,就可以对本地提交历史中最新的三个提交进行重新整理了,包括提交合并,提交日志修改等等。

通过指定 HEAD~3 作为新的基提交,你实际上没有移动分支——你只是将之后的 3 次提交重写了。注意它不会把上游分支(master)的更改并入到 feature 分支中。

交互式 rebase 是在你工作流中引入 git rebase 的的好办法,因为它只影响本地分支。其他开发者只能看到你已经完成的结果,那就是一个非常整洁、易于追踪的分支历史。

追踪关系

建立test仓库 并建立追踪关系

$ git branck --track test origin/master
  • 1

修改追踪关系
先切换到test

$ git checkout test
  • 1

修改追踪仓库(一定要先切换)

$ git branch --set-upstream-to  origin/master
  • 1

建立追踪关系之后,本地分支名称和远程一样时,使用git push时不用带上远程名称,git pull也不用带上远程分支名

git冲突的修复

1. 直接编辑冲突文件

使用git pull --rebase经常会出现冲突
冲突产生后,文件系统中冲突了的文件里面的内容会显示为类似下面这样:

<<<<<<< HEAD
 * test2
 * test3
=======
 * this is my modify, my be conflicked
 * test1000
>>>>>>> my_modify
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其中:<<<<<<<(7个<)HEAD与=之间的内容是remote上的修改,冲突标记=与>>>>>>>之间的内容是我的修改内容。
在这两者之间选择任何你需要的内容保留下来,并删除所有的===,<<<,>>>即可解决冲突,解决完成之后,git add -A, git rebase --continue就提交了代码

2. 利用图形界面工具解决冲突

当然我们也可以利用图形工具解决冲突
如果要解决的冲突很多,且比较复杂,图形界面的冲突解决工具就显得很重要了。
执行git mergetool用预先配置的Meld(Beyond Compare)解决冲

上面左右两个窗口依次是“LOCAL”、“REMOTE”,它们只是提供解决冲突需要的信息,是无法编辑的。中间的窗口是合并后的结果,可以手动修改,也可以点击相应颜色的箭头选择“LOCAL”或者“REMOTE”。

如果不向解决冲突,回到之前状态,可执行:

$ git rebase --abort
  • 1

3. 代码提交完整流程

步骤如下:
git add -A
git commit -m “message”
git pull --rebase (或者git fetch + git rebase)
解决冲突
git add 冲突文件
git rebase –continue
git push
其中,3、4、5点,如果没遇到冲突就不用进行,直接push上去。
当遇到冲突时,git会提示patch failed,并要我们解决问题了再执行git rebase --continue

推荐阅读