Git使用笔记

Git 是一个非常强大的版本控制工具,记录常用的命令和碰到的一些问题。

首先应该了解 Git 里面的几个概念。

  • Workspace:工作区
  • Index/Stage:暂存区
  • Repository/HEAD:当前本地仓库版本
  • Remote:远程仓库

HEAD 指向的版本就是当前本地仓库的版本,上一个版本就是 HEAD^,上上一个版本就是 HEAD^^,往上 10 个版本,可以写成 HEAD~10

常用命令

初始化新仓库

初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。

1
git init

克隆仓库:

1
2
3
git clone [url]
git clone git@github.com:shipengqi/shipengqi.github.io.git blog

这里的 blog 是创建并克隆到指定的文件夹。如果不指定,默认是远程仓库的名字。

检查状态

1
git status

查看 commit 历史

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 显示当前分支的版本历史
git log
# 显示 commit 历史,以及每次 commit 发生变更的文件
git log --stat
# 搜索提交历史,根据关键词
git log -S [keyword]
# 显示某个 commit 之后的所有变动,每个 commit 占据一行
git log [tag] HEAD --pretty=format:%s
# 显示某个 commit 之后的所有变动,其"提交说明"必须符合搜索条件
git log [tag] HEAD --grep feature
# 显示某个文件的版本历史,包括文件改名
git log --follow [file]
git whatchanged [file]
# 显示指定文件相关的每一次 diff
git log -p [file]
# 显示过去 5 次提交
git log -5 --pretty --oneline
# 显示所有提交过的用户,按提交次数排序
git shortlog -sn
# 显示指定文件是什么人在什么时间修改过
git blame [file]
#查看命令历史
git reflog

当你用 git reset --hard HEAD^ 回退到上个版本时,再想恢复,就必须找到要恢复版本的 commit id。可以通过 git reflog 找到那次 commit。
回退前,用 git log 可以查看提交历史,以便确定要回退到哪个版本。
回退后,用 git reflog 查看命令历史,以便确定要回到未来的哪个版本。

比较差异

1
2
3
4
5
6
7
8
9
10
11
# 显示暂存区和工作区的差异
git diff
# 显示暂存区和上一个 commit 的差异
git diff --cached [file]
# 显示工作区与当前分支最新 commit 之间的差异
git diff HEAD
# 显示两次提交之间的差异
git diff [first-branch]...[second-branch]

查看某次提交

1
2
3
4
5
6
7
8
# 显示某次提交的元数据和内容变化
git show [commit]
# 显示某次提交发生变化的文件
git show --name-only [commit]
# 显示某次提交时,某个文件的内容
git show [commit]:[filename]

添加到暂存区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 提交所有改动
git add -A
# 提交被修改 (modified) 和被删除 (deleted) 文件,不包括新文件 (new)
git add -u
# 提交新文件 (new) 和被修改 (modified) 文件,不包括被删除 (deleted) 文件
git add .
# 添加指定文件到暂存区
git add [file1] [file2] ...
# 添加指定目录到暂存区,包括子目录
git add [dir]
# 添加每个变化前,都会要求确认
# 对于同一个文件的多处变化,可以实现分次提交
git add -p

删除文件

1
2
3
4
5
# 同时删除工作区和暂存区中的文件
git rm [file1] [file2] ...
# 从暂存区删除文件, 但工作区不删除
git rm --cached [file]

取消已经添加到暂存区的文件

取消已经添加到暂存区的文件: git reset HEAD <file1>...
对于用 git add 添加到暂存区的文件,如果想要撤销暂存其中的一个文件,可以使用 git reset HEAD <file1> <file2>... 的方式取消暂存。

1
2
3
git reset HEAD file2
或者
git rm --cached file2

这样 file2 文件又回到了之前已修改未暂存的状态。

取消对文件的修改:

取消对文件的修改: git checkout -- <file>...

1
2
3
git checkout -- file2
#或者
git checkout file2

该文件已经恢复到修改前的版本。这条命令有些危险,所有对文件的修改都没有了,所以在用这条命令前,确定真的不再需要保留刚才的修改。

撤销某次提交

对于已经把代码push到远程仓库,你回退本地代码其实也想同时回退远程仓库代码,回滚到某个指定的版本,远程,本地代码保持一致。使用git revert
revert 之后本地代码会回滚到指定的历史版本,这时再 git push 更新远程仓库的代码.

1
2
#回滚至某个commit版本
git revert commit-id

git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。

如果代码还没有push,使用git reset --hard回退:

1
git reset --hard commit-id

如果在push代码以后,使用 git reset --hard 回退代码到某个版本,会和远程仓库代码产生冲突。

只撤销提交,不改变代码,也就是重置当前分支的指针为指定commit id,同时改变暂存区,但工作区不变:

1
git reset commit-id

git revertgit reset的区别:

  • 对于已经把代码已经push到远程仓库, reset 删除指定commit以后,你git push可能导致一大堆冲突.但是revert 并不会。
  • 如果在日后现有分支和历史分支需要合并的时候,reset 恢复部分的代码依然会出现在历史分支里.但是revert 方向提交的commit 并不会出现在历史分支里。
  • reset 是在正常的commit历史中,删除了指定的commit,这时 HEAD 是向后移动了,而 revert 是在正常的commit历史中再commit一次,只不过是反向提交,他的 HEAD 是一直向前的.

