git入门(廖雪峰老师)

 根据廖雪峰老师的git教程进行学习总结;

1、之前上班用的都是svn进行管理,那么svn和git有什么区别呢?

  svn是集中式的版本控制系统,而git是分布式版本控制系统,那么集中式和分布式版本控制系统有什么区别呢?

  集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。(集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,宽带够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟)

  分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作得时候,就不需要联网了,因为版本库就在你自己的电脑上,既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比如说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩只需把自己的修改推送个对方,就可以互相看到对方的修改了。(和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了)

2、在Windows上安装Git

  在Windows上使用Git,可以从Git官网直接下载:https://git-scm.com/downloads,然后按默认选项安装即可。

  安装完成后,在开始菜单里找到“Git”=》“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!

       

  安装完成后,还需要最后一步设置,在命令行输入:

$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

  注意git config命令的--global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。

3、创建版本库

  首先,选择一个合适的地方,创建一个空目录:

$ mkdir git
$ cd git
$ pwd
/e/git

  pwd命令用于显示当前目录。我在e盘,仓库位于/e/git

  注意:windows系统中,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文

  第二步,通过git init命令把这个目录变成Git可以管理的仓库

$ git init
Initialized empty Git repository in E:/git/.git/

  这样Git仓库就创建好了,而且告诉你是一个空的(empty Git repository),当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。

  如果你没有看到.git目录,那是因为这个目录默认是隐藏的,用ls -ah命令就可以看见

  

4、把文件添加到版本库

  首先,在目录下编写一个readme.txt文件,然后用命令git add告诉Git,把文件添加到仓库

$ git add readme.txt

  然后,用命令git commit告诉Git,把文件提交到仓库:

$ git commit -m "wrote a readme file"

  显示如下:

  

  简单解释一下git commit命令,-m后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便的找到改动记录。git commit命令行执行成功后会告诉你,1 file changed:一个文件被改动;2 insertions:插入了两行内容(为什么Git添加文件需要add,commit一共两步呢?因为commit可以一次提交很多文件,所以你可以多次add不同的文件)

5、查看仓库当前状态和查看修改内容

  git status命令可以让我们时刻掌握仓库当前的状态;

  如果不记得修改内容,可以使用git diff这个命令查看;

  提交修改和提交新文件一样分两步,第一步是git add;第二步是git commit -m "explain";

  

  要随时掌握工作区的状态,使用git status命令;

  如果git status告诉你有文件被修改过,用git diff可以查看修改内容

6、版本回退

  可以使用git log命令查看历史记录,如果嫌输出的信息太多,可以加上--pretty=oneline

  

  前面一大串十六进制表示的是commit id(版本号)。

  在git中,用HEAD表示当前版本,也就是最新的提交,上一个版本就是HEAD^,上上一个版本就是HEAD^^,一次类推100个,可以简写成HEAD~100,如果只是回退到上一个版本,可以使用git reset命令:

$ git reset --hard HEAD^

  

  查看了一下内容,果然被回退了,那么,再想来到刚刚提交最新的那个版本怎么办?

  可以找到那个版本的commit id,就可以指定回到未来的某个版本

$ git reset --hard 39263

  版本号没必要写全,前几位就可以了,git会自动去找,

  

  又回到刚刚最新版本了。

  Git中,提供了一个命令git reflog用来记录你的每一次命令:

  

  小结:HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。

       穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本(git log --pretty=oneline)

       要重返未来,用git reflog 查看命令历史,以便确定要回到哪个版本

7、工作区和暂存区

  Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

  

  前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:

  第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

  第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

  因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

  修改readme.txt文件,添加LICENSE文件,没有git add,使用git status查看:

  

  使用git add把两个都添加后:

  

  现在,暂存区的状态就变成这样了:

  

  git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支

  

  

8、管理修改

  当你修改文件之后,如果git add进去了,可是没有git commit,然后又修改了这个文件,然后再git commit进去,那么其实提交的内容是第一次修改的内容,因为第二次修改没有git add进去,也就是还停留在工作区,所以你git commit之后再git status还是会提示你有一个文件没有add,可以使用git diff HEAD -- readme.txt命令查看工作区和版本库里面最新版本的区别。

  

  小结:每次修改,如果不用git add到暂存区,就不会加入到commit中。

