git详解

来源:互联网 发布:前景检测算法 matlab 编辑:程序博客网 时间:2024/06/08 08:29

git详解

本文为“廖雪峰git教程”的学习心得

1.安装与设置git

  • 首先安装git,在ubuntu中使用apt-get install git 即可。
  • 安装完成后,还需要最后一步设置,在命令行输入:
    git config --global user.name "xxxx"
    git config --global user.email "xxx@xxxx.com"
  • 因为git是分布式的,所以,每个机器都必须自报家门:你的名字和Email地址。注意git config命令的–global参数,用了这个参数,表示此机器上所有的git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址
  • 配置公司的服务器地址,这样git才能通过ssh等方式定位到具体的ip
sudo vi /etc/hosts在文件尾加入公司服务器地址:1xx.xx.xx.xx git.xxx.org1xx.xx.xx.xx Yocto.xxx.org

2.初始化一个空的版本库

  • 创建版本库,所谓的版本库就是在这个目录下所有的文件都将被git管理,每个文件的修改、删除,Git都能跟踪。比如我们进入一个目录/xxxx,在控制台中输入
    git init
    随后控制台显示
    Initialized empty Git repository in /xxxx/.git/
    它告诉我们创建了一个空的仓库.git,我们可以用ls -a 查看/xxxx目录,发现确实多出来了一个.git目录,以后里面都会存放和此版本库有关的一些文件
  • 于是乎,/xxxx就变成了版本库,里面的文件改动就可以被git管理了。有一点是要注意的,git只能管理文本文件比如txt、c文件等,像图片、可执行程序等二进制文件则无法被管理

3.add(stash)和commit的区别

  • 首先对于简单应用,可以简单粗暴的认为,add 和 commit操作是绑在一起用的,要把文件提交到版本库,只需先add 再 commit即可
    这里写图片描述
  • add的功能是将文件添加入“暂存区域”,比如我们添加一个test.c
    git add test.c
  • commit的功能是把“暂存区域”内的所有文件提交到本地版本库中,比如输入:
    git commit -m "wrote a test.c file"
    其中的-m参数后面可以指定
    随后控制台显示
    [master (root-commit) 6c614cb] wrote a test.c file
    1 file changed, 34 insertions(+)
    create mode 100755 test.c
    表示commit成功
  • 为什么提交要分为add和commit两步呢?主要是为了实现分批提交,降低commit的颗粒度,比如我们修改了 a.c, b.c, c.c, d.c,其中 a.c 和 c.c 是一个功能相关修改,b.c,d.c属于另外一个功能相关修改。那么就可以采用:
  • 很多时候,我们会修改一些代码或添加一些新文件,这些改动并不是针对项目的,仅仅是为了在本地方便自己开发。我们不想提交这些改动应该怎么做?这里不能使用gitignore来忽略这些文件,因为涉及到改动gitignore,这会污染远程仓库的gitignore。完全新添加的文件属于”Untracked files”,而已有文件中修改过的代码则属于”tracked files”。对于”Untracked files”,放在在工作区没有关系,我们可以不用管,而”tracked files”改过了,就不能随意放在工作区了,必须要放到暂存区。这里使用git stash来将它们放入暂存区,这条命令与git add最大的区别是,stash之后commit便会忽略暂存区中对应的文件,这相当于一种忽略机制
git add a.c c.cgit commit -m "function 1"git add b.c d.cgit commit -m "function 2"
  • 这样,我们就能实现先改2个文件,再改2个文件的效果(其实我们是4个文件同时改了)。由此一来,版本之间区分就更加细致

4.查看状态

  • 当我们在版本库内工作时,可以随时输入git status来查看距离上一次add、commit以来,哪些文件被改动过了
  • 我们还可以输入git diff xxx 来查看某个文件中具体被改动的内容,被改动的地方都会被打印出来
  • 当需要查看提交日志(log)时,使用git log 命令即可,我们就可以得知每一次commit的历史记录,这个操作一般用来确定要回退到过去的某个版本
  • 我们还可以通过git reflog来查看每一次git操作,以及它们的“简略编号”commit_id,这个操作一般用来查看id以回到任意某个版本
  • 我们可以输入git branch 来查看一共有几条分支,以及他们的信息,当前分支前面会标一个*号
  • git log --graph --pretty=oneline --abbrev-commit命令可以看到极为直观的分支图,使用git log --graph则会看到详细但不直观的分支图

5.版本回退

所谓的版本回退,是将版本库、工作区的状态全部回到从前的某个状态。我们还是以test.c为例,使用git log查看当前commit的历史记录,可知当前版本是add printf的那一次commit

