git的原理和使用|青训营笔记

105 阅读7分钟

这是我参与「第四届青训营 」笔记创作活动的第1天

一、创建仓库repository

仓库相当于一个目录,这个目录里的所有东西都可以被git管理起来,每个文件的修改、删除,git都能追踪,可以还原。

  • 创建一个空目录(相当于请求内存空间,但是还没有内容)
$ mkdir learngit  //创建一个名为learngit的空目录
$ cd learngit  //change direction 切换到learngit目录(仓库)
$ pwd  //显示当前目录
/Users/dengjieAir/learngit
  • 第二步,通过git init命令把这个目录变成Git可以管理的仓库
vi readme.txt

写入该指令后,将进入编辑器编辑readme.txt文件,需要按i键进入编辑状态,编辑文件内容

$ git add readme.txt

添加成功后,将不会有任何显示

  • 第三步,用命令 git commit 告诉git,把文件提交到仓库
$ git commit -m"wrote a readme file"

-m 后面输入的内容是本次提交的说明,可以输入任何内容,方便找到改动记录

{git commit}命令执行成功后会告诉你,{1 file changed}:一个文件被改动;{2 insertions}:插入了两行内容(readme.txt有两行内容)

二、对文件管理、修改

1、对文件修改和查看

修改readme.txt文件,改成如下内容:

Git is a distributed version control system.
Git is free software.

运行命令{ git status }查看当前目录的状态,通过输出结果可以看到,readme.txt 被修改过了。但是还没有准备提交的修改。 运行{ git diff }命令查看修改内容,输出结果可以看到,具体什么地方进行了修改了什么内容 当查看清楚后,对文件进行添加和提交

git commit -m"add distributed"

提交后,用{ git status }命令查看仓库内容,看到输出为(working tree clean)可知工作目录是干净的

小结
  • 要随时掌握工作区的状态,使用{ git status }命令
  • 如果{ git status } 告诉你文件有被修改,用{ git diff }可以查看修改内容

2、版本回退(恢复之前的文件)

对文件再进行一次修改,修改readme.txt文件如下:

Git is a distributed version control system.
Git is free software distributed under the GPL.

然后添加提交

$ git add readme.txt
$ git commit -m"append GPL"

查看以前提交的版本

使用{ git log }命令可以查看之前提交的三个版本,通过提交说明区别

最近的一次是{ append GPL},上一次是{ add distributed }, 最早的一次是{ wrote a readme file }

可以通过{ git log --pretty=oneline}来简化输出,将只会输出commit id(版本号) 和提交说明

回到以前的版本

{HEAD}表示当前版本,上一个版本是{HEAD^},上两个版本是{HEAD^^},上100个版本是{HEAD~100}

$ git reset --hard HEAD^
  • 回退之后用{ cat }命令查看readme.txt的内容是不是版本{ add distributed}
$ cat readme.txt
Git is a distributed version control system
Git is free software    
  • 使用{ git log }命令发现只有两个版本了,最新的版本不见了,可以通过reset命令恢复未来的某个版本,在上面找到{ append GPL }版本的版本号{ commit id }是{ 1094abc...},
$ git reset --hard 1094a   //写部分版本好就可以
HEAD is now at 82b0afe applend GPL
  • 在通过cat查看readme.txt 内容
$ git readme.txt
Git is distributed version control system.
Git is free software distributed under the GPL
  • 如果电脑关机,想找的已经回退的版本号,通过命令{ git reflog }用来记录我的每一次命令
$ git reflog

在输出中可以通过提交说明查找已经回退的版本号

小结
  • HEAD指向的版本是当前版本,因此Git允许我们在版本历史中穿梭,使用命令{ git reset --hard commit_id}

  • 穿梭前,用{ git log }可以查看提交历史,以便确定要回退到哪个版本

  • 要回去未来版本,用{ git reflog} 查看命令历史,以便确定要回到未来某个版本

