目录
一、背景
话说早期用SVN管理代码,后面公司慢慢改成了用gitlab管理,gitlab也是用了两年了,但是对git命令一直处于朦朦胧胧的状态,因为所有关于git的操作都是在eclipse或者idea里点点点,很少用gitbash,虽然已经装了很久了。突然觉得还是有必要去系统的学习下git命令,争取以后的操作都用gitbash~~~~
二、关于git需要知道的一些概念
1.git是干什么的
首先免费、开源,然后是分布式版本控制系统。
通俗的说,就是管理文件,文件可以是代码、图片、配置文件等等,假如这些文件有多个版本,然后我们就可以用git这个东西去管理它们,而且git管理的非常好,比你自己人为管理更加可靠,快速。
至于分布式,意思就是说git安装到本地后我们可以在我们计算机本机进行文件版本管理,也能支持连接到远程文件管理(例如github、gitlab),当然没网的情况,通常在本地进行文件版本管理即可。
2.工作目录
工作目录就是你文件所在的地方,比如d:/workspace/project。
工作目录包含了被git管理的文件以及还没有被git管理的文件。
这也是git初始化的起始的目录。从此目录执行git init命令,则代表该目录及其子目录已经被git管理起来了并在该目录下会生成.git目录。
3.暂存区
英文名:Staging Area。暂存区也称为Index区,Index这个单词在eclipse的git管理中经常打交道~
要使git管理我们的文件,首先我们要把文件提交给git去管理,它不会自己识别文件自己管理(没那么智能)。
例如:我在工作目录新建了一个abc.txt文件,我们要执行git add提交给git,告诉git,帮我管理abc.txt
这时候,这个文件就会被存入到git的暂存区内。
4.存储库
英文名:Repository
这是git最终存储不同版本文件的地方,我们通常会把暂存区的文件,commit到存储库中,最终让git把文件存储起来。
5.Untracked
这个单词在git中比较常见,代表未被git管理的文件的状态。例如我在工作空间新建了一个abc.yml,这个文件起始状态为Untracked
6.git管理文件关注的是什么?
git管理过的文件,文件名称并不重要,重要的是文件的内容,git是根据内容计算出sha-1的commit的id的值的。
7.git对目录是无感知的,它管理不了目录,除非目录下有文件
8.有些文件不想被git管理怎么办?
工作目录中,有些文件不想被git管理的话,就新建一个.gitignore文件,把不想管理的文件名加进去即可。
9.HEAD是什么?
我们经常能看到HEAD这个单词,它仅仅代表了当前分支的意思,假如工作空间在master分支,HEAD代表master、在dev分支,HEAD代表dev。
10.commitId是通过SHA-1算法算出来的,可能会重复,但是概率极低
11.git的终极目录
工作空间的.git目录里存储这git的一切~
12.git的每次commit,也就是那个commitId,都代表了一对象,一个对象指向了一个Tree,Tree又会指向别的对象和Tree,每次checkout commitId就像获取一串葡萄一样。
13.branch分支的理解
branch只是一个指向标记,指向某个commit上。branch可以删除,但不影响commit。
14.所有的分支是共享存储区的
15.ORIG_HEAD是什么?
ORIG_HEAD存储着最近一次危险动作(merge、rebase、reset等)的SHA-1的值,方便跳转到危险动作之前的状态
16.标签tag理解
tag只是commit的一个标记,当commit有特殊意义的时候,可以打个tag。
17.什么是detached HEAD?
断头状态,代表HEAD没有指向任何分支状态。HEAD此时指向的是某次commitId。
18.orgin
按照惯例通常orgin代表远端节点默认名称。
三、git 命令总结
(<>扩折号代表参数,输入参数时,不需要加<>)
1.git init:使git管理当前目录以及子目录,对当前目录以及子目录进行版本控制。
2.git status:最常用的命令之一,查看当前目录下所有文件版本状态信息,这里可以看到有未被git控制文件状态等
3.git add <文件名> :将新文件交给git服务器进行版本管理,支持通配符号,例如:*.html。支持所有: git add --all。
在暂存区的文件,文件被修改后,需要再次git add。
4.git commit -m "提交描述信息" :将暂存区的文件提交到存储库。commit命令只是操作的index区,也就是暂存区。
5.git log:查看commit的记录,精简的格式:git log --oneline --graph.
查看某个人的commit记录: git log --oneline --author="xxx"
查看某个关键字的commit记录: git log --oneline --grep="xxx"
查看一段日期内的commit记录: git log --oneline --since="9am" --until="12am" --after="2017-02"
查看具体文件的commit记录及详情(-p):git log -p <文件名>
6.git rm <文件名>:真实删除文件值并放置暂存区。
不真实删除文件,只是摆脱git控制:git rm <文件名> --cached
7.git mv <旧的文件名> <新的文件名字> : 修改文件名称,直接将改变放置暂存区
8.git commit --amend -m "修改的信息":修改最后一次提交的信息。
9.git commit --amend --no-edit :将暂存区的内容,合并到最近一次的commit提交中。
10.git blame -L <起始行>,<结束行> <文件名称> : 查看行数范围内代码的提交作者,提交id等
11.git checkout . : 从git中恢复所有缺失文件到工作空间,或者某个文件后悔修改了想还原,也可以用此命令
从git中恢复某一个文件到工作空间 git checkout <文件名>
从git中恢复至某一commitId状态到工作空间 git checkout <commitId>
12.git reset <commitId>:默认参数为:--mixed;撤销某次提交,或者称为恢复到commitId提交的状态。同时被撤销的文件修改的内容也会从暂存区删除,但不会删除工作空间的文件。
恢复到master前几次(^,前几次就有几个^)提交:git reset master^
参数:--soft;此参数不会删除暂存区的内容
参数:--hard:此参数会删除暂存区的文件,也会删除工作目录中的文件
如果记得当时的commitId,即使删除错了,也可以恢复到之前的commitId版本: git commit <commitId> --hard
如果忘记了当时的commitId,通过git reflog(默认保存30天)找到之前的commiId,然后利用 --hard 进行恢复。
13.git branch: 查看git中所有分支。--remote:可以查看远端分支的情况。
14.git branch <branchName>: 新增一个分支。默认以当前的分支内容创建新的分支。
15.git branch -m <oldName> <newName>:修改分支的名字
16.git branch -d <branchName>:删除分支,当分支没有合并会提示删除失败。-D:强制删除
17.git checkout <branchName>:切换分支。切换分支并不影响工作目录中的新的文件,也就是Untracked文件
18.git merge <branchName>:合并分支。例如分支的提交合并到master中,首先切换到要master,再执行此命令。
19.git branch <branchName> <commitId> : 以某次commit生成新的branch,用于创建新的分支,或者分支误杀恢复
20.git rebase <branchName> : 合并分支。基于某一分支合并。rebase会重新计算sha-1的值,也就是commitId,对已经push的并不友好。
21.git reset ORIG_HEAD --hard:跳转到危险动作之前的状态(reset、merge、rebase操作之前的状态)
22.git branch <new_branch_name> <commitId> :以某次commit生成新的分支。
23.git rebase -i <commitId> :进入互动模式来修改commit信息,展示了从最后一次commit到 <commitId>之间所有的提交信息。
修改提交信息:把pick改为reword,保存,输入要修改后的信息,保存即可。此修改会重新生成修改commitId之后所有提交的commitId
合并提交:把pick改为squash,保存,输入要修改后的信息,保存即可。
删除某次提交:把pick改为drop,保存。
24.git revert HEAD --no-edit:取消最后一次提交,--no-edit表示不编辑commit信息。revert会产生一次commit,可以让别人看见的撤销commit操作,reset直接删除commit操作
25.git tag <tagName> <commitId> -a -m "注释":以某次commit打出一个tag,-a代表创建注释,-m代表注释的值
26.git show <tagName> :展示tag的详细信息。
27.git tag -d <tagName>:删除tag标签。
28.git stash:将修改的文件暂存起来,无法存储Untracked的文件(需要-u参数)
29.git stash list:查看stash存储列表
30.git stash pop <stash名称> :取出stash套用到当前分支上,pop出的stash将不会出现在stashlist中,代表被取用,pop看出这是一个栈,最后放入在栈顶。
31.git stash drop <stash名称>:从stash列表中删除stash
32.git fsck --unreachable :查询无用tree、blob、commit等
33.git gc --prune=now:立刻马上清除unreachable的事件
远程命令,就是与github或者gitlab交互的命令:
1.设置远端SSH节点地址:git remote add origin git@127.0.0.1:private/git-practise.git
2.将本地master repository的文件推向远端origin节点:git push -u origin master
3.git fetch <origin>:将远端的内容抓取到本地,同时会使origin/HEAD和orgin/master指向抓取到的内容上,同时会使得本地master和origin/master内容不一致
4.git merge origin/master:fetch过后,使得本地master和origin/master保持同步
5.将远端origin节点的内容更新到本地git仓库:git pull origin master,其中git pull = git fetch + git merge
6.git clone git@127.0.0.1:private/git-practise.git <new-file-name>:将远程代码拉取到本地,并取个新名字。
7.git remote -v :展示remote节点的具体信息
四、git命令实战
1.如果解决合并分支冲突?
假如两个分支都修改了同一个文件,此时会merge或者rebase都会提示冲突:
对于git merge 产生的冲突解决办法:
(1)git status 查看哪个是冲突文件
(2)修改冲突文件保存
(3)git add 冲突文件到暂存区
(4)git commit -m "conflict fixed" 即可
对于git rebase产生的冲突解决办法:
(1)git status 查看哪个是冲突文件
(2)修改冲突文件保存
(3)git add 冲突文件到暂存区
(4)git rebase --continue 即可
2.如果合并冲突的不是一个普通文件,例如图片,如何解决冲突?
(1)如果要用自己的图片,则先 git checkout --ours a.png,然后git add 到暂存区,最后commit
(2)如果要用别人的图片,则先 git checkout --theirs a.png,然后git add 到暂存区,最后commit
3.当需要切换分支,保存当前进度:
一、先提交代码,然后reset接着解决,reset用mix模式,会保留工作空间内容,但不会保留存储区的内容
(1)git add --all
(2)git commit -m "not finished yet"
(3)git reset @^
二、使用stash命令
4.想要删除提交中的某个文件,以config/database.yml为例:
git filter-branch --tree-filter "rm -f config/database.yml"
5.从其他分支里捡过来某次提交记录到自己的分支中:
捡过来合并:git cherry-pick <commitId> ...,捡多个话,后面接多个<commitId>即可。
捡过来不合并:git cherry-pick <commitId> --no-commit
6.与他人合作同一个项目的时候,如何远使用程操控和本地仓库,减少最少的操作错误?
(1)先拉再推
每次提交本地代码到远程时,先把远程的代码拉下来,这样减少推送的错误。
(2)强制推送,无视规则
git push -f,这样会覆盖掉同事提交的代码,呵呵~
7.如何在github上,修改别人的开源项目源码,提交自己代码?
首先,别人的代码你是没有权限pull的,虽然是开源的,你也只能看,如果想要贡献源码或者改bug,可以提交一个Pull Request(PR),具体如下:
(1)首先fork别人的项目到自己的github中
(2)修改代码,提交代码到自己github中
(3)在github中新建一个New pull request,写上信息,发送即可。
(4)原作者在 Pull Request中就能看见你的pull request,选择是否合并
8.使用了git push -f 怎么办?
会强制提交自己的代码覆盖被人提交的代码。
首先我们要对分支启用保护机制,到gitlab中的项目的settings中找到Repository,找到repository setting,找到protected branch,将分支进行保护,这样避免了使用-f强推
如果已经强推了怎么办,让另一个同事再强推一遍,然后你改完再提交即可。
9.如何根据某个commitId创建新的分支?
先查询git的commitId : git log --oneline
然后根据commitId,执行如下命令:git branch 新的分支名称 某次commitId
五、常见git错误
1.fatal: refusing to merge unrelated histories
执行git pull origin master时候,或者其它时候报错fatal: refusing to merge unrelated histories,由于gitlab上创建的项目自个新建了文件,有了更新
使用git pull origin master --allow-unrelated-histories即可
2.[rejected] master -> master (non-fast-forward)
由于gitlab上创建的项目自个新建了文件,有了更新,要先执行git pull origin master操作
3.remote: GitLab: You are not allowed to push code to protected branches on this project.
遇到这个错误,一般来说是你想push代码到master主分支上,而你的权限并不是项目的所有人。
就是你没有权限这么做,gitlib默认master分支是受保护的,除了项目所有者,其它角色是不能直接提交的。
你只能创建单点分支,提交到git上,然后发起Merge Requests请求,让项目所有者去审核,审核通过后,由项目所有者来进行merge
这才是git提交代码到主分支正确的步骤~~~~
4.fatal: Not a git repository (or any parent up to mount point
没有执行git init,就想用git管理?所以要先 git init,再执行别的命令
六、如何与他人合作
1.git flow:这种方式最好,但是比较复杂,需要程序员学习git知识才能协作的好一点,具体过程可自行查询
2.github flow:这种方式就是大家都用一个master分支,要求程序员代码能力强悍,否则会搞出一堆问题。
3.gitlab flow:大家提交在主分支提交代码,版本控制用新建的分支控制
这里讲的比较简单,我还在摸索中,具体可见:git flow、github flow、gitlab flow
关于git学习和使用中问题,会慢慢补充到此文章中~