Git 实用指南

前言

分布式 VCS 和中央式的区别在于,分布式 VCS 除了中央仓库之外,还有本地仓库:团队中每一个成员的机器上都有一份本地仓库,这个仓库里包含了所有的版本历史,或者换句话说,每个人在自己的机器上就可以提交代码、查看历史,而无需联网和中央仓库交互。

优点

  1. 大多数的操作可以在本地进行,所以速度更快,而且由于无需联网,所以即使不在公司甚至没有在联网,你也可以提交代码、查看历史,从而极大地减小了开发者的网络条件和物理位置的限制。
  2. 由于可以提交到本地,所以你可以分步提交代码,把代码提交做得更细,而不是一个提交包含很多代码,难以 review 也难以回溯。

缺点

  1. 由于每一个机器都有完整的本地仓库,所以初次获取项目的时候会比较耗时。
  2. 由于每个机器都有完整的本地仓库,所以本地占用的存储比中央式 VCS 要高。

实用指南

  1. 把远程仓库取到本地
1
git clone 远程仓库地址
  1. 把写完的代码提交
1
2
3
4
5
6
7
8
9
10
11
添加文件到暂存区
git add . 【添加所有文件到暂存区,add也支持匹配表达式,如 git add *.java】
git add 文件名 【添加指定文件到暂存区】
git commit -m '你提交的信息'

注意:直接执行 git commit 时可能弹出“输入提交的信息界面”,需要按一下 "i"(小写)来切换到插入模式,然后就可以输入你的提交信息了;在输入完成后别按回车,而是要按 ESC 键返回到命令模式,然后连续输入两个大写的 "Z",就保存并退出了。

在这个过程中,可以使用 git status 来随时查看工作目录的状态:
每个文件有 "changed / unstaged"(已修改), "staged"(已修改并暂存), "commited"(已提交) 三种状态,以及一种特殊状态 "untracked"(未跟踪)提交一次或多次之后,把本地提交 push 到中央仓库(git push)

注意:如果 push 失败,就用 pull(git pull) 把本地仓库的提交和中央仓库的提交进行合并,然后再 push 一次

git log 列出提交历史
git log -p 查看详细历史
git log --stat 查看简要统计
git show 查看具体的 commit
git diff --staged 比对暂存区和上一条提交
git diff 比对工作目录和暂存区
git diff HEAD 比对工作目录和上一条提交

  1. .gitignore——排除不想被管理的文件和目录

gitignore

1
2
3
.gitignore,这个文本文件记录了所有你希望被 Git 忽略的目录和文件。
文件中 # 打头的是注释文件,其他的都是对忽略文件的配置。
匹配规则:https://git-scm.com/docs/gitignore
  1. 偏移符号
1
2
3
4
5
在 Git 中,有两个「偏移符号」: ^ 和 ~。

^ 的用法:在 commit 的后面加一个或多个 ^ 号,可以把 commit 往回偏移,偏移的数量是 ^ 的数量。例如:master^ 表示 master 指向的 commit 之前的那个 commit; HEAD^^ 表示 HEAD 所指向的 commit 往前数两个 commit。

~ 的用法:在 commit 的后面加上 ~ 号和一个数,可以把 commit 往回偏移,偏移的数量是 ~ 号后面的数。例如:HEAD~5 表示 HEAD 指向的 commit往前数 5 个 commit。

HEAD、master 与 branch

1.HEAD
2.master
3.branch
--------------------------------------------------------------------
1.HEAD 是指向当前 commit 的引用,它具有唯一性,每个仓库中只有一个 HEAD。在每次提交时它都会自动向前移动到最新的 commit

2.branch 是一类引用。HEAD 除了直接指向 commit,也可以通过指向某个 branch 来间接指向 commit。当 HEAD 指向一个 branch 时,commit 发生时,HEAD 会带着它所指向的 branch 一起移动。

3.master 是 Git 中的默认 branch,它和其它 branch 的区别在于:
    a.新建的仓库中的第一个 commit 会被 master 自动指向;
    b.在 git clone 时,会自动 checkout 出 master。

4.branch 的创建、切换、删除和提交:
    a.创建 branch 的方式是 git branch 名称 或 git checkout -b 名称(创建后自动切换);
    b.切换的方式是 git checkout 名称;
    c.push 的时候,如果当前分支是一个本地创建的分支,需要指定远程仓库名和分支名,用 git push origin branch_name 的格式,而不能只用 git push;
      或者可以通过 git config 修改 push.default 来改变 push 时的行为逻辑。
    d.git branch -a 看到所有分支

