安装git:
yum install zlib (系统默认已经装上)
yum install zlib-devel
sudo yum install gcc-c++
yum install perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker
wget https://www.kernel.org/pub/software/scm/git/git-2.10.0.tar.gz
tar -zxvf git-2.10.0.tar.gz
cd git-2.10.0
./configure
make
make install
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
创建版本库:
mkdir learngit
git init //通过git init命令把这个目录变成Git可以管理的仓库,当前目录下多了一个.git的目录,用ls -ah命令就可以看见。
touch readme.txt //新建一个文件
git add readme.txt //把文件添加到仓库
git commit -m "wrote a readme file" //把文件提交到仓库
因为commit可以一次提交很多文件,所以你可以多次add不同的文件,比如:
git add file1.txt
git add file2.txt file3.txt
git commit -m "add 3 files."
git status命令可以让我们时刻掌握仓库当前的状态。
git diff命令查看上次修改的内容。此命令比较的是工作目录中当前文件和暂存区域快照之间的差异, 也就是修改之后还没有暂存起来的变化内容。
git diff --staged命令查看已暂存的将要添加到下次提交里的内容。
git commit -a命令Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而可以跳过 git add 步骤。
git rm filename命令删除暂存区中的文件。
git rm --cached filename命令删除暂存和已经提交的文件。
git mv file_from file_to命令重命名文件,相当于$ mv README.md README,$ git rm README.md,$ git add README三个命令。
git log命令查看提交历史记录。回退后记录被清除。也可加上--pretty=oneline参数:git log --pretty=oneline
git reflog命令查看命令历史记录。
版本回退:
Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交(例如:3628164...882e1e0),上一个版本就是HEAD^, 上上一个版本就是HEAD^^, 往上100写成HEAD~100。
git reset --hard HEAD^
git reset --hard 3628164 //版本号前几位即可
- soft 参数:将本地版本库的头指针全部重置到指定版本,且将这次提交之后的所有变更都移动到暂存区。
- 默认的mixed参数:将本地版本库的头指针全部重置到指定版本,且会重置暂存区,即这次提交之后的所有变更都移动到未暂存阶段(工作区)。
- hard参数:会将工作区代码也回退到这个版本
工作区和暂存区:
比如learngit文件夹就是一个工作区,工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
如上图
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
在创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。
管理修改:
第一次修改 -> git add -> 第二次修改 -> git commit
Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交。在工作区的第二次修改并没有放入暂存区,所以git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。
撤销修改:
git checkout -- readme.txt
命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
- readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
- readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
git reset HEAD readme.txt
用命令git reset HEAD file可以把暂存区的修改撤销掉(unstage)。
场景1:当修该了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。
场景2:当修改了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退,不过前提是没有推送到远程库。
删除文件:
rm test.txt
git status //这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了
git rm test.txt
git commit -m "remove test.txt"
rm test.txt
git status
git checkout -- test.txt //撤销了删除操作,其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
GitHub:
在 https://github.com/settings/ssh 中添加公钥。
git remote add origin git@github.com:zzhblh/HelloWorld.git //远程库的名字就是origin,这是Git默认的叫法
git push -u origin master
第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
更好的方式是先创建远程库,然后,从远程库克隆:
git clone git@github.com:zzhblh/HelloWorld.git
会在当前目录创建一个叫做HelloWorld的git目录。
分支管理:
一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:
当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:
git checkout -b dev
git checkout命令加上-b参数表示创建并切换,相当于git branch dev
git checkout dev
。查看当前分支git branch
,当前分支前面会标一个*号。从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:
如果
git checkout master
切换回master,会发现master没有改变,因为那个提交是在dev分支。假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:
git checkout master
切换回master,git merge dev
命令用于合并指定分支到当前分支。直接把master指向dev的当前提交。合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:
git branch -d dev
删除dev分支。解决冲突:
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突:
$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
git status
可以告诉我们冲突的文件:
git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
我们可以直接查看readme.txt的内容:
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用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存:
Creating a new branch is quick and simple.
再提交:
git add readme.txt
git commit -m "conflict fixed"
[master 59bc1cb] conflict fixed
现在,master分支和feature1分支变成了下图所示:
用带参数的git log也可以看到分支的合并情况:
git log --graph --pretty=oneline --abbrev-commit
* 59bc1cb conflict fixed
|\
| * 75a857c AND simple
* | 400b400 & simple
|/
* fec145a branch test
...
最后,删除feature1分支:
git branch -d feature1
Deleted branch feature1 (was 75a857c)
分支策略:
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活。
干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本。
每个人都在dev分支上干活,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
Bug分支:
Git还提供了一个stash功能,需要把当前工作现场“储藏”起来,等以后恢复现场后继续工作。需要stash的原因是因为一个本地的git repo只有一个工作区和暂存区,而我们的checkout只是将HEAD指针从一个分支切换到另一个分支。所以工作区或暂存区不‘干净’(有未commit或stash的内容)的话,会把当前的修改提交到切换之后的分支。
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
$ git checkout -b issue-101
Switched to a new branch 'issue-101'
现在修复bug,需要把“Git is free software ...”改为“Git is a free software ...”,然后提交:
$ git add readme.txt
$ git commit -m "fix bug 101"
[issue-101 cc17032] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)
修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 commits.
$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
readme.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git branch -d issue-101
Deleted branch issue-101 (was cc17032).
现在,是时候接着回到dev分支干活了。为了保证dev上的相同bug被修复,需要将master合并至dev上:checkout dev
切换回dev分支,git merge master
合并master至dev上。
$ git checkout dev
Switched to branch 'dev'
$ git merge master
$ git status
# On branch dev
nothing to commit (working directory clean)
工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看:
$ git stash list
stash@{0}: WIP on dev: 6224937 add merge
工作区需要恢复,有两个办法:
一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
另一种方式是用git stash pop,恢复的同时把stash内容也删了。
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
$ git stash apply stash@{0}
推送/抓取分支:
多人协作时,大家都会往master和dev分支上推送各自的修改。当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。
要查看远程库的信息,用git remote:
$ git remote
origin
或者,用git remote -v显示更详细的信息:
$ git remote -v
origin git@github.com:zzhblh/HelloWorld.git (fetch)
origin git@github.com:zzhblh/HelloWorld.git (push)
上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。
推送分支:
就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:
$ git push origin master
如果要推送本地dev分支到远程dev分支,就改成:
$ git push origin dev:dev
抓取分支:
从远程库clone时,默认情况下,只能看到本地的master分支。可以用git branch命令查看:
$ git branch
* master
git fetch:相当于是从远程获取最新版本到本地,不会自动merge。
从远程的origin的master分支到origin/master分支上:
Git fetch origin master
从远程获取origin的remotebranch分支到本地,本地会创建一个remotebranch的copy:
$git checkout localbranch
$git fetch origin remotebranch
$git branch
master
*localbranch
remotebranch
从远程获取master到本地的tmp分支上(不会自动切换到该分支),之后再进行比较合并:
git fetch origin master:tmp
git diff tmp
git merge tmp
git pull:相当于是从远程获取最新版本并merge到本地,其实相当于git fetch 和 git merge。
从远程的origin的master主分支下载最新的版本,并与当前分支进行合并:
git pull origin master
在实际使用中,git fetch更安全一些。因为在merge前,我们可以查看更新情况,然后再决定是否合并。
跟踪远程分支:从远程分支 checkout 出来的本地分支,称为跟踪分支 (tracking branch)。跟踪分支是一种和某个远程分支有直接联系的本地分支。在跟踪分支里输入 git push/ git pull,Git 会自行推断应该向哪个服务器的哪个分支推送/获取数据。在克隆仓库时,Git 通常会自动创建一个名为 master 的分支来跟踪 origin/master。这正是 git push 和 git pull 一开始就能正常工作的原因。
$ git checkout -b dev origin/dev
或关联本地已存在的dev分支:
$ git branch --set-upstream dev origin/dev
多人协作的工作模式通常是这样:
首先,可以试图用git push origin branch-name
推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用git push origin branch-name
推送就能成功!
如果git pull
提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name
。
git仓库:
安装git后,创建一个git用户组和用户,用来运行git服务:
$ groupadd git
$ adduser git -g git
创建证书登录
收集所有需要登录的用户的公钥,客户端的公钥在客户端的id_rsa.pub文件中,把我们的公钥导入到刚创建的用户git下的.ssh目录(没有.ssh目录则创建)。/home/git/.ssh/authorized_keys文件里,一行一个。
如果没有该文件创建它:
$ cd /home/git/
$ mkdir .ssh
$ chmod 700 .ssh
$ touch .ssh/authorized_keys
$ chmod 600 .ssh/authorized_keys
初始化Git仓库
首先我们选定一个目录作为Git仓库,假定是/home/gitrepo/w3cschoolcc.git,在/home/gitrepo目录下输入命令:
$ cd /home
$ mkdir gitrepo
$ chown -R git:git gitrepo/
$ cd gitrepo
//创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾
$ git init --bare w3cschoolcc.git
Initialized empty Git repository in /home/gitrepo/w3cschoolcc.git/
以上命令Git创建一个空仓库,服务器上的Git仓库通常都以.git结尾。然后,把仓库所属用户改为git:
$ chown -R git:git w3cschoolcc.git
在其他机器上就可以克隆仓库
$ git clone git@192.168.45.4:/home/gitrepo/w3cschoolcc.git
Cloning into 'w3cschoolcc'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
在IntelliJ IDEA中使用仓库
设置git.exe路径:
重启idea,在vcs下可以看到git菜单项。
把一个本地项目提交到刚刚创建的git仓库:
- vcs->Import into Version Control->Create Git Repository选中目录,把它变成git目录。
- vcs->git->remotes关联远程库,URL为仓库地址git@192.168.45.4:/home/gitrepo/w3cschoolcc.git。
- vcs->git->push即可推送。
- vcs->git->branch -> +New Branch,输入分支名称,自动创建并切换到新的分支。
- vcs->git->branch->分支名->Checkout切换分支。
从仓库获取项目:
- vcs->Check Out from Version Control->Git。
-
填写仓库地址和本地目录名字。
参考:
https://git-scm.com/book/zh/v1/
https://marklodato.github.io/visual-git-guide/index-zh-cn.html
https://segmentfault.com/q/1010000000156026