一.Git简介
Git是世界上最先进的分布式版本控制系统,相对于SVN这样的集中式版本控制系统,Git不需要“中央服务器”,每一台电脑都有一个完整的版本库。因此,它的安全性要高很多。Git一般工作流程如下:
- 克隆 Git 远程资源库作为工作目录
- 在克隆的资源库上添加或修改文件
- 如果其他人修改了,则需要相应更新资源
- 在提交前查看修改
- 提交修改
- 修改完成后,若发现错误,撤回提交并再次修改并提交
Git支持Linux/Mac OS/Windows,可根据需求自行安装。安装后需要做如下配置:
- /etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。
- ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。
- 当前项目的 Git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。
每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。根据需要,配置相应的工作环境变量如用户名、邮箱等。
二.版本管理
Git中的文件被分为两种状态,一种是已跟踪状态(tracked),另一种是未跟踪状态(untracked)。只有处于已跟踪状态的文件才被纳入GIT的版本控制,已跟踪状态的文件又分为未修改、已修改、暂存状态,如上图所示。
Git中的工作区域又分为三个:工作区、暂存区、版本库(仓库)
- 工作区
就是在电脑中能看到的目录 - 暂存区
stage或者index,存放在.git目录下的index文件中 - 版本库
工作区有一个隐藏的目录.git,即git的版本库,又名仓库(repository)。简单理解成一个目录,是Git用来跟踪和管理版本库的。工作区所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
创建仓库
步骤1:创建一个空目录,如“mkdir git;cd git”
步骤2: git init
创建完成后,在当前目录下生成一个隐藏目录.git添加文件
步骤1:创建文件readme.txt
步骤2:git add readme.txt,将文件写入暂存区
步骤3:git commit -m "wrote a readme file",将暂存区所有添加到仓库中。(-m 为本次提交说明)查看状态
步骤1:git status,获取仓库当前的状态
步骤2:git diff,查看文件修改的内容版本回退
Git每一次的commit提交为“保存一个快照”,一旦把文件改乱了,或者误删了文件,还可以从其中的一个commit恢复。
步骤1:git log,显示从最近到最远的提交日志
步骤2:git reset --hard HEAD^,回退到上一个版本
注:在Git中,用HEAD表示当前版本,也就是最新的提交,上一个版本就是HEAD^,依次类推;也可用git log显示出来的commit id来标识版本回退(--hard撤销工作区域与暂存区的修改)
步骤3:要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本修改文件
工作区中新建的文件,git status查看时为“Untracked files”状态,当提交到版本库并修改后,
步骤1:git status,状态为“Changes not staged for commit”
步骤2:git add再git commit
注:git commit只会把提交到暂存区的修改,加入到commit中
步骤3:如果我们提交过后发现有个文件改错了,或者只是想修改提交说明,这时可以对相应文件做出修改,将修改过的文件通过"git add"添加到暂存区,然后执行git commit --amend,覆盖上一次提交-
撤销修改
步骤1:git checkout -- file,可以丢弃工作区的修改(注意“--”非常重要,否则表示切换分支)。存在下面几种情况:- 修改后没有存入暂存区,可以直接回到和版本库一样
- 已经添加到暂存区,但未修改,用命令git reset HEAD <file>(即取消暂存),就回到前面1场景
- 已经添加到暂存区,又做了修改,撤销修改就回到了添加到暂存区后的状态
- 若修改已经提交到了版本库,则参考前面“版本回退”
-
删除文件
步骤1:使用rm 删除工作区的文件
步骤2:有两种情况- 情况一:从版本库中删除,即git rm然后提交git commit
- 情况二:删除错了,git checkout -- test.txt将文件恢复到当前版本(注意不要忘了"--")
三.远程仓库
可以自己搭建Git服务器作为远程仓库,也可以使用已有的代码托管服务网站,如GitHub、码云等。
-
GitHub
1.1 配置环境
步骤1:ssh-keygen -t rsa -C "youremail@example.com"生成公私钥(在用户主目录下 "cd ~");生成的公私钥主要用于加密通信内容以及身份校验,私钥自己保留,公钥提供给GitHub
步骤2:登陆GitHub,打开“Account settings”,“SSH Keys”页面:点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容
1.2 添加远程仓库
- 步骤1:登陆GitHub,在右上角找到“Create a new repo”按钮,创建一个新的空的仓库;然后,可以从这个仓库在本地克隆出新的仓库,也可以把一个已有的本地仓库与之关联
- 步骤2:在本地的仓库下运行git remote add origin git@github.com:xxx.git;其中origin是远程库的名字,git@github.com:xxx.git是步骤1创建的远程仓库;使得本地仓库与远程仓库关联
- 步骤3:git push -u origin master,将本地库的所有内容推送到远程库上(-u主要是第一次推送,指定origin为默认主机,后续推送时则不需要)
1.3 从远程库克隆
- 步骤1:类似于1.2中的步骤1,在GitHub网站上创建远程仓库,如名字叫gitskills
- 步骤2:从远程库克隆,git clone git@github.com:xxx/gitskills.git,这样就在本地目录下克隆了一份远程库。Git支持多种协议,如默认的ssh(git://),以及http、https等
1.4 使用GitHub参与开源项目
- 在GitHub上,可以Fork任意开源仓库;
- 自己拥有Fork后的仓库的读写权限,然后在本地克隆一个Fork后的仓库;
- 可以推送pull request给官方仓库来贡献代码
2.自己搭建Git服务器
- 步骤1:安装Git,sudo apt-get install git
- 步骤2:创建用户,sudo adduser git
- 步骤3:创建证书登录,收集需要登录的用户的公钥,把用户公钥导入到/home/git/.ssh/authorized_keys文件中
- 步骤4: 初始化Git仓库,sudo git init --bare sample.git;修改用户sudo chown -R git:git sample.git防止其他用户修改
- 步骤5:禁用shell登录,通过编辑/etc/passwd文件完成
- 步骤6:本地仓库克隆git clone git@server:/srv/sample.git
3.码云
四.分支管理
- 创建和合并分支
在Git里,默认为主分支即master,而HEAD指向当前的分支。一开始master指向最新的提交,而HEAD指向master;当创建并切换到分支dev后,dev指向与master相同的提交,并且HEAD指向dev。当在新的分支dev上提交时,dev指针向前移动,master不变。
- git checkout -b dev,创建并切换到分支dev
- git branch,查看分支
- git branch <name>,创建分支
- git checkout <name>,切换分支
- git merge <name>,合并某分支到当前分支
- git branch -d <name>,删除分支
-
解决冲突
当把feature1分支的修改自动合入master分支时,可能会产生冲突。Git会在文件中使用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们手工修改后再提交。
-
分支管理策略
- master分支主要用来发布版本,比较稳定
- 团队在dev分支上工作,dev分支是不稳定的,等到某个版本稳定时,再合入master分支
- 个人在自己的分支下开发工作,如michael、bob等,再不断的往dev分支上合入代码
-
Bug分支
当你在dev分支上的开发进行到一半时,需要在master分支上去修复一个紧急的Bug。第一步就需要先切换到master 分支。但当你执行 $ git checkout master 命令的时候,会提示出错,需要将当前的分支修改提交后才能切换。怎么办?- 步骤1:git stash,将当前工作现场储藏起来
- 步骤2:git checkout master,切换到主分支并创建bug分支来修复问题
- 步骤3:修复完成后,切回主分支并合并修改,然后删除bug分支
- 步骤4:切换至dev分支,git checkout dev,并执行git stash list查看,git stash pop恢复现场并删除储藏(或者git stash apply stash@{0}恢复,但是恢复后,stash内容并不删除,用git stash drop来删除)
多人协作
- 克隆远程库,同时执行git checkout -b dev origin/dev,创建远程库的dev分支到本地
- 试图用git push origin <branch-name>推送自己的修改;
- 若推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;git pull --rebase origin master,rebase是通过将local commit一个一个地在更新后的master分支上运行;git add <conflicted-files>;git rebase --continue
- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功
- 如果git pull提示no tracking information,则说明本地分支和远程分支的连接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>。
关于Git的工作流请参考:深入理解学习Git工作流
-
Rebase
rebase的目的是把本地未push的分叉提交历史整理成直线,使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。- 步骤: git rebase
有两种方法将一个分支的改动合并进另一个分支,一个就是前面所说的分支合并,另一个就是分支衍合,这两种方式有什么区别呢?
分支合并(merge)是将两个分支的改动合并到一起,并生成一个新的提交,提交历史是按时间排序的,即我们实际提交的顺序,通过git log --graph或一些图形化工具,可能很明显地看到分支的合并历史,如果分支比较多就很混乱,而且如果以功能点新建分支,等功能点完成后合回主线,由于merge后提交是按提交时间排序的,提交历史就比较乱,各个功能点的提交混杂在一起。
而分支衍合(rebase)是找到两个分支的共同祖先提交,将要被rebase进来的分支的提交依次在要被rebase到的分支上重演一遍,即回到两个分支的共同祖先,将branch(假如叫experiment)的每次提交的差异保存到临时文件里,然后切换到要衍合入的分支(假如是master),依次应用补丁文件。experiment上有几次提交,在master就生成几次新的提交,而且是连在一起的,这样合进主线后每个功能点的提交就都在一起,而且提交历史是线性的
git rebase -i HEAD ~ 2,HEAD~2表示倒数第三个提交,这条命令要指定要重排的最旧的提交的父提交。
- cherry-pick
当与别人和作开发时,会向别人贡献代码或者接收别人贡献的代码,有时候可能不想完全Merge别人贡献的代码,只想要其中的某一个提交,这时就可以使用cherry-pick了。就一个命令
git cherry-pick <commit-id>
一般通过在gerrit中粘贴相应的命令,直接在提交的目录下执行这个命令
五.标签管理
tag是一个容易让人记住的有意义的名字,与某个commit绑定在一起
- 切换到需要打标签的分支是,执行git tag <tagname>
- 查看所有的标签,git tag
- 历史提交打标签,git tag <tagname> commit_id
- 查看标签信息,git show <tagname>
- 删除标签,git tag -d <tagname>
- 推送某个标签到远程,使用命令git push origin <tagname>
- 一次性推送全部尚未推送到远程的本地标签git push origin --tags
六.其它
-
忽略特殊文件
Git工作区的根目录下创建一个.gitignore文件,把要忽略的文件名填进去,Git就会自动忽略这些文件。忽略文件的是:- 忽略操作系统自动生成的文件,如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,如.class .o文件;
- 忽略私有的带有敏感信息的配置文件
忽略的规则是:
- 所有以#开头的行会被忽略
- 可以使用glob模式匹配
- 匹配模式后跟反斜杠(/)表示要忽略的是目录
- 如果不要忽略某模式的文件在模式前加"!"
Git跟踪限制
目前的版本控制系统只能跟踪文本文件如网页、txt、程序源代码等,视频图片等二进制文件,Git无法知道具体改动细节
参考:
Git手册
https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/00137402760310626208b4f695940a49e5348b689d095fc000
http://www.runoob.com/git/git-remote-repo.html