commit 774f8be8f6e711816ab5f84c5bba822cc9226d20Author: taurenking <xxx.com>Date:   Sat Sep 24 21:08:06 2016 +0800    add printfcommit 6fc8d726ad6f4f58ad4654d126bf905427b53b17Author: taurenking <xxx.com>Date:   Sat Sep 24 20:58:28 2016 +0800    add entercommit 6c614cb5a30dcbfd60c078d5871fd8c9a8d651e3Author: taurenking <xxx.com>Date:   Sat Sep 24 07:13:12 2016 +0800    wrote a test.c file
  • 在git中,用HEAD表示当前版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^,往前的100个版本就是HEAD~100
  • 版本回退可以使用git reset --hard commit_id(推荐)或者git reset --hard HEAD^(不推荐) 此类命令,我们输入git reset --hard HEAD^回到上一个版本,再git log查看commit历史,发现最新的add printf那一次commit记录消失了!说明我们成功回到了add enter的那一次状态
commit 6fc8d726ad6f4f58ad4654d126bf905427b53b17Author: taurenking <xxx.com>Date:   Sat Sep 24 20:58:28 2016 +0800    add entercommit 6c614cb5a30dcbfd60c078d5871fd8c9a8d651e3Author: taurenking <xxx.com>Date:   Sat Sep 24 07:13:12 2016 +0800    wrote a test.c file

这里写图片描述

  • 那我们如果反悔了怎么办?想回到add printf的那一次commit,可是git log中的那一次记录已经消失了,这时我们可以输入git reflog来查看每一次git操作,以及它们的“简略编号”commit_id,此操作一般用来查看id以回到“未来”的某个版本,打印信息如下:
    6fc8d72 HEAD@{0}: reset: moving to HEAD^
    774f8be HEAD@{1}: commit: add printf
    6fc8d72 HEAD@{2}: commit: add enter
    6c614cb HEAD@{3}: commit (initial): wrote a test.c file
    我们输入 git reset --hard 774f8be,就回到了add printf的那一次commit状态了
  • 总结:git reset --hard commit_id或者git reset --hard HEAD^ 都可以回退版本,第一个更值得推荐,因为可以回到任意的某个状态,极为灵活

6.重置工作区与删除文件

  • 当我们对工作区的文件进行了误操作时,我们可以将文件重置为当前版本库中的状态,使用git reset --hard 即可。注意,这和版本回退是两个概念,这里只是重置工作区到最近一次的commit状态罢了,版本库并未发生任何改动
  • 当我们要删除一个文件该怎么做呢?首先肯定是先把工作区内的实际文件删掉,直接rm -rf xxx 即可;然后输入git rm xxx从版本库中删除该文件,最后git commit
  • 如果我们的文件删错了并且还未add、commit,那么直接重置工作区即可找回。但是如果删除后已经add、commit了,那就只能版本回退了

7.远程仓库

  • GitHub提供很有名的服务,但是很多公司也自己搭建服务器来实现远程仓库。一般远程仓库使用ssh或http等协议。比如GitHub,如果上面有一个远程版本库(也可以理解为项目),那么网页上会提供一个ssh或http的地址来对应该版本库,通过这个地址就能访问该版本库
    这里写图片描述
  • 使用http的优点是比较简单,缺点是速度比较慢,并且每次推送都必须输入口令
  • 如果要使用ssh,必须先要拥有SSH Key,用户目录下(root用户就是/root)输入ssh-keygen -t rsa -C "xxxx@xxx.com",它会询问是否在当前目录创建,直接按回车确认,然后它会让我们设置一个密码
  • 这样一来用户目录下就有了一个.ssh目录,里面有两个文件id_rsa和id_rsa.pub,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人,一般使用ssh来访问GitHub这种远程仓库时,就需要事先把自己id_rsa.pub公钥登在网上登记一下
  • 远程仓库已经存在,而我们本地没有任何东西,需要把远程仓库原模原样的复制一份到本地时,就可以使用clone命令,首先进入一个空目录,clone之前目录无须事先进行init!!!!输入git clone xxxxxxxxxxx(远程仓库的ssh或http地址),即可
  • 远程仓库不存在,而我们想把本地已经存在的版本库传上去时:
    • 第一步,需要现在GitHub或其他服务器创建一个空的远程版本库(也可以理解为项目)
    • 第二步,登记ssh公钥(可选),然后获得远程仓库的ssh或http地址
    • 第三部,在本地版本库的目录内,输入 git remote add origin xxxxxxxxxxx(远程仓库的ssh或http地址) 令本地版本库和远程版本库关联起来。命令中的origin是git对远程版本库的默认命名,也可以改成别的(一般别改。。)
    • 第四步,本地库的内容推送到远程,输入git push -u origin master,实际上是把当前分支master推送到远程。由于远程库是空的,我们第一次推送master分支时,加上了-u参数,git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
  • 从现在起,只要本地作了add、commit,就可以通过命令: git push origin master来推送到远程了
  • 注意第一次用ssh连接远程仓库时可能会警告:
    The authenticity of host ‘github.com (xx.xx.xx.xx)’ can’t be established.
    RSA key fingerprint is xx.xx.xx.xx.xx.
    Are you sure you want to continue connecting (yes/no)?
    这是正常的,按照它说的输入yes即可