分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#切换分支
git checkout dev
#切换并新建一个分支
git checkout -b newBranch
#删除一个分支
git branch -d branch
# 列出所有本地分支
git branch
# 列出所有远程分支
git branch -r
# 列出所有本地分支和远程分支
git branch -a
# 删除远程分支
git push origin --delete [branch-name]
git branch -dr [remote/branch]
# 合并指定分支到当前分支
git merge [branch]
# 选择一个commit,合并进当前分支
git cherry-pick [commit]

同步远程仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 下载远程仓库的所有变动
git fetch
# 显示所有远程仓库
git remote -v
# 显示某个远程仓库的信息
git remote show [remote]
# 增加一个新的远程仓库,并命名
git remote add [shortname] [url]
# 取回远程仓库的变化,并与本地分支合并
git pull [remote] [branch]
# 上传本地指定分支到远程仓库
git push [remote] [branch]
# 强行推送当前分支到远程仓库,即使有冲突
git push [remote] --force
# 推送所有分支到远程仓库
git push [remote] --all

生成压缩包

1
git archive

配置别名

1
git config --global alias.st status

以后st就表示status,可以敲git st代替git status

错误处理

读取不到github上的分支

Please make sure you have the correct access rights and the repository exists.

一般可能是因为没有在github账号添加SSH key。
解决方法:

1
2
3
4
5
6
7
8
#git设置account
git config --global user.name "yourname"
git config --global user.email“your@email.com"
#生成ssh key
ssh-keygen
#拷贝id_rsa.pub
cat id_rsa.pub

登录github,添加ssh key即可。

remote error

1
2
3
4
5
6
7
8
9
git push origin feature/es6
fatal: remote error:
You can't push to git://github.houston.softwaregrp.net/he-chatops/hubot-enterprise-bot.git
Use https://github.houston.softwaregrp.net/he-chatops/hubot-enterprise-bot.git
#解决
git remote rm origin
git remote add origin git@github.houston.softwaregrp.net:he-chatops/hubot-enterprise-bot.git
git push origin feature/es6

技巧

记录常用的技巧。

配置常用的命令别名

1
2
3
4
git config --global alias.st status
git config --global alias.br branch
git config --global alias.co checkout
git config --global alias.ci commit

原生的git log不太好用,一样可以配置:

1
git config --global alias.lg 'log --color --graph --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset" --abbrev-commit'

然后git lg就成了下面的样子:

撤销修改

每次碰到要撤销一些提交,可能会被revertreset搞混淆。记录常用的撤销修改的操作:

修改文件之后,还没有执行git add

1
git checkout -- <file>

撤销某个文件,或者某些文件的修改,请注意--,推荐加上去,加了就一定是撤销对文件的修改,不加的话,
可能会变成切换分支,如果没有对应的分支,才会去尝试恢复对应的文件。

执行了git add,但是没有commit

执行了git add之后,修改会被加入到缓存区,这个时候执行:

1
git reset HEAD <file1> <file2> ...

执行了git addcommit

这种情况,修改已经提交到了当前分支,但是还没有push到远程仓库,使用:

1
git reset --hard HEAD^

HEAD指的是当前版本,HEAD^就是上一次commit了,也可以git reset --hard <commit id>,会退到指定的版本。

注意,如果只是想撤销提交,但是不改变代码,使用git reset HEAD^

push了以后怎么办

对于已经把代码push到远程仓库,你回退本地代码其实也想同时回退远程仓库代码,回滚到某个指定的版本,远程,本地代码保持一致。使用git revert
revert 之后本地代码会回滚到指定的历史版本,这时再 git push 更新远程仓库的代码.

1
2
#回滚至某个commit版本
git revert commit-id

git revertgit reset的区别:

  • 对于已经把代码已经push到远程仓库, reset 删除指定commit以后,你git push可能导致一大堆冲突.但是revert 并不会。
  • 如果在日后现有分支和历史分支需要合并的时候,reset 恢复部分的代码依然会出现在历史分支里.但是revert 方向提交的commit 并不会出现在历史分支里。
  • reset 是在正常的commit历史中,删除了指定的commit,这时 HEAD 是向后移动了,而 revert 是在正常的commit历史中再commit一次,只不过是反向提交,他的 HEAD 是一直向前的。

怎么把主仓库的 commits 拉取到自己 fork 的仓库

如果一个项目多人维护,每个人都 fork 了主仓库,并修改,提交 PR,那么如果在你提交自己的修改之前,主仓库 merge 了别人的 PR,
你 fork 的仓库的 commit 就会落后于主仓库,例如会有类似 This branch is 12 commit behind ITOM-Shared-Services:master. 的提示。这个时候
直接提交你的代码,创建 PR,如果 merge 你的 PR,可能就会有冲突,怎么解决?

  1. 切换在你本地的仓库
  2. 添加 remote
    1
    git remote add itom git@github.houston.softwaregrp.net:ITOM-Shared-Services/cdf-service.git

上面的命令中 itom 是给这个 remote 命名,git@github.houston.softwaregrp.net:ITOM-Shared-Services/cdf-service.git 是 remote 的地址。

  1. 验证 git remote -v
  2. 在每次提交前执行 git pull itom master,可以把主仓库的最新 commits 拉去到本地。
  3. 提交代码