5.branch 的删除:
    a.git branch -d 名称。
    b.git push origin -d branch_name # 用 -d 参数把远程仓库的 branch 也删了
    c.git push origin :branch

注意:
1. HEAD 指向的 branch 不能删除。如果要删除 HEAD 指向的 branch,需要先用 checkout 把 HEAD 指向其他地方。
2. 由于 Git 中的 branch 只是一个引用,所以删除 branch 的操作也只会删掉这个引用,并不会删除任何的 commit(一定时间后,会被 Git 的回收机制删除掉)
3. 出于安全考虑,没有被合并到 master 过的 branch 在删除时会失败(如果你确认是要删除这个 branch,可以把 -d 改成 -D)

Merge

从两个 commit「分叉」的位置起,把目标 commit 的内容应用到当前 commit(HEAD 所指向的 commit),并生成一个新的 commit;

适用场景

  • 合并分支

    1
    2
    1. 当一个 branch 的开发已经完成,需要把内容合并回去时,用 merge 来进行合并。
    2. git merge branch1
  • pull 的内部操作

    1
    1. pull 的实际操作其实是把远端仓库的内容用 fetch 取下来之后,用 merge 来合并。

冲突

  1. 解决冲突后手动 commit
  2. git merge –abort 【放弃解决冲突,取消 merge】

Feature Branching:工作流

  • 任何新的功能(feature)或 bug 修复全都新建一个 branch 来写;
  • branch 写完后,合并到 master,然后删掉这个 branch。

Git合并特定commits 到另一个分支

  • 合并某个分支上的单个commit
1
2
3
4
5
1. git log 查看想选择哪些commits进行合并
eg:dd2e86 - 946992 -9143a9 - a6fd86 - 5a6057
2.需要将62ecb3 合并到master,而不合并feature上的其他commits
git checkout master
git cherry-pick 62ecb3
  • 合并某个分支上的一系列commits
1
2
3
4
5
6
假设需要合并feature分支的commit dd2e86 ~a6fd86 到master分支。

首先需要基于feature创建一个新的分支,并指明新分支的最后一个commit:
git checkout -b newbranch a6fd86
然后,rebase 这个新分支的 commit 到master,dd2e86^ 指明你想从哪个特定的commit开始。
git rebase --onto master dd2e86^

rebase——在新位置重新提交

git rebase 目标基础点

场景:
我在主分支commit a时新建了新分支,此时开始分叉,分叉后我又在主分支改了东西commit b,此时我后悔了,我不该在commit a时分叉的。
因为commit b的东西我新分支也需要,此时用衍合(rebase)。
就等于我丢弃原分叉,在commit b重新分叉(原分叉的改动内容当然也是带上的,没有丢)

注意:为了避免和远端仓库发生冲突,一般不要从 master 向其他 branch 执行 rebase 操作。
而如果是 master 以外的 branch 之间的 rebase(比如 branch1 和 branch2 之间),就不必这么多费一步,直接 rebase 就好。

其它特性

git commit --amend 对最新一条 commit 进行修正,生成一条新的commit替换了原commit。
注意:commit之后,push之前使用有效;只针对目前最新的 commit 有效

git reset --hard 目标commit  撤销最新的提交

撤销过往的提交
1. 用 git rebase -i 在编辑界面中删除想撤销的 commits
2. 用 git rebase --onto 在 rebase 命令中直接剔除想撤销的 commits

git push origin branch1 -f  忽略冲突,强制 push
git revert HEAD^ commit 撤销提交

reset 的三种参数:
1. --hard:重置位置的同时,清空工作目录的所有改动;
2. --soft:重置位置的同时,保留工作目录和暂存区的内容,并把重置 HEAD 的位置所导致的新的文件差异放进暂存区。
3. --mixed(默认):重置位置的同时,保留工作目录的内容,并清空暂存区。

场景:
当你手头有一件临时工作要做,需要把工作目录暂时清理干净,那么你可以:
git stash -u (扔)
git stash pop (取)

恢复已删除的 branch
1. git reflog 查看一下 HEAD 的移动历史
2. git checkout c08de9a
3. git checkout -b branch1
注意:不再被引用直接或间接指向的 commits 会在一定时间后被 Git 回收

tag:不可移动的 branch,tag 被用来在关键版本处打标记用。
git config: Git 的设置 https://git-scm.com/docs/git-config
cherry-pick:把选中的 commits 一个个合并进来

参考:

https://git-scm.com/docs

------本文结束感谢阅读------
显示评论
0%