9、撤销修改

  当你修改了内容还没有git add的时候,你可以发现git会告诉你,git checkout -- file可以丢弃工作区的修改:

  

$ git checkout -- readme.txt

  命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:

  一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

  一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

  总之,就是让这个文件回到最近一次git commitgit add时的状态。

    

  注意:git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout命令

  如果你进行了修改,并且git add进去了暂存区:

  

  Git同样告诉我们,用命令git reset HEAD <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区:

  

  然后再丢弃工作区的修改:

  

  小结:

    场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file

    场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。

    场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

10、删除文件

  一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm命令删了:

$ rm test.txt

  这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了:

  

  现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit

  

  现在,文件就从版本库中被删除了。(提示:先手动删除文件,然后使用git rm <file>和git add <file>效果是一样的)

  另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

$ git checkout -- test.txt

  git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

  小结:命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容

11、远程仓库

  使用GitHub来做Git仓库托管服务。

  第一步:需要创建一个GitHub账号,由于本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以需要一点设置:

    ①:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsaid_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:

$ssh-keygen -t rsa -C "youremail@163.com"

  你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。

  如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsaid_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

    ②:登陆GitHub,打开“Account settings”,“SSH Keys”页面:

    然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:

    点“Add Key”,你就应该看到已经添加的Key:

  

  小结:可以把自己用到的每台电脑的key都添加到GitHub上,就可以在每台电脑上往GitHub推送了。GitHub上免费托管的Git仓库一般是任何人都可以看到的。(除非交钱,把仓库改为私有的)

12、添加远程库

  现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。

  首先,登陆GitHub,然后,在右上角找到“new repository”按钮,创建一个新的仓库:

  在Repository name填入learngit,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库:

  目前,在GitHub上的这个learngit仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。

  现在,我们根据GitHub的提示,在本地的learngit仓库下运行命令:

$ git remote add origina git@github.com:lsc202426/test.git
$ git push -u origina master

  或者

$ git remote add origin https://github.com/lsc202426/learngit.git
$ git push -u origin master

  SSH警告:当你第一次使用Git的clone或者push命令连接GitHub时,会得到一个警告:这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。

  Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:warning

  由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

  添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。

  看人家评论里面是这样解决的,后面再尝试一下。

  从现在起,只要本地作了提交,就可以通过命令:

$ git push origin master

   小结:要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git;

        关联后,使用命令git push -u origin master第一次推送master分支的所有内容;

        此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改

13、从远程库克隆

  首先,登陆GitHub,创建一个新的仓库,名字叫gitskills

  我们勾选Initialize this repository with a README,这样GitHub会自动为我们创建一个README.md文件。创建完毕后,可以看到README.md文件:

  现在,远程库已经准备好了,下一步是用命令git clone克隆一个本地库:

  

  注意把Git库的地址换成你自己的,然后进入gitskills目录看看,已经有README.md文件了:

  

  如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https

  小结:要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。

14、创建与合并分支

  首先,我们创建dev分支,然后切换到dev分支:

  

  git checkout命令加上-b参数表示创建并切换,相当于一下两条命令:

$ git branch dev
$git checkout dev
Switched to branch 'dev'

  然后,用git branch命令查看当前分支:

  

  git branch命令会列出所有分支,当前分支前面会标一个*号。

  然后,我们就可以在dev分支上正常提交,比如对readme.txt做个修改,加上一行,然后提交:

  

  现在,dev分支的工作完成,我们就可以切换回master分支:

  

  切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了,因为那个提交是在dev分支上,而master分支此刻的提交点并没有变。

  现在,我们把dev分支的工作成果合并到master分支上:

  

  git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。

  注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

  当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。

  合并完成后,就可以放心地删除dev分支了:

  

  删除后,查看branch,就只剩下master分支了:

  

  因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

  小结:Git鼓励大量使用分支:

     查看分支:git branch

     创建分支:git branch <name>

     切换分支:git chcekout <name>

     创建+切换分支:git checkout -b <name>

     合并某分支到当前分支:git merge <name>

     删除分支:git branch -d <name>

