0、常用命令
git init # 初始化仓库
git add <file> # 将文件添加到仓库,反复多次使用
git commit # 将文件提交到仓库
git status # 查看当前仓库的状态
git diff # 查看具体修改内容(对比)
git log # 查看提交日志(用于回滚)
git reset # 回滚版本
git reflog # 查看历史命令
git checkout # 加上--表示让文件回到最近一次git commit或者git add的状态,如果没有--那么会切换到另一个分支。
git rm # 从版本库删除文件
git remote add origin git@server-name:path/repo-name.git # 关联远程仓库
git push -u origin master # 推送最新修改到远程仓库
git clone # 克隆远程仓库到本地
--分支技巧--
git branch # 查看分支
git branch <name> # 创建分支
git checkout <name> # 切换分支
git checkout -b <name> # 创建+切换分支
git merge <name> # 合并分支到当前分支
git branch -d <name> # 删除分支
git branch -D <name> # 强行删除
git log --graph # 查看分支合并图
git stash # 临时保存工作现场
-- 远程协作 --
git remote -v # 查看远程库信息
git push origin branch-name # 推送本地修改到远程, 如果冲突先用git pull抓取远程的新提交
git checkout -b branch-name orgin/branch-name # 在本地创建和远程分支对应的分支
git branch --set-upstream branch-name orgin/branch-name # 监理本地分支和远程分支的关联
git pull # 从远程抓取分支
git tag # 查看标签
git tag v1.0 # 创建标签v1.0
git tag -a <tagname> -m "blablabla..." #可以指定标签信息;
git tag -s <tagname> -m "blablabla..." #可以用PGP签名标签;
git tag # 可以查看所有标签。
git push origin <tagname> # 可以推送一个本地标签;
git push origin --tags # 可以推送全部未推送过的本地标签;
git tag -d <tagname> # 可以删除一个本地标签;
git push origin :refs/tags/<tagname> #可以删除一个远程标签。
1、概述
Git是一款免费、开源的以及目前最先进的分布式版本控制系统,用于敏捷高效地处理任何或大或小的项目,它是由Torvalds开始着手开发的,是为了辅助管理Linux内核代码而出现的。
2、Git的安装配置
在各个平台上安装Git后需要进行如下配置:
$git config --global user.name "Your.Name"
$git config --global user.email "email@example.com"
需要注意的是在git config命令中使用了**--global**这个参数的话,表示你这台机器上所有的Git仓库都会使用这个配置,担任也可以对某个仓库指定不同的用户名和Email地址。
3、创建版本库
版本库又名仓库(Repository),可以简单理解成一个目录,这个目录中所有的文件都可以被Git管理起来,每个文件的修改、删除Git都能跟踪到,以便在任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
创建仓库的步骤如下:
首先在某个地方创建一个空的目录:
$mkdir learngit
$cd lenrngit
$pwd
/xxx/xxx*/learngit
第二步通过git init命令吧该目录编程Git可以管理的仓库:
$git init
Initialized empty Git repository in /xxx/xxx*/learngit/.git/
这样我们就把仓库创建好了,而且会告诉你是一个空的仓库(empty Git repository),在目录下会多出一个.git目录,这个目录是Git来跟踪管理版本库的,不能乱修改,否则Git仓库就会被破坏了。
然后我们就可以把文件添加到仓库了:
首先我们在repository目录下添加一个文件为readme.txt文件,然后通过git add命令将文件添加到仓库:
git add readme.txt
没有提示说明添加成功。
第二步,我们使用git commit命令将文件提交到仓库:
$git commit -m "wrote a readme file"
。。。
关于git commit命令,-m后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的内容,这样你就可以方便的找到你修改的记录。然后git commit命令执行以后会提示。
而git add命令和git commit命令不同的地方就是在于git add命令可以多次add不同的文件,而git commit命令可以一次性提交多个文件。
所以我们初始化一个仓库可以使用git init命令,而将文件添加到Git仓库 需要分两步:
- 第一步 使用命令git add <file> 注意,可以反复多次使用,添加多个文件;
- 第二步 使用git commit命令,完成提交。
4、查看仓库文件的动态
在项目下使用git status命令可以查看仓库当前的状态。
首先我们先修改readme.txt文件为:
Git is a distributed version control system.
Git is a free software.
然后与性能**git statu状态可以看到
$git status
#On branch master
#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
#
no changes added to commit (use "git add" and/or "git commit -a")
有信息可知当前的readme.txt被修改过了,但是还没准备被提交。如果需要知道文件被修改了哪些内容,我们可以使用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.
可知我们在文件中的第一行添加distributed,然后我们可以将修改后的文件提交到仓库:
第一步:使用git add命令
git add readmit.txt
然后我们使用git status命令在查看仓库当前的状态:
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: readme.txt
#
从信息中可知,被修改的文件readme.txt将要被提交,所以我们就可以放心的提交了。
第二步,我们使用git commit命令提交:
$git commit -m "add distributed"
[master ea34578] add distributed
1 file changed, 1 insertion(+), 1 deletion(-)
提交后我们再查看仓库的状态:
$ git status
# On branch master
nothing to commit (working directory clean)
可知,文件提交完毕,已经没有需要提交的修噶,而且工作目录是干净的。
所以我们知道可以使用git status名查看仓库当前的状态,如果发现有文件被修改后,可以使用git diff命令查看那些地方被修改。
5、版本回退
在Git中当文件修改到一定程度时就可以保留一个快照,而commit就是这个快照,当我们把文件改乱了,后者误删了文件,还可以从最近的一个commit恢复,然后继续工作。我们可以通过git log命令查看历史记录:
$ git log
commit ed1f749af9ebef82c3fd2100a20eefcb1e31517a
Author: Quison <mguichun@foxmail.com>
Date: Thu Oct 22 15:33:02 2015 +0800
append GPL
commit d4eb2a653e049efec205d4b1773872da6d7c0af7
Author: Quison <mguichun@foxmail.com>
Date: Thu Oct 22 12:16:57 2015 +0800
add distributed
commit 18ee88caf5d2089d20175c6eb084f4acb543adde
Author: Quison <mguichun@foxmail.com>
Date: Thu Oct 22 10:54:00 2015 +0800
wrote a readme file
可知我们修改了三次文件,如果嫌信息太繁复的话我们可以使用**--pretty=online**参数查看:
$ git log --pretty=oneline
ed1f749af9ebef82c3fd2100a20eefcb1e31517a append GPL
d4eb2a653e049efec205d4b1773872da6d7c0af7 add distributed
18ee88caf5d2089d20175c6eb084f4acb543adde wrote a readme file
可知前面的一大串字母数字是commit id(版本号),它和SVN不一样,Git的commit id不是1、2、3...递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,这样做是因为在以后的多人在同一个爸爸库里工作,如果都使用1、2、3...作为版本号,那么容易冲突。并且每天提交一个新版本,实际上Git就好吧他们自己自动串成一条时间线。
那么回退到以前的版本该如何做呢?首先Git必须知道当前是哪个版本,在Git中,HEAD表示当前版本,也就是最新提交的版本,而上一个版本就是HEAD^,上上个版本就是HEAD^^,当然在往前的话有数不过来的,所以写成HEAD~100,表示前100个版本。
如果我们要回退到上一个版本(add distributed),就可以使用get reset命令:
$git reset --hard HEAD^
HEAD is now at ea34578 add distributed
然后我们在使用git log命令查看历史记录,发现记录如下:
$ git log
commit ea34578d5496d7dd233c827ed32a8cd576c5ee85
Author: Michael Liao <askxuefeng@gmail.com>
Date: Tue Aug 20 14:53:12 2013 +0800
add distributed
commit cb926e7ea50ad11b8f9e909c05226233bf755030
Author: Michael Liao <askxuefeng@gmail.com>
Date: Mon Aug 19 17:51:55 2013 +0800
wrote a readme file
我们发现append GPL那个版本已经看不到了,如果想退换append GPL版本的话需要git reset commit id:
$git reset --hard ed1f749
HEAD is now at ed1f749 append GPL
commit id没必要写全,然后查看一下commit的内容发现内容已经变回以前的append GPL版本了。
当然也有一种情况,回退之后关闭bash窗口,那么不知道commit的id,如何回到append GPL版本呢?我们可以使用git reflog来记录每一次的命令:
$ git reflog
ed1f749 HEAD@{0}: reset: moving to ed1f749
d4eb2a6 HEAD@{1}: reset: moving to HEAD^
ed1f749 HEAD@{2}: commit: append GPL
d4eb2a6 HEAD@{3}: commit: add distributed
18ee88c HEAD@{4}: commit (initial): wrote a readme file
然后我们找到想要回退的commit的id就可以回退过去了。所以我们知道
- HEAD 指向的版本就是当前版本,一次,GIt允许我们在版本的历史之间穿梭,使用命令 git reset --hard commit_id;
- 穿梭钱,使用git log可以查看提交历史,以便确定要回退到哪个版本;
- 如果要重返未来,那么使用git reflogg查看命令历史,以便要确定回到哪个版本。
6、工作区和暂存区
Git和其他版本控制系统如SVN的不同之处就是有一个暂存区的概念。
工作区就是我们电脑中能看到的目录,比如我们的learngit文件夹。
而在工作区中存在一个隐藏的目录**.git这个不算工作区,而是Git的版本库(Repository)。在版本库中存在了很多东西,其中最重要的就是称为stage(或者index)的暂存区**,以及为我们自动穿件的第一个分支master以及执行master的一个指针叫HEAD。
对于把文件王Git版本库中添加的时候是分两步执行的:
- 第一步 是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
- 第二步 是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
可以简单的理解为需要提交的文件修改一起放到暂存区,然后一次性提交暂存区的所有修改。
7、管理修改
要指定Git管理的是修改,而不是文件,所有它比其他的版本控制系统优秀得多。
要注意的是git commit命令是负责把暂存区的修改提交,如果多次修改后没有add到暂存区的修改时不会被版本库记录的。
8、撤销修改
在错误的情况下,可以通过git checkout --readme.txt将readme.txt文件在工作区的修改全部撤销,这里有两种情况:
- 一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就可以回到版本库一模一样的状态;
- 一种是readme.txt已经添加到暂存区后,有作了修改,现在撤销修改就回到添加到暂存区后的状态,总之,就是让这个文件回到最近一次的git commit 或 git add时的状态。
可知有以下三种情况:
- 当改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,使用命令git checkout -- file;
- 当你不但改乱了工作区某个文件的内容,还添加到暂存区是,想丢弃修改的话,分两步进行,第一步使用命令git reset HEAD file,回到第一种情况,然后按照第一种情况操作。
- 如果已经提交了不合适的修改到版本库是,逍遥撤销本次提交,可以参照版本回退,前提是没有推送到远程库。
9、删除文件
如果一个文件已经添加到了仓库,那么我们在文件管理器下直接删除文件的话,再使用git status命令的话Git会提示我们哪些文件被删除了,如果真的想在版本库中删除该文件需要使用git rm命令删除,还需要使用git commit命令提交。
还有另外一种情况就是,错删了文件,但是版本库中还有,那么可以使用**git checkout命令吧误删的文件恢复到最新版本,git checkout其实是使用版本库中的版本替换工作区的版本。
10、远程仓库
GitHub是一个专门提供Git仓库托管的服务网站,只需注册一个GitHub账号就可以免费获得Git远程仓库。本地的Git仓库和GtiHub仓库直接的传输是通过SSH加密的,所以需要设置连接:
第一步 创建SSH Key,创建之后再用户的主目录下会有.ssh目录,并且目录下会有id_rsa和id_rsa.pup,创建的的命令如下:
$ssh-keygen -t rsa -C "youremail@example.com"
创建成功后会有id_rsa私钥,id_rsa.pub是公钥。
第二步 登录GitHub,打开“Account setting”,“SSH Keys”页面,然后点击“Add SSH Key”,填上任意的title,在Key文本框里粘贴id_rsa.pub文件的内容。添加了SSH Key后GitHub就可以识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
11、添加远程库
对于远程库的添加,我们登录GitHub账户后,新建一个repository,然后对于这个空的仓库我们可以从这个仓库克隆出新的仓库,也可以把已有的本地仓库与之关联,然后把本地仓库的内容推送到GitHub仓库。现在根据我们关联本地已有的仓库learngit:
$git remote add origin git@github.com:username/learngit.git
以上添加后,远程库的名字就是origin,这个是Git默认的交付,也可以改成别的,但是origin这个名字一目了然,就知道是远程库。
下一步我们可以将本地的所有内容推送到远程库上:
$ git push -u origin master
The authenticity of host 'github.com (192.30.252.131)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? y
Please type 'yes' or 'no': yes
Warning: Permanently added 'github.com,192.30.252.131' (RSA) to the list of known hosts.
Counting objects: 17, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (12/12), done.
Writing objects: 100% (17/17), 1.37 KiB | 0 bytes/s, done.
Total 17 (delta 4), reused 0 (delta 0)
To git@github.com:Quision/learngit.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
将本地仓库的内容推送到远程仓库使用的命令是git push命令,实际上时吧当前分支master推送到远程,由于远程库是空的,所有我们第一次推送master分支是要加上-u参数,Git不但会把本地的master分支内容推送到远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时可以简化命令。推送后我们可以看到GitHub页面上的远程库内容和本地的一模一样。从现在开始我们就可以使用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推送最新的修改;
12、从远程库克隆
上节中是现有本地库,后有远程库,如何关联两者,现在我们先创建远程库,然后从远程库克隆到本地。首先我们新建一个仓库名为gitskills。
在远程库创建好之后,我们使用git clone克隆一个本地库:
$git clone git@github.com:username/gitskills.git
Cloning into 'gitskills'...
Warning: Permanently added the RSA host key for IP address '192.30.252.130' to the list of known hosts.
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
Checking connectivity... done.
所以我们知道要克隆一个仓库就必须先知道仓库的地址,然后使用git clone命令克隆,Git支持多种协议,包括https 但是通过SSH支持原生的git协议速度最快。
13、分支管理
在版本回退中,我们知道,每次提交Git都把它们串成一条世界线,这条时间线就是一个分支截止到目前,只有一条世界线,在Git中,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交,所以,HEAD指向的就是当前分支,一开始的时候master分支是一条线,Git用master指向最新的提交,然后在用HEAD指向master,就能确定当前分支以及当前份指导的提交点。
当我们新建一个分支是,如dev,的那个Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上。
对于分支的合并,就是直接把一个分支提交到另一个分支,如吧master分支指向提交dev的当前提交。它就是改动指针的指向,工作区的内容不变。
对于分支的删除如果把dev分支删除后,我们就只剩下master分支了。
对于分支的新建如下:
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命令查看当前分支,然后git会列出所有分支,并且在当前分支前面标记一个星号。对于分支的创建、合并和删除非常快,所以在工作时我们合并后再删掉分支,这和直接在master分支上工作的效果是一样的,而且过程也更安全。
总之,分支的操作命令如下:
- 查看分支 git branch;
- 创建分支 git branch <name>;
- 切换分支 git checkout <name>;
- 创建+切换分支 git checkout - <name>
- 合并某分支到当前分支 git merge <name>;
- 删除分支 git branch -d <name>;
14、解决冲突
在分支工作的工程中,会出现冲突的情况,也就是两个分支提交的修改不一致,导致不能快速合并,然后必须手动解决重读,再提交,完成合并。
然后可以使用git log --graph命令可以查看分支合并图。
15、分支管理策略
通常在分支合并是,Git会使用Fast forward(快进)模式,但这种模式下,删除分支后,会丢失分支in系,如果要强制禁用Fast forward模式,Git就会merge时生成一个新的commit,这样,从分支历史上就可以看出分支的信息。
在实际开发中,我们应该按照几个基本原则进行分支管理,首先master分支应是非常稳定的,也就是仅仅是用来发布新版本,平时不能在上面干活,然后干活都在dev分支上,也就是说dev分支不稳定的,到某个时候比如发布1.0版本时,再把dev分支合并到master上,在master分支上发布正式版本。团队中的每个人都在dev分支上干活,每个人都有自己的分支,是不是的网dev分支上合并就可以了。
所以在团队开发中,合并分支的时候加上**--no-f**参数就可以用普通模式合并,这样合并后的历史有分支,也能看出曾经做个的合并,而Fast forward合并是看不出来的。
16、Bug分支
在软件的开发中,bug的出现是很平常的事,当出现bug的时候需要你在短时间内修复,而你当前的工作又没办法在短时间内完成,那么可以使用stash功能把当前的工作现场存储起来,然后等到回复现场好继续工作。
当完成修复工作后,我们使用git stash list命令查看工作现场存储,然后使用git stash apply恢复现场,然后再使用git stash drop来删除。当然也可以使用git stash pop命令在恢复的同时把stash内容也删除了。
17、Feature分支
在软件开发中,总是有新的功能不断的添加进来,而在添加一个新的功能时又不希望一些实验性的代码把主分支搞乱,所以在新添加一个功能是最后能新建一个feature分支,在上面开发,然后完成后合并,最后删除该feature分支。当要丢失一个没有被合并过的分支需要使用**git branch -D < name>**命令强行删除。
18、多人协作
当从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来,并且,远程仓库的默认名称是origin。可以通过git remote命令查看,可以添加-v参数查看详细的信息,如果没有权限推送就看不到地址。
推送分支的话就是把该分支的所有本地提交推送到远程库,推送时要指定本地分支,这样Git就会吧该分支推送到远程库对应的远程分支上:
$git push origin master
#如果要推送其他分支就把名字改成要推送的分支的名字
$git push origin dev
但是并不是一定要把本地分支往远程推送,可以选择的推送一些分支:
- master 分支是主分支,因此需要时刻与远程同步;
- dev 分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
- bug分支只用于在本地修复bug;
- feature分支是否推送到远程,取决于你和你的队员是否在上面开发。
对于抓取分支,是使用git pull命令进行操作,因此多人协作工作一般是这样的:
- 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 branch-name origin/branch-name进行关联。
19、标签管理
在发布一个版本时,我们通常在版本库中打一个标签,这样,就唯一确定了打标签时刻的版本,在将来我们可以通过标签把对应版本提取出来,所以标签y 而是版本库的一个快照,Git的标签库虽然是版本库的快照,但其实他就是指向某个commit的指针(和分支很像,但是分支可以移动,标签不能移动)所以创建和删除标签都是快速的完成的。
对应Git中标签的创建非常简单,首先切换到需要打标签的分支上,然后使用git tag< name>命令打标签:
$git branch
*dev
master
$git checkout master
Switched to branch 'master'
#打标签
$git tag v1.0
#查看标签
$git tag
v1.0
对于想打在历史上的标签,可以通过git log命令查找到对应的commit id然后打上就可以了,创建标签时可以使用-a知道那个标签名,-m指定说明文字,然后使用git show < tagname>就可以看到说明文字了,还可以通过-s参数用私钥签名一个标签。
如果标签打错了可以使用git tag -d v0.1命令对标签删除,因为创建的标签都只存储在本地,不会自动推送到远程库,所以打错的标签可以在本地安全的删除。如果要推送某个标签到远程,使用命令git push origin < tagname>来推送,或者将所有未推送的标签一起推送到远程,使用git push origin --tags。如果要删除远程的标签就需要先删除本地的标签,然后再删除远程的标签git push origin:refs/tags/v0.xx。就可以了
20、使用GitHub
对应git工作目录中我们可以创建一个特殊的.gitignore文件,然后把忽略的文件名填进去,git就会自动忽略这些文件。忽略文件的原则是:
- 1.忽略操作系统自动生成的文件,比如缩略图等;
- 2.忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就有必要放进版本库,比如Java编译产生的.class文件;
- 3.忽略自己带有敏感信息的配置文件,比如存放口令的配置文件。
提交.gitignore文件就好了。
对应常用的一些命令可以配置别名git config --global alias aliasname name。