之前版本控制一直用的是 svn,没怎么接触 git,项目使用的就是 git,所以专门学习一下同时做个笔记,方便其他想快速了解 git 的小伙伴参考。
本地控制
最流行的是 RCS,在硬盘上保存各个文件的前后变化。
集中化版本控制(CVCS)
典型代表 SVN,由服务器集中记录管理版本变更历史,以及各个开发人员的权限等。
分布式版本控制(DVCS)
典型代表 Git,每个开发人员本地都有一份仓库备份,所有的修改提交都是在本地仓库执行的,最终再将本地仓库与远程仓库对比合并。
使用快照记录,保存当前版本的文件而非版本差异,若文件在当前版本未修改则直接保留链接指向之前的文件。
当在本地修改文件后,会变成已修改状态(modified)
将已修改的文件 add
之后,会变成已暂存(staged),表示下次提交后将存入到本地仓库。
每次提交 commit
都会将暂存区的文件提交到本地仓库中,此时状态为已提交(committed)。
--global 指定修改的是当前用户的配置文件(~/.gitconfig)
--system 指定修改系统所有用户的通用配置文件 (/etc/gitconfig)
修改用户信息
$ git config --global user.name "xxxx" $ git config --global user.email [email protected]
修改文本编辑器(默认的是 vim)
$ git config --global core.editor emacs
检查配置
git config --list
会显示所有 .gitconfig 文件里的配置信息,当需要找某一个是用 git config
。
$ git config user.name xxxx
克隆现有仓库
$ git clone url
如果仓库中包含了子模块,也要一起克隆下来的话,加上 --recursive
$ git clone --recursive url
查看文件状态
$ git status
这样展示的信息有点多,如果喜欢简写的加上 -s
$ git status -s M 1.txt MM 2.txt A 3.txt M 4.txt ?? 5.txt
解释一下上面的标记:A 是添加到暂存区中的新文件,?? 是未被跟踪的文件。M 的分为两种,一种靠右边的是修改未暂存的文件如 1.txt;另一种是靠左边的是修改并加入到暂存区的文件如 4.txt;两个 M 表示文件在添加暂存区后又被修改了,如果直接提交是不包含后面修改的内容的。
忽略文件
有时候我们的项目在运行时会自动生成好多新的文件,此时查看文件状态会有好多我们不需要关心的项。如何屏蔽这些文件呢?
$ vim .gitignore
直接打开这个文件,如果没有的话会在编辑保存后创建。满足此文件中的所有文件都会在 git status
时被忽略掉。匹配规则有点类似正则,# 来注释,具体的可自行查询。
查看修改
有时候我们需要确认暂存区或修改的文件是否是自己想要修改的内容,可以使用 diff
来查看:
$ git diff 文件名
不指定文件的话就是显示的未暂存的文件的修改内容;如果需要查看暂存区中文件的修改内容使用:
$ git diff --cached
或
$ git diff --staged
提交更新
当将修改的文件添加到暂存区后,就可以提交到仓库了:
$ git commit
这样的话,会进入到编辑器状态,在里面写上关于这次提交的修改内容的介绍。此外,我们常用更简单的方式:
$ git commit -m "引号里面,写上这次提交的介绍"
上述提交的内容都是暂存区的,如果我们想跳过加到暂存区的操作怎么办?提交时加上 -a
,这样会直接将已经跟踪文件的修改全部都提交到本地仓库中,而跳过 git add
的步骤。虽然步骤简单了,还是不推荐,自己斟酌咯~
删除文件
删除原本在仓库中的某个文件,再也不需要了。
#如果需要保存删除记录(推荐) $ git rm 目标文件 $ git commit -m '删除文件的原因'
移动文件
$ git mv 旧文件名 新文件名
查看提交历史
$ git log
加上 -p
会显示每次提交的更改内容,-数字
用来指定显示的历史条数。git log --pretty=format:"格式"
可用来指定以什么样的格式展示,如有需要自行百度。
某些后悔药
不小心把文件添加到暂存区了,想把所有都撤回来,但是我还要继续修改,还想保留之前的修改:
$ git reset HEAD
不小心把文件添加到暂存区了,只想把某个文件撤回来并保留修改:
$ git reset HEAD 文件名
git reset
会保留工作区的更改,但是一旦加上 --hard
,会将工作区的所有更改还原。
提交后,发现还有些没提交或者提交的文件修改不正确还需要再修改:
$ git commit -m "提交了1.txt" $ vim 1.txt $ git add 1.txt 2.txt $ git commit --amend
操作完后,会进入提交描述文档编辑状态,此方法也可以用来修改描述。
撤销对某个文件的更改
$ git checkout 文件名
远程仓库使用
查看远程仓库的链接
$ git remote -v origin url (fetch) origin url (push)
表明当前拥有拉取代码和推送代码的权限。
$ git remote show origin
会列出远程仓库和本地仓库中的状态,是否最新等信息。
添加远程仓库
$ git remote add
从远程仓库拉取
$ git fetch [remote-name]
这个只会从远程仓库拉取数据但是不会合并,需要自己手动合并到本地仓库
推送到远程仓库
$ git push origin master
推送之前需要保证本地仓库是最新的,查看远程仓库时有显示。
远程仓库重命名
$ git remote rename 旧名字 新名字
远程仓库删除
$ git remote rm 名字
打标签
查看标签
$ git tag
添加附注标签
$ git tag -a 标签名
此操作是在当前的提交上打的标签,如果需要在之前的提交上补加标签,只需要在后面加上提交的校验值。
$ git tag -a v0.1 d6da32d19017cec2cb575a3af49d56ba06321982
添加轻量标签
$ git tag 标签名
删除标签
$ git tag -d 标签名
提交标签
$ git push origin 标签名 # 提交本标签 $ git push origin --tags # 提交本地所有标签
别名
linux 下的 alias 一样,经常使用的指令能够更快捷的使用:
$ git config --global alias.sg 'status'
这样之后就可以直接使用 git sg
来代替 git status
,当我们需要取消某个别名时,如下操作:
$ git config --global --unset alias.sg
如果原本指令不是 git
子命令,如打开可视化界面 gitk
时,只需要在前面加上 “!”:
$ git config --global alias.vs '!gitk'
git 分支功能很强大,建议大家开发过程中多开辟分支,在相应的分支上开发,然后再合进主开发分支。下面根据具体情境来介绍一下分支相关的一些操作。
创建分支
首先我们创建一个开发分支(develop)然后创建文本 1.txt,在此基础上创建一个功能分支(func_1),并在 1.txt 中添加一行 "func_1 第一次添加" 并提交。
$ git checkout -b develop # 创建并切换到 develop 分支 $ echo '这是develop写入' > 1.txt $ git add 1.txt $ git commit -m '第一次提交' $ git checkout -b func_1 # 创建并切换到 func_1 分支
切换分支
此时,我们想回到 develop 分支,再次修改 1.txt,增加一行 “develop 第二次写入” 并提交。
$ git checkout develop # 切换到 develop 分支
合并分支
此时我们想把 func_1 上写入的内容合并到 develop 中,有下面两种方式:
merge 合并
$ git merge func_1 # 将 func_1 中的内容合并到 develop Auto-merging 1.txt CONFLICT (content): Merge conflict in 1.txt Automatic merge failed; fix conflicts and then commit the result.
正常的合并操作,会先尝试自动合并,如果没有冲突,会将 func_1 的内容直接添加到后面,然后 develop 指向当前 func_1 的位置,这叫作快进(fast-forward)。此时,我们属于有冲突的情况,需要先编辑有冲突的文件,解决冲突,保存然后提交。冲突的文件内容如下:
<<<<<<< HEAD 这是develop写入的 develop 第二次写入 ======= 这是develop写入 func_1 第一次添加 >>>>>>> func_1
改后为:
这是develop写入的 develop 第二次写入 func_1 第一次添加
提交保存后,我们来看一下结构是怎么样的:
$ git log --graph --oneline * 6d3d517 (HEAD -> develop) func_1 merge 到 develop |\ | * a2447a5 (func_1) func_1 第一次提交 * | a7cb656 develop 第二次写入 |/ * 852dfe4 第一次提交
rebase 变基
我们首先将上面的合并操作回滚掉,因为 func_1 分支没有改动,所以只需回滚 develop 分支即可:
$ git reset --hard HEAD~ # 回滚到上一次提交 $ git log --graph --oneline # 查看当前提交记录 * a7cb656 (HEAD -> develop) develop 第二次写入 * 852dfe4 第一次提交
首先我们需要先切换到 func_1 上进行变基操作:
$ git checkout func_1 $ git rebase develop First, rewinding head to replay your work on top of it... Applying: func_1 µÚÒ»´ÎÌá½» Using index info to reconstruct a base tree... M 1.txt Falling back to patching base and 3-way merge... Auto-merging 1.txt CONFLICT (content): Merge conflict in 1.txt error: Failed to merge in the changes. hint: Use 'git am --show-current-patch' to see the failed patch Patch failed at 0001 func_1 µÚÒ»´ÎÌá½» Resolve all conflicts manually, mark them as resolved with "git add/rm", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort".
同样,变基过程中出现了冲突,我们先解决冲突,然后执行:
$ git add 1.txt $ git rebase --continue Applying: func_1 µÚÒ»´ÎÌá½»
Ps:如果在 rebase 过程中,你不想进行变基了,--abort 可以取消。
回到 develop 分支,进行 merge 操作:
$ git checkout develop $ git merge func_1 $ git log --graph --oneline * 16aaa1e (HEAD -> develop, func_1) func_1 第一次提交 * a7cb656 develop 第二次写入 * 852dfe4 第一次提交
合并后的 develop 分支跟上面直接 merge 的就不一样了,没有了 func_1 的提交历史了。变基的操作不可逆,建议慎用。建议是:在本地自己开发一个功能,建立分支测试没问题后通过变基合入开发分支中,这样能保证提交记录简洁。而大家共享的分支就不要进行变基操作,用 merge 合并来保留提交历史。