git入门教程
一:在Windows上安装Git
在git官网下载安装程序,(网速慢的同学请移步国内镜像),安装完成后,在开始菜单找到git Bash 一个打开后类似于doc命令窗的东东。
安装完成后需要设置你的用户名和邮箱,因为你要自报家门告诉Git你是谁。所以我们先来学习自报家门的两条命令,用来设置你的用户名和邮箱
(1)设置你的用户名和邮箱
a) git config --global user.name "Your Name" 设置用户名
b) git config --global user.email "email@example.com
" 设置邮箱
注意git config 命令的--global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
(2)创建版本库
版本库就是仓库,repository,可以理解为一个目录,里面的文件和文件夹统一的被git管理,每个文件的删除、修改、添加、都能被Git追踪到,以便任何时候都可以追踪历史,甚至在将来的某一时间点来“还原”;
创建版本库也很简单首先你要创建一个空文件夹,然后通过 git init 命令把这个目录变成git可以管理的目录
a) mkdir D:/repository/teacher
pwd
/d/repository
pwd 用于显示当前目录
b) $ git init 把当前目录作为版本库
Initialized empty Git repository in D:/repository/teacher/.git/
瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现当前目录下多了一个.git
的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
如果你没有看到.git
目录,那是因为这个目录默认是隐藏的,用ls -ah
命令就可以看见。
版本库创建好之后,我们需要把文件或者说是我们的项目添加到版本库中,让Git来管理,一定要放到Git的版本库文件夹下或它的子目录下,我这里是 /teacher。放在其他地方就算Git在厉害也找不到。
和把大象放到冰箱需要3步相比,把一个文件放到Git版本库中只需要两步:一: 使用 git add 命令告诉 git 把文件添加到仓库中,二:使用 git commit 命令告诉 Git 把文件提交到版本库中
c) $ git add work 添加文件
我这里添加的是一个文件夹,你也可以add 多次不同的文件,最后用 git commit命令统一提交。
d)$ git commit -m "这是一段提交的说明" 提交添加的文件
git commit命令,-m 后面输入的是本次提交的说明,将来可以从历史记录中方便的找到改动记录。
嫌麻烦不想输入-m "xxx"
行不行?确实有办法可以这么干,但是强烈不建议你这么干,因为输入说明对自己对别人阅读都很重要。实在不想输入说明的童鞋请自行Google,我不告诉你这个参数。
git commit
命令执行成功后会告诉你, 1 file changed
:1个文件被改动(我们新添加的readme.txt文件);2 insertions
:插入了两行内容(readme.txt有两行内容)。
二:时光穿梭机
(1)查看版本库状态及查看修改了哪个地方
在上面我添加了一个work 文件夹 ,现在我把文件夹里的文件删除一个 PNG图片
使用 git status命令查看结果 如下:
$ git status On branch master Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) deleted: "work/\344\272\272\344\272\213\347\256\241\347\220\206ER\345 \233\276.png" Untracked files: (use "git add <file>..." to include in what will be committed)
我们可以看到输出告诉我们,文件夹被修改了,但是还没有准备提交,还输出的被删除的文件名字。
使用 git status 命令能知道文件哪里被修改,但是并不能知道修改了哪些内容。这是需要使用 git diff 命令来查看
$ git diff diff --git a/work/Git.txt b/work/Git.txt index 0e5f860..0063d81 100644 --- a/work/Git.txt +++ b/work/Git.txt @@ -1 +1 @@ -Git Is Very Good +Git Is Very Good!!!
上面的代码是我在一个Git.txt 文件里面,我们可以看到原文件内容是 Git Is Very Good ,修改后加了!!!
知道文件做了什么修改后,我们就可以放心的提交了,提交修改和提交新文件步骤是一样的,第一步是 git add ‘ 文件或目录名 ’,第二部是 git commit -m "修改了XXX" 进行提交
a) $ git status (查看当前仓库状态,是否被提交或哪些东西被修改)
b) $ git diff (查看仓库中具体修改了那些内容)
小结:
用 git status 可以随时掌握工作区的状态。
如果 git status 告诉了你有文件被修改,使用 git diff 命令可以查看具体修改了哪些内容
(2)版本回退
上面我们说了修改文件,查看版本库状态和查看修改了那些内容。像这样我们不断地修改文件,如果不小心乱删或改崩了项目,想回到修改之前怎么办呢?Git显然也考虑到了这一情况,他给我们提供了 commit ,如果我们感觉文件修改的差不多了,我们可以
保存一个快照,对就相当于游戏的存档。这样我们在游戏失败的时候,还可以选择从最近的一个存档继续进行。而不是从第一关重新开始。
这是我们想想,版本库下的文件被修改了几次,好吧我也想不起来了,我们需要版本控制工具为我们提供查看修改历史记录的功能,事实上Git也是这么做的。在Git 中我们使用 git log 命令查看历史更改记录
我们看到 git log 命令显示出从最近到最远的修改记录,包括修改时间,修改人信息,修改时间和修改信息
如果嫌输出信息太多,看的眼花缭乱,可以试试加上 --pretty=oneline
每提交一个新版本,实际上Git就会把它们自动串成一条时间线。如果使用可视化工具查看Git历史,就可以更清楚地看到提交历史的时间线:
需要友情提示的是,你看到的一大串类似2c6d9e907333...
的是commit id
(版本号),和SVN不一样,Git的commit id
不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,而且你看到的commit id
和我的肯定不一样,以你自己的为准。为什么commit id
需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD
表示当前版本,也就是最新的提交2c6d9e...
,上一个版本用 HEAD^
,上上一个版本就是 HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
所以如果我们想回到上一个版本,使用命令 git reset
$ git reset --hard head^
HEAD is now at 07bcfff 测试
这是我们用 git log 看一下提交记录,果然已经回到过去的一个版本了。这是我们用git log 查看是已经看不到最新的那个提交记录了!!如果这时候如果我想回去怎么办,就好比从21世纪回到了大秦帝国,来容易,回去肿么办?
其实办法还是有滴,只要命令行没有关,一直往上找,找到最新那条提交记录的 commit id,是2c6d9e9...于是就可以回到指定ID的未来版本。
1 $ git reset --hard 2c6d 2 HEAD is now at 2c6d9e9 添加Git文件内容,删除表格
再看一下版本,哈哈果然我胡汉三又回来了……
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD
指针,当你回退版本的时候,Git仅仅是把HEAD指向相应的版本commit id;
可是如果命令行窗口关了肿么办。。。也不用捉急,git为我们提供了 git reflog 命令,用来记录你的每一次命令操作。然后就可以看到commit id 了
在使用 git reset --hard commitId 就可以回到任意的版本了。
a)HEAD^ 表示上一版本 ,HEAD^^ 表示上上一版本 ,如果修改太多,比如有100个版本,我们可以用HEAD~100 来表示。 HEAD commitid 表示HEAD指向指定ID的版本
b)git reset --hard HEAD^ 回到上一版本
git reset --hard 2c6d9e9 回到指定ID的版本
c)git log --pretty=oneline 查看提交历史,以便确定要回退到哪个版本、
d)git reflog 查看命令历史,以便确定要回到未来的哪个版本。
(3)工作区和暂存区
工作区就是仓库里面你自己创建的目录,比如我电脑上的 teacher 和 growing都属于工作区
打开一个工作区目录,在工作区里面有个 .git 的隐藏文件夹。这个不算工作区而是 Git 的版本库。
版本库里面有许多东西,最重要的就是成为stage或(index)的暂存区。
还有 Git 自动为我们创建的第一个分支 master ,一级master的一个指针 HEAD
实际上在我们修改文件、添加文件时 执行的 git add 实际上就是把文件放到暂存区中。git commit 后才把文件提交到分支中。
也就是说我们修改的文件,可以通过 git add 统统先放入到 暂存区,然后由 git commit 一次性添加到分支中。
一旦提交后,我们有没有对工作区进行修改,这是暂存区就是空的咯。
暂存区是Git非常重要的概念,弄明白了暂存区,就弄明白了Git的很多操作到底干了什么。
没弄明白暂存区是怎么回事的童鞋,请移驾到 这里,细细品味 。
还有一种说法是 git add 操作已经添加到分支中,git commit 是给当前版本添加指针,具体就见仁见智吧,等下我在继续研究一下。
(4)撤销修改
a)$ git checkout -- 文件名或文件夹名 丢弃工作区内的修改
b)$ git reset HEAD <file> 撤销已经添加到暂存区的修改,然后再用上一命令,丢弃工作区内的修改,git checkout -- <filename>
三:远程仓库
(1)获得Git远程仓库
Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。事实上,我们不需要多
台电脑才能部署我们的Git ,因为世界上有个叫GtiHub的神奇网站。我们只需注册一个GitHub账号,就可以免费获得Git远程仓库。
在继续阅读后续内容前,请自行注册GitHub账号。由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:
第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:
$ ssh-keygen -t rsa -C "youremail@example.com"
你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。
如果一切顺利的话,可以在用户主目录里找到.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件,这两个就是SSH Key的秘钥对,id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。
第2步:登陆GitHub,打开“Settings”,“SSH and GPG keys”页面:
然后,点“New SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub
文件的内容:
(2)添加远程库
现在我们在本地有了仓库,想在GitHub上创建一个仓库,并且让这两个仓库进行远程同步,这样远程仓库既可以用来备份,还可以让其他人通过该仓库来进行协作。
首先登陆GitHub,点击右上角加号,点击New Repository 按钮,创建一个新仓库;
Repository name 填写的是我本地仓库的名字,其他的不用改动;
目前,在GitHub上的这个learngit
仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。
我们根据GitHub的提示在本地仓库下执行命令,和远程仓库关联。
$ git remote add origin https://github.com/hailisen/test.git
hailisen是我的GitHub用户名这里要换成你自己的。这里我们的远程库库已经创建完毕,且已经和本地的版本库进行了关联,然后就需要把本地库的所有内容推送到远程库上。
$ git push origin master 表示把当前 master 分支 推送到 origin 的主机上
如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。
$ git push origin
如果当前分支只有一个追踪分支,name主机名也可以省略 : $git push
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。之后只要本地库做了修改
就可以通过 git push origin master 命令来把本地master 分支的修改推送到GitHub上面。
a) $ git remote add origin https://github.com.hailisen/test.git 本地库关联远程库
b) $ git push origin master 把关联远程库后把本地库内容推送到远程库
c) $ git push --set-upstream origin dev 本地dev分支推到远程的dev分支上,如果远程不存在该分支会自动创建
详情请看这里 : 本地分支跟踪关联远程分支
(3)拉取和克隆
上面说了远程库关联本地库,和本地库想远程库推送修改后的信息。我们知道一般git是多人协作的项目,你新入职一家公司且是新电脑,需要从同事的GitHub上面下载项目代码。这是就需要使用 克隆命令了。
点击一个需要克隆的版本库或项目然后如下:
点击这里复制远程库的URL,复制后 , gitbash 中进入想要复制到的目录下,输入命令 git clone https://github.com/hailisen/teacher.git 这时再看本地文件夹,已经存在克隆的项目了。
好了,你已经成功的从公司GitHub上面下载了项目,你熟悉了几天后自己在项目中改了一些代码,同事又上传了几个新的功能,你需要把远程库的项目和本地的项目合并,这时可以使用命令,git pull 从远程库拉取项目并和本地的合并。
$ git pull origin next:master 表示:取回远程 origin 主机的next 分支与本地master分支合并
总结:
a) git clone 远程gitHub项目的URL 克隆远程项目到本地文件夹下
b) git pull [远程主机名] [远程分支]:[本地分支] 拉取远程origin
主机的next
分支,与本地的master
分支合并,如果当前分支与远程分支存在追踪关系,git pull
就可以省略远程分支名
四:分支管理
(1)创建于合并分支
在版本回退里,每次提交Git都会把它们串成一条时间线,这条时间线就是一个分支,之前我们都是在Git默认创建的分支 master 上工作,master 就是主分支,HEAD
严格来说不是指向提交,而是指向master
,master
才是指向提交的,所以,HEAD
指向的就是当前分 支。一开始的时候 master 就是一条线 Git 用master 指向最新的提交,HEAD指向 master 分支的当前节点。每次提交,master
分支都会向前移动一步,这样,随着你不断提交,master
分支的线也越来越长:
当我们创建新的分支,例如dev时,Git 新建了一个一个指针叫dev ,,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
$ git branch dev --创建dev分支
git checkout
命令加上-b
参数表示创建并切换,相当于以下两条命令:
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
你看,Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
使用 git branch 命令查看当前分支:
$ git branch
* dev
master
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
git checkout master --让head指向master主分支
git merge dev --合并dev分支
所以Git合并分支也很快!就改改指针,工作区内容也不变!如果你看dev分支不顺眼,也可以干掉它。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
git branch -d dev --删除dev分支
a) 查看分支: git branch
b) 创建分支: git branch <分支名>
c) 切换分支: git checkout <分支名>
d) 创建+切换分支: git checkout -b <分支名>
e) 合并某分支到当前分支: git merge <分支名>
f ) 删除分支: git branch -d <分支名>