大家好,我是 17 。
本文把 git 的各种命令一网打进,详细介绍 clone, add,dff,reset,revert,rebase,check-pick,分支等常用命令的使用方法和应用场景。
为了讲述方便 git 版本 2.32.1。环境为 linux centos 7。
在讲具体的命令之前,我们需要先看张图,理解了这张图,对于理解后面的命令会有帮助。
主要分三部分:远程仓库(remote),本地仓库(repository)和工作区(workspace)。本地仓库和工作区之间还有一个暂存区(index)。
远程仓库(remote):作用是同步代码。每个成员把自己的代码 push 到远程仓库,再从远程仓库 pull 来更新代码。
本地仓库(repository):做为分布式管理仓库的特点:每个本地仓库都拥有完整的代码和历史记录。保存记录的功能无需要联网,可以先保存在本地。 执行 git commit
就是把代码提交到本地仓库。
工作区(workspace):对应的是本地分支代码,是我们平时写代码的地方。
暂存区(index):暂存区标记了当前工作区中那些内容是被 Git 管理的。git add
会把内容加到暂存区。添加到暂存区不会有记录。我们的代码没有完成,可以先暂存,等差不多了,再提交。
建立远程仓库
为了能方便练习,最好能建一个远程仓库。
创建仓库
打开 gitee.com/projects/ne… gitee 的新建仓库页面
输入仓库名称 git-learn,其它的都可以不填,点下面的 “创建” 按钮。
新建成功后显示如下界面
点右边的复制按钮(红框内的图标)把地址复制下来,粘贴到 git clone 后面。
git clone git@gitee.com:iam17/git-learn.git 复制代码
首次执行的时候,会问你
Are you sure you want to continue connecting (yes/no/[fingerprint])? 复制代码
输入 yes 回车,拉取失败了。
fatal: Could not read from remote repository. 复制代码
因为我们用的是 ssh 方式,所以需要把公钥放到 gitee 上面。
生成公钥很简单,看这个 帮助页面
放好公钥后,再次执行clone 命令
git clone git@gitee.com:iam17/git-learn.git warning: You appear to have cloned an empty repository. 复制代码
输出个 warning,说 clone 了一个空的仓库,这个不用管它,本来就是空的。
你也可以用 github,不过可能会不打开,而且速度可能很慢。
git clone
执行 clone 命令默认会拉取远程仓库的所有内容。
git clone git@gitee.com:iam17/git-learn.git 复制代码
clone 命令是一个复合命令,相当于连续执行了下面三个命令。
git remote add origin git@gitee.com:iam17/git-learn.git git fetch git checkout master 复制代码
首先添加远程仓库 origin
origin 是约定俗成的远程仓库的默认名,没什么特别的含意。如果不满意,可以修改
git remote rename origin bananer 复制代码
没有特别的原因,这个名字最好别改。
然后从远程仓库拉取所有代码
所有远程分支在本地是只读的。可以查看已经拉取的远程分支
git branch --remote 或 git branch -r 复制代码
远程分支存放在位置在
.git/refs/remotes/origin/ 复制代码
最后根据远程默认分支建立本地分支
远程分支是只读的,不能修改。要想在本地修改,需要建立本地分支。clone 命令默认会根据远程默认分支建立本地分支。
cat .git/refs/remotes/origin/HEAD 输出:ref: refs/remotes/origin/master 复制代码
确认远程默认分支为 master 后,clone 命令接着建立本地 master 分支,并和远程 master 分支做关联。
clone 命令完成后,我们现在就可以在本地 master 分支修改,提交,最后执行 git push
,把 master 分支更新到远程仓库。git push
是省略写法。因为现在只有一个仓库,本地 master 也只有一个远程跟踪分支。完整写法:
git push origin master:master 复制代码
执行 git push
这个简写命令如果失败,请检查 git 配置 push.default=simple
git 的默认设置就是 simple。 如果没有动过,就不用管它。查看 git 配置:
git config --list 复制代码
修改远程仓库的其它分支
如果想修改其它远程仓库的其它分支怎么办?很简单,只需要一句命令:
git checkout dev 复制代码
这个命令完成了两项工作。
- 建立本地分支 dev
- 本地 dev 跟踪远程 dev
我们在本地分支 dev 下修改,提交,最后执行 git push
,把 dev 分支更新到远程仓库的 dev 分支。
git checkout 的分支必须已经 fetch 到本地,否则报错。可能得请先执行 git pull
新建远程仓库没有的分支
如果新建的分支远程仓库里没有,需要先新建本地分支。
# 建立本地分支 feature git checkout -b feature # 做修改,提交到本地. git commit ... # 提交到远程,会在远程新建 feature 分支 git push --set-upstream origin feature 复制代码
git push --set-upstream origin feature
有两个作用
- 让本地分支 feature 跟踪远程分支 feature。
- push feature 到远程,远程仓库如果没有,新建。
你肯定好奇,如果远程仓库有 feature 分支会怎么样?那肯定失败。如果远程仓库有 feature ,请按上面的 “修改远程仓库的其它分支” 的方案执行。
git ls-remote ,这个命令直接访问远程仓库,查看获得远程分支的完整列表。根据这个列表,你就知道是新建还是修改了
按上面的方法来做,无需自己去设置跟踪分支,git 自动给你完成,push 、pull 命令也会简化。虽然在技术可以从一个远程分支 check 出多个本地分支,但在实操上不建议这么做。一个远程分支只 check 一个同名的本地分支。如果有多个任务同时进行,可以从这个本地分支 check 出多个分支。任务完成,所有分支合并到这个唯一本地跟踪分支,由这个分支同步到远程仓库。
clone 整个仓库
clone 命令默认是 clone 整个仓库。虽然有两个参数,可以 clone 部分仓库,--depth
和 --single-branch
,但是对于新手来说,不要用这两个参数。因为设置这两个参数后, 大概率会影响后面的正常使用。收益只是首次 clone 的时候快一些而已,得不偿失。
如果仓库确实需要放很多很大的文件,可以考虑用中央式版本管理系统,比如 svn。
git add
添加内容到暂存区。
git add file 添加一个文件 git add dir 添加一个目录,包括已经删除的文件 git *.c 添加以 .c 结尾的文件 git add . 添加当前目录所有文件,.gitignore 指定的文件除外 git add -u 添加暂存区已有的文件(包括删除操作),但不添加新增的文件 复制代码
不添加文件,仅展示文件是否存在或是否忽略。
有时我们修改了 .gitignore 文件,想看看有没有生效。比如我们想忽略 *.c
文件
如果忽略生效的话,会执行 add 命令的时候会给出提示。
git add -n a.c The following paths are ignored by one of your .gitignore files: a.c 复制代码
允许添加已被忽略的文件
我们的设置是 忽略 *.c
,但有一个 config.c 文件不能忽略。因为只有这一个文件 ,又不想去修改 gitignore 文件,可以强制添加。
git add -f config.c 复制代码
添加成功后,config.c 的改动会被正常跟踪。
git rm
删除已经被跟踪的文件。如果文件未被跟踪,直接 rm
就行。
如果文件已经暂存或提交,需要用 git rm 命令
。
git rm a.txt 如果工作区没有修改, 删除工作区和暂存区文件 git rm --cached a.txt 如果工作区没有修改, 只删除暂存区文件。 git rm -f a.txt 强制删除工作区和暂存区,不管工作区有没有修改 git rm -f --cached a.txt 强制删除暂存区,不管工作区有没有修改 复制代码
如果被删除的文件已经提交过,需要再次提交,完成删除。
git commit -m '删除 a.txt' 复制代码
git diff
工作区与暂存区
git diff 比较所有有变化文件 git diff file 比较特定文件 复制代码
暂存区和本地仓库
默认是暂存区和 HEAD 的比较
git diff --cached 比较暂存区和 HEAD 所有变化文件 git diff --cached file 比较暂存区和 HEAD 特定文件 复制代码
工作区与本地仓库
可以和任意本地仓库的 commit id 作比较
git diff e69de29 和 commit id 比较 git diff HEAD 和 HEAD 比较 git diff dev 和 dev 分支比较 复制代码
查看两次提交之间的差异
中间可以写空格,也可以写两个点,效果一样。
git diff commit1 commit2 git diff commit1..commit2 复制代码
查看两次提交的 commitA 与 commitB 的 merge base 和 commitB 的差异。
有点拗口。先解释一下 merge base 。
如图,commit C 和 commit G 的 merge base 就是 commit E
C 和 G 的 merge base 是 E,C...G
表示 E 和 G 的 diff。
git diff C...G 等价于 git diff $(git merge-base C G) G git merge-base 用来获取 C G 的 最近的基点 复制代码
这种三个点的 diff 还是很有用的。比如我们想看下 topic 分支自从打分支以来一共做了哪些修改,可以用 git diff G...C
。
前面的 git diff G...C
也可以用分支名,git diff master...topic
。
可以查具体的文件。
git diff G...C -- a.txt
其中的 -- 是为了分隔文件名,一般情况下可以省略。
直接比较两个文件的差异
直接比较的意思是,不涉及版本,直接比较当前两个文件的内容。
git diff --no-index a.txt b.txt 复制代码
git log
git log dev 查看 dev 分支的 log git log -p 每次提交所引入的差异 git log --stat 每次提交的简略统计信息 git log --name-status 比 --stat 更简洁,更常用。 git log -2 最近两次提交 git log --since=2.weeks 最近两周内的提交 git log --since=2020-8-26 --until="2018.03.18" 日期两种写法都可 还可以用 / 分隔 git log --author=hongwei 只看 hongwei 的提交 git log --grep=登录 搜索提交信息中有登录的的交 git log --grep='auto commit' 如果搜索信息有空格,需要加上引号 git log -S login 在添加或删除的内容里搜索 包含login 的提交 git log --no-merges 不显示合并提交 复制代码
上面列出的一些用法算是开胃小菜,下面介绍重量级的格式化功能。
git format
如果用默认输出的格式,看起来不大方便,根据你的需求,可以设置输出格式。
选项 | 说明 |
%H |
提交的完整哈希值 |
%h |
提交的简写哈希值 |
%T |
树的完整哈希值 |
%t |
树的简写哈希值 |
%P |
父提交的完整哈希值 |
%p |
父提交的简写哈希值 |
%an |
作者名字 |
%ae |
作者的电子邮件地址 |
%ad |
作者修订日期(可以用 --date=选项 来定制格式) |
%ar |
作者修订日期,按多久以前的方式显示 |
%cn |
提交者的名字 |
%ce |
提交者的电子邮件地址 |
%cd |
提交日期 |
%cr |
提交日期(距今多长时间) |
%s |
提交说明 |
我们可以从上面的选项中选择需要的选项。我的format 是这样的
git log --pretty=format:"%h %an %ad %s" --date=short --graph 复制代码
--date 可选值可以到这里去找 git-scm.com/docs/git-lo…
--date=short 按 2022-08-26 的短格式显示日期。 --graph 图形化显示,主要是用来看分支的关系的。
只查看分支log
默认情况下,在分支执行 git log
会显示打分支前和打分支后的所有 log。只查看分支log ,需要这样写:
git log dev ^master --reverse --reverse 表示按正序显示,默认是倒序显示的。 复制代码
dev ^master
显示在 dev,不在master 的提交,也可以这样写
git log master..dev 复制代码
可以这样理解,从 master 到 dev 需要哪些更改呢?两种写法都可以。
分支名改成 commit 也是可以的,用分支名代表的就是最后一个 commit
git log commit..commit 复制代码
除了两个点的,还有三个点的,表示排除 master,topic 共有的,剩下的所有提交。
git log master...topic 相当于 git log master topic --not $(git merge-base --all master topic)
还是看这张图 git log master...topic
的结果是 ABCFG
。
前面讲过 diff , 三个点也是与 merge-base 有关。