15、解决冲突

  准备新的feature1分支,继续我们的新分支开发:

  

  修改readme.txt最后一行,改为:Creating a new branch is quick AND simple.

  在featurel分支上提交:

  

  切换到master分支:

  

  Git还会自动提示我们当前master分支比远程的master分支要超前一个提交

  在master分支上把readme.txt文件的最后一行改为:Creating a new branch is quick & simple.

   

  现在,master分支和featurel分支个字都分别由新的提交变成了这样:

  

  这种情况下,Git无法执行“快速合并”,只能视图把个字的修改合并起来,但这种合并就可能会有冲突,我们试试看:

  

  果然冲突了!Git告诉我们,readme.txt文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件:

  

  我们可以直接查看readme.txt的内容:

  

  Git用<<<<<<<=======>>>>>>>标记出不同分支的内容,我们修改如下后保存:Creating a new branch is quick and simple.

  

  用带参数的git log也可以看到分支的合并情况:

  

  最后,删除feature1分支:

   

  小结:当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。

        解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。

        用git log --graph命令可以看到分支合并图。

 16、分支管理策略

  通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。

  如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

  下面我们实战一下--no-ff方式的git merge

  首先,创建并切换dev分支;修改readme.txt文件并提交一个新的commit;然后切换回master;准备合并dev分支,请注意--no-ff参数,表示禁用Fast forward

$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

  因为本次合并要创建一个新的commit,所以加上-m 参数,把commit描述写进去合并后,我们用git log看看分支历史:

$ git log --graph --pretty=oneline --abbrev-commit
*   e1e9c68 (HEAD -> master) merge with no-ff
|  
| * f52c633 (dev) add merge
|/  
*   cf810e4 conflict fixed
...

  

  在实际开发中,我们应该按照几个基本原则进行分支管理:

  首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

  那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

  你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

  所以,团队合作的分支看起来就像这样:

  小结:合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。

17、Bug分支

  软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。

  当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交:

$ git status
On branch dev
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   hello.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   readme.txt

  并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?

  幸好,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge

  现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。

  首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git checkout -b issue-101
Switched to a new branch 'issue-101'

  现在修复bug,需要把“Git is free software ...”改为“Git is a free software ...”,然后提交:

$ git add readme.txt 
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

  修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

  太棒了,原计划两个小时的bug修复只花了5分钟!现在,是时候接着回到dev分支干活了!

$ git checkout dev
Switched to branch 'dev'

$ git status
On branch dev
nothing to commit, working tree clean

  工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看:

$ git stash list
stash@{0}: WIP on dev: f52c633 add merge

  工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:

  一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;

  另一种方式是用git stash pop,恢复的同时把stash内容也删了:

$ git stash pop
On branch dev
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   hello.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   readme.txt

Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a)

  再用git stash list查看,就看不到任何stash内容了:

$ git stash list

  你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:

$ git stash apply stash@{0}

  小结:修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;

        当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。

 18、Feature分支

  添加一个新功能时,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。

  如果该feature分支已经add并且commit;但是还没有合并,如果想要删除:

$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.

  销毁失败。Git友情提醒,feature-vulcan分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D参数。。

现在我们强行删除:

$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 287773e).

  小结:开发一个新feature,最好新建一个分支;

        如果要丢弃一个没有被合并过的分支,可以通过git branch -D <name>强行删除。

 19、多人协作

  多人协作的工作模式通常是这样:

  1. 首先,可以试图用git push origin <branch-name>推送自己的修改;

  2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

  3. 如果合并有冲突,则解决冲突,并在本地提交;

  4. 没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!

  如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>

  这就是多人协作的工作模式。

  小结:

  • 查看远程库信息,使用git remote -v

  • 本地新建的分支如果不推送到远程,对其他人就是不可见的;

  • 从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;

  • 在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;

  • 建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name

  • 从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。

 20、rebase(未完待续!)

  

原文地址:https://www.cnblogs.com/chao202426/p/10766016.html