8.利用git来生成patch

有时,我们会需要对项目做补丁,利用git可以生成patch

  • 一种老旧的方法是使用diff实现,首先在工作区做修改,修改完成并测试可靠后,使用 git diff>xxx.patch,将工作区状态和上一次commit之间的区别做成patch
  • 一种推荐的方法是使用format来实现,先把做的改动add、commit一下,然后git format-patch HEAD^即可将当前的commit和再上一次commit之间的区别做成patch

9.添加代码比较工具

虽然git diff可以方便的比较工作区和commit之间的差异,但是却不能编辑

  • 我们可以将vim中自带的vimdiff添加到git的difftool中,这样今后就能用git difftool来比较和编辑代码了
git config --global diff.tool vimdiffgit config --global difftool.prompt falsegit config --global alias.d difftool

9.最基本的分支管理

这里阐述的是最简单的分支管理,不涉及多人合作、以及代码更新,实际工程中的分支管理详见下一章

  • 比如我们一开始有一条主线master,如下图,用HEAD指向的节点表示当前版本
    这里写图片描述
  • 输入git checkout -b dev创建了一个分支dev,git checkout命令加上-b参数表示创建并切换到该分支,相当于以下两条命令:git branch devgit checkout dev
    这里写图片描述
  • 然后,我们就可以在dev分支上正常工作了,add、commit后:
    这里写图片描述
  • 我们想把分支dev合并进主线master,而git merge xxx命令用于合并指定分支xxx当前分支,所以首先要git checkout master回到主干,再git merge dev 合并支线到主干
    这里写图片描述
  • 那么如果多个人同时维护主线master呢?比如我创建了一条分支feature1,工作完后git checkout master回到主干,再git merge feature1 合并支线到主干,突然git告诉我test.c有冲突,原来master被另一个人更新过了,并且那个人写的test.c内容和我有冲突
    这里写图片描述
    这时,我们处于“解决冲突状态”,我们需要去把冲突的文件修改一下,git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改之。修改完后输入:
    git add test.c
    git commit -m "conflict fixed"
    自动退出“解决冲突状态”,完成合并,提交到了本地版本库
    这里写图片描述
  • 如何删除分支?git branch -d xxx 即可删除xxx分支

10.Pull Request工作流实例

  • 整个project涉及到3个仓库

    • public: 在Git服务器上,是项目的正式发布仓库,由项目管理员修改,用户只读
    • private: 在Git服务器上,是用户从public仓库fork(又称clone reponsitory)到自己名下的私有仓库,用户可以读写
    • local: 在用户本地机,是用户从private仓库clone下来的,用户可以直接修改

    开发逻辑:用户在自己的本地仓库做开发,开发完了上传到私有仓库,然后申请合并私有仓库的内容到公共仓库
    这里写图片描述

  • 下面以一个项目实例来阐述细节。假设我们的project是一个GUI程序,该程序将被用在三个产品上,分别是mini、normal、super,该程序的代码在三个产品上有细微的不同,那么本project就适合分出三条branch,名字分别就是mini、normal、super。后面的讲解以super这个产品的开发为例
    这里写图片描述

fork正式项目

  • 首先,在git服务器上,公司public仓库里静静的躺着三条branch:mini、normal、super(其中,默认的branch是normal)。我们若想开发产品super,就必须把fork一份public仓库,从而得到自己的private仓库。这一步通常是网页上登陆git服务器,浏览到public仓库,点fork(又称clone reponsitory)按钮。

clone私有仓库

  • fork完之后就得到了和公司public仓库完全相同的private仓库,里面同样有三条分支。然后我们就要创建一个本地仓库,建一个新的空目录,最好把名字起成project的名字,然后进去直接git clone privateURL localdir(privateURL可以是private仓库的ssh地址),这样local仓库就建成了,可以进行开发了

