本文不是教程大全,也不过多探讨底层原理,重点在于讲解常用命令,及其正确的使用方法。因为总有新人甚至是工作两三年的人仍然会犯一些解决代码冲突时覆盖别人代码之类的错误。本文目标读者是对git有一定了解,已经安装使用过,可以进行简单的提交合并等操作,但偶尔会出错;知道一些git的概念,但又不是很清晰透彻。本文力求准确简洁,如有纰漏之处敬请指正。
为了保证效果,先把重要的概念阐述一下,有助于后续的理解。
Git是一个开源的分布式版本控制系统,用以有效、高速的处理从很小到非常大的项目版本管理。(百度百科)
分布式: 指每个客户端都有完整的数据和历史提交记录。开发者不需要联网就可以本地提交。
Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified) 和 已暂存(staged)。
这会让我们的 Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录。
如果 Git 目录中保存着特定版本的文件,就属于已提交状态。 如果文件已修改并放入暂存区,就属于已暂存状态。 如果自上次检出后,作了修改但还没有放到暂存区域,就是已修改状态。
在指定的目录或新创建的目录下执行:
git init
(以下几条命令中project指项目名)
使用http协议:git clone http://ip:port/项目路径/project.git
使用ssh协议:git clone ssh://[user@]server/project.git
使用git协议: git clone git@域名:path.../project.git
以上三种格式都是通过网络克隆远程服务器的git仓库,如果git仓库在一个共享的文件系统上,也可以使用本地协议克隆该仓库,由于不经常使用,本文不再列举。
获取git仓库之后就要进行本地开发了,通常一个项目有多个人员共同开发,为了减少冲突、方便管理,每个人都会创建自己的分支进行开发。通常每个任务创建一个分支,如果是新增功能性的开发,分支前缀可以是feature-,后面加任务编号或者缩写;如果是bug性的修复,分支前缀可以是bugfix-。
先明确几个概念:
origin,指远程库的别名或主机名。
origin/master, 指远程库master分支在本地的副本。
当克隆仓库或从远程检出分支到本地时,会自动将本地与远程对应的分支建立追踪关系,如master分支追踪origin/master分支,可以通过git branch -vv查看追踪关系:
上图有三个分支,a分支对应origin/a分支;master分支对应origin/master分支。
也可以手动添加追踪关系:git branch --set-upstream-to=origin/next b 该命令指定b分支追踪origin/next分支。
拉取远程代码并合并到本地分支用git pull;只拉取远程代码(更新),不合并用git fetch:
git pull <远程主机名> <远程分支名>:<本地分支名>
从远程获取代码并合并到本地的版本
假设当前分支为branch-L,默认远程主机名origin,则:
git pull -------- 从默认远程主机origin 获取branch-L分支代码并合并到本地分支branch-L
git pull origin dev -------- 从远程origin 获取dev分支的代码并合并到本地分支branch-L
git pull origin dev:branch-L -------- 从远程origin 获取dev分支的代码并合并到本地分支branch-L
git fetch <远程主机名> <远程分支名>
git fetch 更新所有远程库代码
git fetch origin 更新origin对应远程库的代码
注:git pull 相当于(不完全一样) git fetch + git merge
假设本地当前分支为dev,则
git pull origin dev 相当于 git fetch origin dev + git merge origin/dev
常用场景:在自己的分支branchA上开发完成,提交到远程,并在远程合并到dev分支时提示有冲突,这时需要本地更新下远程dev分支代码,并将其合并到branchA,遇到冲突解决掉,然后再把branchA分支push到远程。假设当前分支为branchA:
更新远程dev分支代码:git fetch origin dev
将远程dev分支代码合并到当前分支:git merge origin/dev
(此时有冲突解决冲突,并提交)
将当前分支的变更推送到远程:git push
git-merge - Join two or more development histories together
可见git merge命令是将两个或多个历史开发记录合并到一起。注意后面不管跟几个分支,最后都是合并到当前分支。假设当前分支为a,git merge b 是将分支b与a合并到a;git merge b c 是将分支b、c与a合并到a。
有时候待合并的分支和当前分支对同一个文件的相同部分作了不同的修改,这时候Git就没法干净的合并它们,提示有冲突,需要我们解决冲突。
假设当前分支为master主分支,此时想把iss53的修改合并到主分支上:
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
此时 Git 做了合并,但是没有自动地创建一个新的合并提交。 Git 会暂停下来,等待你去解决合并产生的冲突。 你可以在合并冲突后的任意时刻使用git status命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件:
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add ..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段,看起来像下面这个样子:
<<<<<<< HEAD:index.html
=======
>>>>>>> iss53:index.html
这表示HEAD所指示的版本(HEAD是指向当前分支的指针,可以理解为当前分支的别名)在这个区段的上半部分,而iss53分支所指示的版本在========的下半部分。 为了解决冲突,你必须选择使用由========分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:
上述的冲突解决方案仅保留了其中一个分支的修改,并且<<<<<<<,=======, 和>>>>>>>这些行被完全删除了。 在你解决了所有文件里的冲突之后,对每个文件使用git add命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。
注意:这里需要注意解决冲突的时候是使用某一个分支的版本还是,各取一部分,需要根据实际情况。如果两个分支的修改都是你做的,你知道哪个版本更新,可以自己决定。如果不知道哪个更新,比如有几行其他人改过了,这时候需要根据代码记录找到对应的修改人,双方协商确认一下。否则容易造成代码丢失的现象。切忌在不是完全知情的情况下,私自覆盖他人的代码!!!!!!!
git add index.html
git commit -m "merge branch iss53 into master: #Conflicts: index.html "
将处理好的冲突文件修改添加到暂存区,然后提交可。
注意: 解决完冲突,将冲突文件的修改已经添加到暂存区,准备进行提交的时候,如果是在命令行中操作的,可以git status查看暂存区中要提交的文件列表,如果是图形化工具提交的,可以在对话框中查看提交的文件列表。
这时很多新手有个困惑,就是提交的文件列表里除了自己修改的文件,还有很多不是自己修改的,甚至有人手动取消这些文件,再提交。这样是不对的,会导致代码丢失。
暂存区的文件变更列表中会包含从最新状态到两个分支的共同祖先之间的所有变更过的文件列表。注意这里不仅包括自己的修改,也包括合并树的更改。所以此时不应该取消暂存区中的任何变更,直接提交即可。
补充:以上均为命令行操作,便于新手快速熟悉操作过程,并且命令行的功能是最完备的。当基本操作熟悉之后,就可以选择一个常用的图形化工具,使用起来更加方便。当然常用的IDE如eclipse、IDEA 等也都集成了git插件。
1.https://www.git-scm.com/book/zh/v2/
2.https://www.jianshu.com/p/305723736c7c
3.https://www.cnblogs.com/drizzlewithwind/p/4878150.html