Git 基础
简介
网上有很多关于git的起源,简介,用途,这里就不多数了(或许是我不知道。。。)
安装
sudo apt-get install git-core
用户配置
git是分布式版本控制系统,所以每次提交时需要一个用户名,让人知道是谁提交的。
还需要提供一个邮箱,出了问题让别人能够联系到你。
git config --global user.name "zdran"
git config --global user.email "cm.zdran@foxmail.com"
需要注意的是,config –global 参数是指在本台计算机中的所有仓库都使用该配置当然你也可以给每个仓库都配置一个name和email
创建仓库
创建一个新的文件夹,虽然也可以用不空的文件,但是出了问题不要找我
路径不要出现空格,中文,特殊字符
mkdir learnGit
cd learnGit
git init
看到下面的显示说明你成功了
Initialized empty Git repository in /home/woody/Documents/learnGit/.git/
需要注意的是,成功后会多出一个.git的文件夹,在没有必须打开的情况下
强烈不建议打开这个文件夹
添加文件
友情提示:创建的文件尽量使用utf-8的编码,Windows下的同学尽量不要使用记事本编辑,推荐sublime
在当前文件夹下创建一个readme.txt文件,随便写点东西比如
hello git
然后保存
使用下面的命令可以查看当前仓库的状态
git status
显示结果:
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.txt
nothing added to commit but untracked files present (use "git add" to track)
意思就是说,你修改了一个文件,但是还没有提交。
使用下面的命令添加到仓库
git add readme.txt
注意这仅仅是添加到仓库了,告诉git,这个文件是需要版本控制的。但是还没有提交。
使用下面的命令提交到仓库
git commit -m "创建readme.txt文件"
-m 的参数是对这次提交的一次说明
显示结果:
[master (root-commit) 3eb5f77] 创建readme.txt文件
file changed, 1 insertion(+)
create mode 100644 readme.txt
注意:虽然-m参数是可选的额,但是强烈建议你添加上,因为默认的配置是不添加-m参数是没有办法提交的
时光倒流(版本回退)
我想大部分同学使用git的最初的动力就是因为这个功能吧。
现在我们对readme文件进行修改,再随便添加些东西,然后readme内容就变成下面这样
hello git
hello git 2.0
然后再次add commit。
现在我们的仓库里有两个版本了,我们可以用下面的命令查看git的提交历史
git log
显示结果
commit b5934a2d9f80d257ca50b9b80a4b31d5bb9fe03c
Author: = <woody@gmail.com>
Date: Sun Jan 22 16:27:59 2017 +0800
第二次提交
commit 3eb5f7721cb86c617faf0d2ad13a5cee9d1a3764
Author: = <woody@gmail.com>
Date: Sun Jan 22 16:16:10 2017 +0800
创建readme.txt文件
注意:commit字段是自动生成的,所以你的结果一定与我的不一样,以你的为准
好了,下面下面我们将readme.txt回退到最开始创建的状态。git使用HEAD表示当前的版本,就是最新的版本。
上一个版本是HEAD^,再上一个版本是HEAD^^,上一百个版本是HEAD~100(当然你也可以打100个^)。
使用下面的命令回退到指定版本
git reset --hard HEAD^
HEAD is now at 3eb5f77 创建readme.txt文件
看看内容改了没有。
现在我们再查看下log,结果如下:
commit 3eb5f7721cb86c617faf0d2ad13a5cee9d1a3764
Author: = <coder.0012ff7c@gmail.com>
Date: Sun Jan 22 16:16:10 2017 +0800
创建readme.txt文件
好了,时光倒流成功!但是悲哀的发现我们回不到未来了。还是有办法的,–hard 参数支持commit值。
就是说你可以在–hard后面跟一个commit值,就能跳到那个版本。
执行下面的代码
git reset --hard b5934a
HEAD is now at b5934a2 第二次提交
不用将所有的字符都写上,只要能区分出不同就好,git会自动去匹配
但是万一找不到commit值呢,没关系。使用下面的命令可以查看所有的历史
git reflog
显示结果:
b5934a2 HEAD@{0}: reset: moving to b5934a
3eb5f77 HEAD@{1}: reset: moving to HEAD^
b5934a2 HEAD@{2}: commit: 第二次提交
3eb5f77 HEAD@{3}: commit (initial): 创建readme.txt文件
最前面那串字符就是。好了。搞定!
工作区,暂存区,版本库
工作区:就是指当前的目录,比如我们创建的learnGit目录
暂存区:通过git add 命令将文件添加到暂存区。
版本库:就是指创建仓库时自动生成的那个.git目录,也是强烈建议不要乱动的目录。
git中的文件必须先添加到暂存区,然后才能添加到版本库。
重新创建一个文件Hello.java,随便写点内容比如
hello java
然后再修改下readme文件,添加一行
添加 hello.java 文件
然后查看下仓库状态
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
Untracked files:
(use "git add <file>..." to include in what will be committed)
Hello.java
no changes added to commit (use "git add" and/or "git commit -a")
说明readme.txt被修改了,而hello.java还没有被添加到仓库。
然后我们将这两个文件添加到暂存区
git add readme.txt
git add Hello.java
你也可以使用下面的命令将当前文件夹下的所有文件都添加到暂存区
git add –all
再查看下状态显示结果:
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: Hello.java
modified: readme.txt
然后再用commit命令添加到版本库
git commit -m “添加Hello.java文件”
再查看下状态显示结果为
On branch master
nothing to commit, working tree clean
撤销修改
仅仅是修改了工作区,并没有add到暂存区
我们对Hello.java做一些修改,如下:
public class Hello{
public static void main(String[] args){
}
}
如果这个时候我们需要回到上一个版本,当然,你可以手动的修改Hello.java文件。
但是要是万一你哪里再记错了,就尴尬了。
查看一下状态
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: Hello.java
no changes added to commit (use "git add" and/or "git commit -a")
git告诉你说,使用 git checkout – … 可以放弃当前工作区的修改。试一下,
git checkout Hello.java
查看下Hello.java 文件,是不是恢复了。
不仅仅修改了工作区,而且还add到了暂存区。
我们对Hello.java再次做一些修改,如下:
public class Hello{
public static void main(String[] args){
}
}
然后add到暂存区
git add Hello.java
查看一下状态
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: Hello.java
git告诉你说,使用 git reset HEAD … 可以撤销缓存。然后我们尝试一下
git reset HEAD Hello.java
运行结果
Unstaged changes after reset:
M Hello.java
查看一下状态
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: Hello.java
no changes added to commit (use "git add" and/or "git commit -a")
是不是又回到第一种的状态了。
不仅仅修改了工作区,还add到了暂存区,而且手贱的我还commit到了版本库。
如果你目前还没有推送到远程仓库,还有的救,还记得上面说的时光倒流么。不记得的可以翻一翻。
git reset --hard HEAD^
这样就回到了第二种情况。
文件删除
在仓库里新建一个文件,然后添加到版本库
git add test.txt
git commit -m "add test.txt"
然后我们将这个文件删除掉。
rm test.txt
查看一下状态
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: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
git 检测到有文件被删除了,git告诉你有两种方式
git rm <file>... :从版本库删除文件,并重新commit
撤销删除操作,test.txt将被恢复。
使用下面的命令从版本库删除文件。
git rm test.txt
git commit -m "remove test.txt"
小结:通过以上的学习我们已经可以熟练的操作本地的git仓库了。
Git远程仓库
创建远程仓库
git是分布式版本控制系统。分布式,即最开始仅有一个仓库作为服务器,然后其他人从这个仓库克隆出代码,提交到这个仓库,
每个克隆的仓库都可以是一个服务器,各个仓库之间没有主次之分。
要学习远程仓库,首先得有个远程仓库,由于搭建远程git仓库有(本)点(人)复(不)杂(会)。这里使用github作为远程仓库
由于之前写过类似的,这里不再赘述。
需要注意的是,在创建仓库是使用learnGit作为仓库名
你只需做到使用下面的命令测试成功就好
ssh -v git@github.com
推送到远程仓库
将本地仓库与远程仓库关联,在本地仓库目录下执行下面的命令。
git remote add origin git@github.com:coderRan/learnGit.git
注意:git@github.com:coderRan/learnGit.git一定是你自己的创建的仓库,不要复制我的,
因为这是我的仓库,关联不会出问题,推送的时候你是推送不上去的,因为我的github没有添加你的ssh key
现在仅仅是关联起来了,还没有将代码推送到远程仓库,执行下面的命令
git push -u origin master
要是没有出现下面的结果请看0x03
上传文件是需要时间的,所以要耐心等待一下。执行结果
Counting objects: 16, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (16/16), 1.29 KiB | 0 bytes/s, done.
Total 16 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To github.com:coderRan/learnGit.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
去你刚才创建的仓库看一下,应该上传上去了。
第一次推送有点麻烦,因为git需要将本地仓库的分支与远程仓库的分支进行关联。以后推送的话可以使用下面的命令
git push origin master
SSH警告
在第一次使用 push 或者 clone 这个命令时,会弹出一个警告
The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?
这个是SSH链接需要你确认ssh key。
输入yes,结果
Warning: Permanently added 'github.com' (RSA) to the list of known hosts.
这些警告只出现在第一次提交时,以后就不会出来了。
从远程仓库克隆
新建一个 目录
mkdir newGit
cd newGit
然后从我们刚刚新建的仓库克隆一个
git clone git@github.com:coderRan/learnGit.git
执行结果:
Cloning into 'learnGit'...
remote: Counting objects: 16, done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 16 (delta 1), reused 16 (delta 1), pack-reused 0
Receiving objects: 100% (16/16), done.
Resolving deltas: 100% (1/1), done.
Git 分支
分支简介
分支是什么呢?
你可以理解成树枝。
分支可以用来干什么呢?
使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。
假如,你开发了一个支付宝,然后想添加一个扫福的功能,你总不能直接在你的版本库里乱改吧,这个版本正常用着呢,万一改错,不能用了就麻烦了。话说不是有clone么,克隆一份就好了,再怎么改也不会出问题,但是,你在开发过程中,完成一部分工作后总得提交到远程仓库吧。(有人说,不用提交啊,自己在本地仓库搞就好了。注意,协同作业)
这个时候就用到分支了。把仓库看成一棵树,随着一次次的commit,这棵树越长越高,分支就向树枝一样。在分支上可以随便撒野,不会搞坏主分支。你可以把你的修改先提交到自己创建的分支上,等你把这个扫福的功能开发完了,还能合并到主分支上。
分支的创建
在我们创建好仓库后,版本库的状态是下面这样的,其中的[A],[B],[C] 代表一个commit
HEAD->master-->|
|
[A]-->[B]-->[C]
其中 master 代表的是主分支,而HEAD代表的是当前分支,由于只有一个分支所以HEAD指向的是 master
我们使用下面的命令创建一个分支,然后切换到新建的分支。
git branch mybranch
git checkout mybranch
你也可以使用一条命令,创建并切换
git checkout -b mybranch
查看下分支
git branch
master
* mybranch
现在git仓库的状态是下面这样的
master-->|
|
[A]-->[B]-->[C]
|
|<--mybranch<--HEAD
然后我们再次对Hello.java文件做一下修改
public class Hello{
public static void main(String[] args){
}
public void addBranch(){
}
}
然后commit到版本库
分支的合并
现在git仓库的状态是下面这样的
master-->|
|
[A]-->[B]-->[C]~~>[D]
|
|<--mybranch<--HEAD
然后切换到 master 分支
1git checkout master
现在git仓库的状态是下面这样的
HEAD-->master-->|
|
[A]-->[B]-->[C]~~>[D]
|
|<--mybranch
合并到 master 分支
git merge mybranch
执行结果
Updating 28c1cc8..11d62a7
Fast-forward
Hello.java | 3 +++
1 files changed, 3 insertions(+)
现在git仓库的状态是下面这样的
HEAD-->master-->|
|
[A]-->[B]-->[C]~~>[D]
|
|<--mybranch
分支的删除
git branch -d mybranch
解决冲突
快速合并的速度确实很快,但是前提是两个分支没有冲突。
重新创建新的分支
git checkout -b newbranch
修改 readme.txt 文件
hello git
hello git 2.0
添加 hello.java 文件
在 newbranch 分支上做的修改
提交
git add readme.txt
git commit -m "newbranch 修改"
[newbranch b8ac4ad] newbranch 修改
1 file changed, 1 insertion(+)
切换到 master 分支
git checkout master
打开 readme.txt 文件,发现并没有改变,因为我们的修改不是在master分支上的。
添加一行
hello git
hello git 2.0
添加 hello.java 文件
在 master 分支上做的修改
提交
git add readme.txt
git commit -m "master 修改"
[master 80c56bf] master 修改
1 file changed, 1 insertion(+)
现在 两个分支上对一个文件的同一个地方(同一行)做了不同的修改,是没办法快速合并的。
合并下试试
git merge newbranch
git merge newbranch
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
果然,git说, readme.txt 文件冲突了,修复后重新提交。
使用 git status 看看状态
git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
(use "git push" to publish your local commits)
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
git说 对 readme.txt 共同的两个修改
我们直接打开 readme.txt 文件看看
hello git
hello git 2.0
添加 hello.java 文件
<<<<<<< HEAD
在 master 分支上做的修改
=======
在 newbranch 分支进行修改
>>>>>>> newbranch
其中 <<<,===,>>>是分割线。
我们修改一下
hello git
hello git 2.0
添加 hello.java 文件
修复了冲突
然后提交
git add readme.txt
git commit -m “修复了冲突”
[master 9f1e5f5] 修复了冲突
好了,解决。现在git已经将两个分支自动合并了。你可以使用
git log --graph
查看分支图
然后删除 newbranch 分支
git branch -d newbranch
Deleted branch newbranch (was b8ac4ad).
合并的方式选择
从上面我们知道,分支的合并有两种方式,一种是 快速合并,一种是解决冲突的合并。
两种合并的区别是
快速合并要保证没有冲突,合并完删除分支后,会丢掉分支信息。
冲突合并会保留分支信息,Git会在merge时生成一个新的commit。
可以使用--no-ff
参数强制禁用快速合并
git merge --no-ff -m "禁用快速合并" newbranch
因为需要重新生成一个 commit 所以需要添加-m
参数。
Git tag
有时我们需要定位到某个版本,但是目前我们只能通过那个长长的commit号来确定。
为了更好的定位,我们可以给commit 打个tag。
默认在最新的commit上打tag
git tag v1.0
对任意commit打tag
找到commit号(还记得怎么看commitid号么?)
git tag v0.9 <commitid>
查看tag
需要注意的是,tag是按字母排序的额,不是按时间排序的
git tag
查看tag信息
git show <tagname>
删除tag
git tag -d <tagname>
操作远程仓库的tag
因为tag默认是存储到本地的,所以需要手动将tag推送到远程仓库
git push origin <tagname>
如果tag比较多,可以一次将所有tag都推送到远程仓库
git push origin --tags
如果你已经将tag推送到远程仓库了,再想删除就有点麻烦了。
首先需要删除本地的tag
git tag -d <tagname>
然后再删除远程仓库的tag
git push origin :refs/tags/<tagname>
好了,有了这些基本上可以应付工作中的需要了。