整顿branch

  • 利用git branch查看local仓库的branch,发现clone下来的只有一条默认的branch,即normal,其实super和mini只不过被隐藏起来了罢了,这是git clone的特性,即只显示默认的branch。我们使用git checkout super,再次git branch,发现我们成功切换到了super
  • 在开始改代码前,我们必须新建一条专为开发使用的branch。git checkout -b super_dev,一般用作开发的分支都以_dev结尾

开发local代码

  • 在super_dev分支上,我们可以进行多次小修改,并且多次add、commit
git add a.c c.cgit commit -m "function 1"
  • 很多时候,我们会修改一些代码或添加一些新文件,这些改动并不是针对项目的,仅仅是为了在本地方便自己开发。我们不想提交这些改动应该怎么做?这里不能使用gitignore来忽略这些文件,因为涉及到改动gitignore,这会污染远程仓库的gitignore。完全新添加的文件属于”Untracked files”,而已有文件中修改过的代码则属于”tracked files”。对于”Untracked files”,放在工作区没有关系,我们可以不用管,而”tracked files”改过了,就不能随意放在工作区了,必须要放到暂存区。这里使用git stash来将它们放入暂存区,这条命令与git add最大的区别是,stash之后commit便会忽略暂存区中对应的文件,这相当于一种忽略机制。但是当我们提交完后要让它们从暂存区中弹出来git stash pop

同步更新

  • 当我们完成工作了,想发布代码时,先要将public仓库当前状态同步到local。这一步是必须的,因为我们要保证发布的代码含有public仓库最新的状态
git checkout supergit pull publicURL supergit checkout super_devgit rebase super

首先切换到super,将public仓库中的super同步下来。然后切换回super_dev,用rebase将super的更新作用到当前分支super_dev

  • rebase时,若public仓库中的更新和我们super_dev中的改动有冲突,则需要我们解决,rebase产生冲突时,会自动创建并切换到一个临时branch,可以用git branch查看。当我们解决完冲突后,使用git rebase --continue返回

提交工作

  • 提交工作时,最好使用专用的merge_request分支,如果没有的话就创建一个。之所以要使用专用的merge_request分支,是因为开发(dev)可能和merge_request同时进行,并且代码可能不一样,要保证两者的独立性和纯洁性
git checkout super_devgit checkout -b super_merge_request
  • 如果已经存在super_merge_request分支,则将super_dev更新作用到super_merge_request即可
git checkout super_merge_requestgit rebase super_dev
  • 然后最重要的是精简commit,将杂乱无章的commit合并精简。假设我们曾有过三次commit,号码为111111、222222、333333,先要将其合并,则输入 git rebase -i 000000其中00000是服务器上最新的状态,即我们改动之前的状态。输入完会弹出窗口,里面#开头的都是注释,不用care
pick 33333 xxxxxxxpick 22222 xxxxxxxpick 11111 xxxxxxx

我们只需将222222、1111111前面的pick改成s,即可将这两个commit记录删掉(合并到33333),保存退出后又会弹出窗口,我们将非注释部分的那几行 commit message, 修改合并成一行新的 commit message,保存然后退出,再次输入git log查看 commit 历史信息,会发现这三个 commit 已经合并了
- 将super_merge_request推送到远端的私有仓库的super中

git push private_URL super_merge_request:super#或者下面这句也可以git push origin super_merge_request:super
  • 最后我们可以登录服务器查看private仓库里的提交,做最后的检查

merge request

  • 我们在private仓库里的提交边上可以找到merge request(有时也称之为Pull Request)按钮,发起一个merge request,弹出窗口将询问以指定代码审核者、源分支、目标仓库和目标分支。
  • 管理员若接受此merge request,则我们的代码改动将会被合并到public仓库的super分支,至此整个开发过程就完成了

回炉重造

  • 如果管理员认为还有问题,需要修改,那么不用关闭网页上的merge request,直接更新即可。
  • 先在本地super_merge_request上修改,然后commit,再按照前文所说的将commit合并精简。再将super_merge_request推送到远端的私有仓库的super中
git push private_URL super_merge_request:super#或者下面这句也可以git push origin super_merge_request:super
  • 由于之前的那次merge request已和private仓库中的super关联起来了,当我们改动了private仓库中的super后,merge request便会自动更新,相当智能
  • 当管理员认可了merge request,最后在本地进行
git checkout super_devgit rebase  super_merge_request

来将super_merge_request的特性合并入dev

  • 重返工作时别忘了用下面命令恢复在暂存区的改动(比如用来增加工作效率的脚本)
git stash listgit stash pop stash@{num}
1 0
原创粉丝点击