3、工作区和暂存区

  • 工作区(Working Directory) 就是在电脑中所能看见的目录,比如learngit文件夹就是一个工作区
  • 版本库(Repository)
  • 工作区中有一个隐藏目录{ .git },这不算工作区,而是Git的版本库   在版本库中,最重要的东西就是称为stage(或者index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master 的一个名叫HEAD的指针

image.png

  • 文件添加提交过程
    • 第一步,用{ git add }把文件添加进去,实际上是将修改的文件添加到暂存区中
    • 第二步,用{ git commit }提交修改,实际上是将暂存区的所有内容提交到当前的分支 当我们创建Git版本库的时候,Git自动为我们创建了唯一一个master分支,所以{ git commit }就是向master分支上面提交修改

4、撤销修改

撤销工作区

命令{ git checkout -- readme.txt }可以撤销在工作区中的全部修改,这里有两种情况:

  • 一种是readme.txt自修改后还没有存放到暂存区,撤销后直接回到了和版本库一模一样的状态
  • 一种是readme.txt已经添加到暂存区中,又做了修改,只能撤销到暂存区后的状态

撤销暂存区

命令{ git reset HEAD }可以将暂存区的修改撤销掉(unstage),重新放回工作区

$ git reset HEAD readme.txt
Unstaged changed after reset:
M.   readme.txt

{ git reset }命令即可以回退版本,也可以将暂存区的修改回退到工作区,当用HEAD时,表示最新版本

使用 git status 查看一下,现在暂存区是干净的,工作区有修改

小结
  • 撤销工作区的内容,用命令 { git checkout -- file }
  • 想要撤销暂存区的内容,需要两步,先用命令{ git reset HEAD },回到了工作区,再用checkout撤销
  • 当想要撤销已经提交到版本库的内容时,git reset --hard HEAD^回到上一个版本

5、删除文件

用 git rm 命令删除文件

$git rm test.txt
$git commit -m"remove test.txt"

删错之后恢复

git checkout -- test.txt

Git checkout 实际使用 版本库里面的版本去替换工作区的版本,无论工作区是修改还是删除都可以一键还原

三、远程仓库

1、本地内容推送到远程库

  • 第一步,关联一个远程仓库
$ git remote add origin git@github.com:1171276417/learngit.git

其中origin为远程仓库名,learngit为我在github接收的仓库名

  • 第二步,将本地库的所有内容推送到远程库上
$ git push -u origin master   //远程库不为空则不需要-u参数

{ git push }命令会把本地的master分支和远程的master分支关联起来

  • 删除远程库
$ git remote rm <name>
  • 查看远程库
$ git remote -v

2、从远程库克隆到本地

  • 使用{ git clone }命令克隆
$ git clone git@github.com:1171276417/abc.git
  • 切换目录,查看文件
$ cd abc  //切换到abc目录
$ ls  //查看该目录的文件

四、分支管理

1、创建与合并分支

  • 每次提交是一个节点,git将它们串成一条时间线,这条时间线是一个分支。目前只有一条分支,叫做主分支,即master分支,HEAD是指向master,master指向最新的提交,所以HEAD指向目前分支

image.png 每一次提交,master多一个节点,master分支就会向前走一步,越来越长

  • 创建并切换一个新分支dev,Git新建了一个叫做dev的指针,新建分支时dev指向master指向的提交,即指向最新提交,再当时切换到了dev分支,所以HEAD指向当前分支dev

image.png

  • 在dev分支上面进行一场添加提交,dev分支上将生成一个新节点往前一步,但是master分支不动,HEAD仍然指向最新的提交,即dev当前指向的节点

image.png

  • 当dev分支的工作完成后,将master指向dev,即完成了分支的合并,master指向dev的最新提交,切换会master分支,HEAD也指向master

image.png

  • 当分支合并完成后,可以将dev指针删除,只剩下master一条分支

image.png

  • 命令
    • 第一步,创建dev分支,并且切换到dev分支
$ git branch -b dev   //-b参数表示创建并切换
//第一行包含两个指令,等价于下面两行
$ git branch dev  //创建一个名为dev的分支
$ git checkout dev   //切换到dev分支
-   然后可以查看当前分支,{ get banch }命令会列出所有分支,当前分支前面会标一个*号
$ git banch
*dev
master
  • 对文件进行修改,在分支上面正常提交
  • dev分支的工作完成后,切换回master主分支中
$ git checkout master
//等价于git switch -c dev
  • 用{ git merge }命令将指定分支合并到当前分支
$ git merge dev
  • 最后将dev分支删除
$ git branch -d dev
小结
  • 查看分支:git branch 
  • 创建分支:git branch <name>
  • 切换分支:git checkout <name> git switch <name>
  • 创建+切换分支: git checkout -b <name>或者 git switch -c <name>
  • 合并指定分支到当前分支:git merge <name>
  • 删除分支:git branch -d <name>

2、解决冲突

在分支上面对一个文件进行修改提交,切换到master分支上对同一个文件进行修改提交,如下

image.png

对这两个分支进行合并会造成冲突,git status可告诉我们冲突的文件

命令{ cat }可以查看文件的冲突具体内容,需要手动修改文件内容,再进行提交,合并完成

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

$ git log --graph --pretty=oneline --abbrev-commit

最后删除feature1分支 $ git branch -d feature1

3、分支管理策略

合并分支时,如果可能git会使用Fast forward模式,但是在这种模式下,删除分支后,将会丢掉部分分支信息。

通常会强制禁用Fast forward模式,git就会在merge时生成一个新的commit,在分支历史上也可以看出分支信息。(--no-ff方式的git merge

  • 首先创建并切换到分支,在分支上面修改提交文件
  • 然后切回master,准备合并dev分支
$ 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的描述

merge后如图

image.png

4、Bug分支

每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。

当你接到一个修复一个代号101的bug的任务时,你想创建一个分支issue-101来修复它,但是当前正在dev上进行的工作还没有提交,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作

$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge
  • 首先确定要在哪个分支上修复bug,就从哪个分支创建临时分支:
$ git checkout master
$ git checkout -b issue-101
  • 修复bug,将修复后的文件提交
$ git add readme.txt 
$ git commit -m "fix bug 101"
  • 修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:
$ git switch master
$ git merge --no-ff -m "merged bug fix 101" issue-101
  • 最后接着回到dev分支干活,并且恢复stash隐藏的内容

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

    • 一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除
    • 另一种方式是用git stash pop,恢复的同时把stash内容也删了
$ git switch dev
$ git status  //工作区为空,已经被隐藏了
$ git stash list  //查看隐藏区,内容在里面
$ git stash pop
$ git stash list  //再查看隐藏区内容为空

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

$ git stash apply stash@{0}
  • 在master分支上修复了bug后,dev分支是早期从master分支分出来的,这个bug在当前dev分支上也存在。同样的bug,要在dev上修复,我们只需要把4c805e2 fix bug 101这个提交所做的修改“复制”到dev分支

  • Git专门提供了一个cherry-pick命令,让我们能复制一个特定的提交到当前分支:

$ git branch
* dev
  master
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 1011 file changed, 1 insertion(+), 1 deletion(-)
小结
  • 修复bug,通过创建新的bug分支进行修复,然后合并,最后删除分支
  • 暂时不能提交目前内容时,将工作现场git stash隐藏,修复bug后,在git stash pop,回到工作现场
  • master分支上面修复bug时,想要合并到当前分支,可以用git cherry-pick <commit>命令,把bug提交的修改“复制”到当前分支

5、Feature分支

  • 开发新功能 每一个功能,最好新建一个feature分支,在上面开发完成后合并,最好删除该feature分支
  • 强行删除未合并分支 正常删除分支只能合并之后删除,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D参数
$ git branch -D feature
Deleted branch feature (was 287773e).

6、多人协作

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

$ git remote -v
origin  git@github.com:michaelliao/learngit.git (fetch)
origin  git@github.com:michaelliao/learngit.git (push
  • 推送分支 把指定分支上的所有本地提交推送到远程库
$ git push origin dev
  • 抓取通知 从远程库clone时,默认情况下只能看到本地的master分支

要在dev分支上开发,就必须创建远程origindev分支到本地创建:

$ git checkout -b dev origin/dev

以在dev上继续修改,然后,时不时地把dev分支push到远程

  • 结局冲突 别人已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送

提交有冲突,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送

$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> dev

git pull失败,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置devorigin/dev的链接:

$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

再pull:

$ git pull

git pull成功,但是合并有冲突,需要手动解决后,提交,再push:

工作模式

  • 1、首先用git push origin <branch-name>推送自己的修改
  • 2、如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并 如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
  • 3、如果合并有冲突,则解决冲突,并在本地提交
  • 4、没有冲突或者解决掉冲突后,再用git push 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,如果有冲突,要先处理冲突

7、Rebase

把分叉的提交历史“整理”成一条直线,看上去更直观 输入命令git rebase

五、标签管理

  • 目的 tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起,它是指向某个commit的指针

1、创建标签

  • 第一步,切换到需要打标签的分支上
$ git branch
* dev
  master
$ git checkout master
Switched to branch 'master'
  • 第二步,命令git tag <name>就可以打一个新标签
$ git tag v1.0

创建带有说明的标签,用-a指定标签名,-m指定说明文字:

$ git tag -a v0.1 -m "version 0.1 released" 1094adb

命令git tag查看所有标签:

$ git tag
v1.0
  • 对历史的某个提交打标签(默认对最新提交标签)需要找到历史提交的commit_id
$ git log --pretty=oneline --abbrev-commit   //查找历史提交id
$ git tag v0.9 f52c633     //对id为f52c633的提交打v0.9标签
  • 查看标签信息git show <tagname>
$ git show v0.9
小结
  • 命令git tag <tagname>用于新建一个标签,默认为HEAD,也可以指定一个commit id

  • 命令git tag -a <tagname> -m "blablabla..."可以指定标签信息

  • 命令git tag可以查看所有标签

2、操作标签

  • 推送标签到远程
    • 推送某个标签
$ git push origin v1.0
  • 一次性推送全部标签
$ git push origin --tags
  • 删除标签
    • 删除未推送到远程的标签
$ git tag -d v0.1
-   删除已经推送到远程的标签
    -   首先从本地删除
$ git tag -d v0.9
Deleted tag 'v0.9' (was f52c633)
  • 然后从远程删除,删除命令也是push
$ git push origin :refs/tags/v0.9
To github.com:michaelliao/learngit.git
 - [deleted]         v0.9