git基础

创建版本库

  • -mkdir 名称:创建一个目录
  • -cd 名称 :进入名称对应文件夹
  • -pwd :查看当前目录结构
  • -git init:将目录变成管理仓库,细心的读者可以发现当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
  • -ls -ah :列表目录,可以查看影藏目录

把文件添加到版本库

  • -git add 文件名:告诉Git,把文件添加到仓库
    • 可以提交多个文件如: git add "xxx" "xxx"
  • -git commit:提交文件
    • -m:可以再后面续写本次提交的说明如-m "xxx"

时光穿梭机

查看修改的文件

  • -git status :查看结果,具体修改了什么文件

  • -git diff 文件名:查看文件名指定的文件做了哪些修改“详细”

    • $ git diff readme.txt 
      diff --git a/readme.txt b/readme.txt
      index 46d49bf..9247db6 100644
      --- a/readme.txt
      +++ b/readme.txt
      @@ -1,2 +1,2 @@
      -Git is a version control system.
      +Git is a distributed version control system.
       Git is free software.
          //可看出添加了distribute这个单词
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24

      #### 版本回退

      + -git log:查看日志历史记录了

      + ```java
      $ git log
      commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
      Author: Michael Liao <askxuefeng@gmail.com>
      Date: Fri May 18 21:06:15 2018 +0800

      append GPL

      commit e475afc93c209a690c39c13a46716e8fa000c366
      Author: Michael Liao <askxuefeng@gmail.com>
      Date: Fri May 18 21:03:36 2018 +0800

      add distributed

      commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
      Author: Michael Liao <askxuefeng@gmail.com>
      Date: Fri May 18 20:59:18 2018 +0800

      wrote a readme file
    • –pretty=oneline

    • $ git log --pretty=oneline
      1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
      e475afc93c209a690c39c13a46716e8fa000c366 add distributed
      eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file
      //前半部分问版本号
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129

      + -git reset :版本回退

      + -- hard HEAD^:退回到上一个版本
      + -- hard HEAD^^:退回到上上一个版本
      + -- hard HEAD~100:退回到上100个版本
      + -- hard 1094a...:退回到指定id的版本,不用写权,git会自动匹配

      + -git reflog :用来记录每一次命令

      ##### 小结

      + Git的版本回退速度非常快,因为Git在内部有个指向当前版本的`HEAD`指针,当你回退版本的时候,Git仅仅是把HEAD从指向`append GPL改为指向`add distributed`然后顺便把工作区的文件更新了。所以你让`HEAD`指向哪个版本号,你就把当前版本定位在哪

      ![d](http://ypimage.oss-cn-shenzhen.aliyuncs.com/upload_image/git/zero.jpg)



      + `HEAD`指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令`git reset --hard commit_id`
      + 穿梭前,用`git log`可以查看提交历史,以便确定要回退到哪个版本。
      + 要重返未来,用`git reflog`查看命令历史,以便确定要回到未来的哪个版本。

      #### 工作区和暂存区

      ##### 工作区

      + 就是你在电脑里能看到的目录,比如我的`learngit`文件夹就是一个工作区:

      ![s](http://ypimage.oss-cn-shenzhen.aliyuncs.com/upload_image/git/zero.png)

      ##### 版本库(Repository)

      + 工作区有一个隐藏目录`.git`,这个不算工作区,而是Git的版本库。
      + Git的版本库里存了很多东西,其中最重要的就是称为**stage**(或者叫index)的暂存区,还有Git为我们自动创建的第一个**分支**`master`,以及指向`master`的一个**指针**`HEAD`

      ![a](http://ypimage.oss-cn-shenzhen.aliyuncs.com/upload_image/git/first.jpg)



      + 分支和`HEAD`的概念我们以后再讲。

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

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

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

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

      你可以简单理解为,**需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改**

      #### 管理修改

      + git管理的只是修改,而不是文件
      + 你又理解了Git是如何跟踪修改的,每次修改,如果不用`git add`到暂存区,那就不会加入到`commit`中。

      #### 撤销修改

      + -git checkout -- test.txt :把test.txt`文件在工作区的修改全部撤销,这里有两种情况:
      + 自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态
      + 已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态
      + **注意**`git checkout -- file`命令中的`--`很重要,没有`--`,就变成了“切换到另一个分支”的命令
      + -git reset HEAD test.txt:可以把暂存区的修改撤销掉(unstage),重新放回工作区
      + 可以再接git checkout -- test.txt将工作区的文件改回原先的状态
      + 小结:
      + 当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令`git checkout -- file`
      + 当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令`git reset HEAD <file>`,就回到了场景1,第二步按场景1操作。
      + 已经提交了不合适的修改到版本库时,想要撤销本次提交,参考[版本回退](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/0013744142037508cf42e51debf49668810645e02887691000)一节,不过前提是没有推送到远程库

      #### 删除文件

      + -rm test.txt:将工作区中指定的文件删除
      + -git rm test.txt:git会检测到之前删除的文件,这个命令会从版本库删除文件名指定文件
      + 如果删错了
      + -git checkout -- test.txt:其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

      ### 远程仓库

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

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

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

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

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

      ![s](http://ypimage.oss-cn-shenzhen.aliyuncs.com/upload_image/git/first.png)

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

      ![s](http://ypimage.oss-cn-shenzhen.aliyuncs.com/upload_image/git/secend.png)

      + 为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。

      ### 从远程库克隆

      + $ git clone https://github.com/646190632/Jiao:克隆远程仓库的项目到本地,git支持多种协议

      ### 分支管理

      #### 时间线

      + 每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即`master`分支。
      + `HEAD`严格来说不是指向提交,而是指向`master``master`才是指向提交的,所以,`HEAD`指向的就是当前分支。

      ![d](http://ypimage.oss-cn-shenzhen.aliyuncs.com/upload_image/git/third.png)

      #### 创建分支

      + 当我们创建新的分支,例如`dev`时,Git新建了一个指针叫`dev`,指向`master`相同的提交,再把`HEAD`指向`dev`,就表示当前分支在`dev`
      + Git创建一个分支很快,因为除了增加一个`dev`指针,改改`HEAD`的指向,工作区的文件都没有任何变化

      ![s](http://ypimage.oss-cn-shenzhen.aliyuncs.com/upload_image/git/fourth.png)

      + 对工作区的修改和提交就是针对`dev`分支了,比如新提交一次后,`dev`指针往前移动一步,而`master`指针不变

      ![a](http://ypimage.oss-cn-shenzhen.aliyuncs.com/upload_image/git/fifth.png)

      + 代码实现

      ```java
      $ git checkout -b dev
      Switched to a new branch 'dev'
      //git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
      $ git branch dev
      $ git checkout dev
      Switched to branch 'dev'

查看当前分支

  • 代码实现
  • git branch命令查看当前分支:
1
2
3
4
$ git branch
* dev
master
//git branch命令会列出所有分支,当前分支前面会标一个*号。

合并分支

  • 假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

  • 代码实现
1
2
3
4
5
6
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
//git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。
  • 注意:上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
    • 当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。

分支的删除

  • 可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

  • 代码实现
1
2
$ git branch -d dev
Deleted branch dev (was b17d20e).
  • 因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

解决冲突

  • git冲突提示不仅会在命令行中,还在文件中会显示
1
2
3
4
5
6
7
8
9
10
11
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

//Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存:
  • 如果出现冲突,根据实际情况修改原有冲突文件,两个分支分再次别提交到仓库,再次合并

  • $ git log --graph --pretty=oneline --abbrev-commit:可以通过日志查看解决冲突进程

分支管理策略

  • Fast forward模式:这种模式下,删除分支后,会丢掉分支信息。 因为fast forward合并就看不出来曾经做过合并。
    • --no-ff 参数用于禁用该模式
  • 如果禁用Fast forward模式的话,**Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息 **
  • 在实际开发中,我们应该按照几个基本原则进行分支管理:
    • 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
    • 那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
    • 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
    • 所以,团队合作的分支看起来就像这样:

Bug分支

  • 软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
  • $ git stash功能 :可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作
  • 关于恢复储藏起来的工作
    • git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
    • git stash pop,恢复的同时把stash内容也删了:
  • 注意:
    • 可以多次使用stash功能
    • $ git stash apply stash@{0}可以选择取出指定的stash内容

Feature分支

多人协作

  • 当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin
  • $ git remote:查看远程库的信息
    • 参数-v查看详细信息
推送分支
  • $ git push 远程库 要推送的分支:把该分支上的所有本地提交推送到远程库 。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:
  • 关于哪些分支需要推送
    • master分支是主分支,因此要时刻与远程同步;
    • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
    • bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
    • feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发
抓取分支
  • $ git branch --set-upstream-to=origin/dev dev:首先需要将本地的分支和远程的分支链接,此处设置devorigin/dev的链接
  • $ git pull:用git pull把最新的提交从origin/dev抓下来
小结
  • 多人协作的工作模式通常是这样:
    • 首先,可以试图用git push origin <branch-name>推送自己的修改;
    • 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
    • 如果合并有冲突,则解决冲突,并在本地提交;
    • 没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!

rebase

1
2
3
4
5
6
$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master) add author
* 3611cfe add comment
* f005ed4 (origin/master) set exit=1
* d1be385 init hello
...
  • $ git rebase:Git把我们本地的提交“挪动”了位置,放到了f005ed4 (origin/master) set exit=1之后,这样,整个提交历史就成了一条直线
    • rebase操作可以把本地未push的分叉提交历史整理成直线;
    • rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比

标签管理

  • 标签都只存储在本地,不会自动推送到远程

创建标签

  • $ git tag 版本:打一个新标签
    • 可用git tag查看所有标签:
  • $ git tag 版本号 提交的id:给固定的某次提交或修改添加版本号
    • -a:标签名
    • -m:指定说明文字
  • $ git show 版本号:显示这个版本号对应提交的详细信息
  • 默认标签是打在最新提交的commit上的。
  • 如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?
    • 方法是找到历史提交的commit id,然后打上就可以了

操作标签

  • 删除本地打错的标签
    • $ git tag -d v0.1
  • 标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
    • $ git tag -d v0.9
    • $ git push origin :refs/tags/v0.9:注意格式
  • 推送某个标签名称对应的版本到远程
    • git push origin <tagname>
  • 一次性推送全部尚未推送到远程的本地标签
    • $ git push origin --tags

git基础
https://andrewjiao.github.io/2018/12/20/git操作/
作者
Andrew_Jiao
发布于
2018年12月20日
许可协议