git

git

git是一个分布式版本控制系统。分布式是相对于集中式来说的,对于集中式版本控制(如SVN),所有的代码都放在集中的服务器上,大家都从这个服务器下载代码,然后在本地开发,然后再上传。而分布式版本控制没有中央服务器,每个人的本地电脑都是一个完整的版本库,可以在本地创建分支,在本地提交代码,还可以提交到远程分支;

git创建

git的仓库就是一个文件夹目录(又叫repository),里面的所有文件都被git进行管理;

所以首先创建一个空目录

$ mkdir myrep    //linux创建文件夹命令
$ cd myrep    

然后使用 git init 把这个普通目录变成git仓库

$ git init

目录下会生成一个 .git 文件夹,里面是管理git仓库相关文件;

可以不必在空文件夹下git init, 也可以直接在现有文件目录下git init;

版本控制系统只追踪文本文件的改动,对于二进制文件则无法追踪其内容变化,只能知道大小变了;

其它linux相关命令:

$ ls -al    //查看当前目录下所有文件,包括隐藏文件
$ cd mydir    //打开文件夹
$ touch abc.txt    //创建文件
$ echo "12345" >> abc.txt    //向abc.txt写入字符串
$ cat abc.txt    //查看abc.txt的文本内容

 时光机穿梭

git时光机

git status 命令,用于随时掌握仓库的当前状态:

$ git status
On branch master    //显示当前在那个分支
Changes not staged for commit:    //变动了的文件,但还没有add到staged区
  (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")

git status 一共有3种:   

untracked files 从未被追踪过的文件,即从来没有被add过的文件;

changes not staged for commit  文本发生了变动,但本次变动没有被add到staged区;

changes to be commited  文本的变动已经add到了staged区,就等着被commit啦;

git diff 命令,就是看difference

$ git diff readme.txt     //diff命令,用于查看对比分析区别
diff --git a/readme.txt b/readme.txt    //目前进行比较的是a版本的readme.txt和b版本的readme.txt
index 46d49bf..9247db6 100644      //前两个数是两个版本的git的哈希值,最后一个数先不管
--- a/readme.txt    //---表示变动前的文件
+++ b/readme.txt    //+++表示变动后的文件
 //下面是合并格式的首部;首部用@@分隔,前两个数-1和2,-表示第一个文件,1表示从第一行开始,2表示一共2行; +1和2用同样的方法表示第二个文件
@@ -1,2 +1,2 @@  
//下面是合并格式的正文内容
-Git is a version control system.    //-表示第一个文件,所以这是第一个文件的第1行  
+Git is a distributed version control system.    //这是第二个文件的第2行
 Git is free software.    //这是两个文件共有的部分,所以是第一个文件和第二个文件的第2行

 版本回退

每一次commit都会产生一个版本,所以版本回退就是回退到某次commit的结果

使用 git reset --hard HEAD^ 可以会退到上一次的版本;  注:--hard参数暂时忽略

//在git中,HEAD表示当前版本,HEAD^表示上一个版本,同理HEAD^^表示上上个版本
//HEAD实质上是一个指针,改变指针的指向就会切换到不同的版本
$ git reset --hard HEAD^ HEAD is now at e475afc add distributed

使用   git reset --hard 某版本号     回退到指定版本号的版本

$ git reset --hard 1094a    //1094a为某个版本号的前几位,版本号没有必要写全,只要系统能识别就行
HEAD is now at 83b0afe append GPL

使用git log来查看历史提交的commit

$ git log
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:06:15 2018 +0800

    append GPL

commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:03:36 2018 +0800

    add distributed

commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 20:59:18 2018 +0800

    wrote a readme file

当回退到旧版本后,又想前进到新版本(回到未来),这时是无法找到新版本的commit id的,这时就需要使用 git reflog 来找出以前的命令:

$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file

 工作区和暂存区

 工作区:就是电脑里面能看到的仓库的目录,即最早初始化git,使用git init 的地方。

 版本库:是工作区文件目录里的隐藏的 .git 目录,这就是git的版本库;工作区和版本库的关系如下:

你平时写的所有代码只有一份,存储在工作区中,平时写代码也是对工作区的代码文件进行编辑;

而使用 git add 命令就会把代码的变动情况存储到stage区,也叫索引区、暂存区(注意是把代码的变化情况存入stage,而非把整个工作区代码存入stage);

使用 git commit 命令就可将stage区的缓存一次性提交到当前HEAD所指向的分支;

 git管理的是修改而非整个文件

举例来说,如果有操作序列: 第一次修改 -> git add -> 第二次修改 -> git commit;则会发现第二次的修改没有被提交,执行 commit 后git会提示 changes not staged for commit,这就是说第二次的修改没有被commit,即说明git管理的是文件的变动,而非整个文件(可以把修改当成一个对象实体,git管理的就是这样的对象)

改为 第一次修改 -> git add -> 第二次修改 -> git add -> git commit,则顺利将第二次的修改也提交了。

撤销修改

要和版本回退相区分;

场景一:修改了某文件,但还没有add到暂存区,想撤销修改时,用命令 git checkout -- fileName;

              注:git checkout -- fileName 命令,当做了修改但没有add时,使用该命令后撤回到修改前的原始状态;当add过后又做了修改时,使用该命令撤回到刚add后的状态;

场景二:修改了文件,并add到了暂存区,想要撤销修改,分两步走

               先使用 git reset HEAD fileName, 可以将暂存区的修改撤销掉,使之变成 changes not staged for commit 状态

               然后再用场景一中的 git checkout -- fileName 进行撤销修改

场景三:实在不行就版本回退吧;

 删除文件

//先正常删除文件
$ re test.txt
//这时若使用 git status 查看状态,会发现处于 changes not staged for commit状态

//如果确实需要删除文件,则用git rm 从版本库删除文件,并commit
$ git rm test.txt
$ git commit -m "remove test.txt"

//如果是误删,想恢复,则参照上一节的撤销修改操作,使用 git checkout -- fileName
$ git checkout -- test.txt                        

 远程仓库

用github做远程服务器

第一步:创建ssh key。 在用户主目录下(C盘user目录)查看是否有id_rsa和id_rsa.pub文件,如果没有则需要生成。用bash或shell创建ssh密钥:

$ ssh-keygen -t rsa -C "youremail@xxx.com"

然后一路默认直到完成。之后会在主目录里发现 .ssh目录, 里面有上述两个文件,其中 id_rsa.pub 是公钥,id_rsa是私钥。(原理是数字签名)

第二步,登录github,在ssh的key设置中添加公钥。

之后就可以使用ssh协议将本地的git和远程的GitHub关联了

添加远程仓库

第一步:在github上新建一个repository,随便起名比如myrepo

第二步:在本地仓库运行 git remote add,将本地仓库与远程仓库相关联

//添加后远程库的名字就是origin,也可以起别的名字
$ git remote add origin git@github.com:yourGitHubID/myrepo.git

//将本地内容推送到远程库, origin是远程库的名字,master是当前本地分支
//实质是将本地库的当前分支推送到远程
$git push -u origin master

由于在push前远程库是空的,所以第一次push时,加上-u参数,git不仅会将本地的master分支内容推送到远程的此时新建的master分支,还会把本地master分支和远程master分支关联起来。

以后push就只需要使用 git push origin master 推送新的修改了

远程库克隆至本地

// git@github...等一串字符是远程仓库地址
$ git clone git@github.com:yourGitHubID/yourRepo.git

 分支管理

创建与合并分支,及其原理

开始的时候,master分支是一条线,git用mater指向最新的提交,再用HEAD指向master,就能确定当前分支和分支的提交点。

 

每次提交,master都会向前移动一步,因此HEAD即当前分支始终指向最新。

当创建新的分支,如dev时,git上新建一个指针叫dev,指向与master相同的提交,再将HEAD指向dev,表示当前在dev分支

现在开始,修改和提交都是针对dev分支了,如果再在dev分支提交一次,会出现master指针不变,而dev指针向前移动了一步

如果在dev上的开发工作完成了,需要把dev的工作合并到master分支,git会直接把master指针指向dev当前的指向,完成合并

合并完成后,可以删除dev分支了

相关指令:

$ git branch     //查看分支
$ git branch Name    //创建分支
$ git checkout Name    //切换分支
$ git checkout -b Name    //创建+切换分支
$ git merge Name    //合并Name分支到当前分支,【注意一定是合并到当前分支,而非合并到Name分支】
$ git branch -d Name    //删除分支

 解决冲突

当两个不同分支修改了同一个文件时,再试图合并这两个分支时,就会出现冲突;如下master和feature1分支都修改了readme.txt,并且都进行了提交,若试图合并则会导致冲突

$ git merge feature1    //尝试合并, git提示冲突,冲突文件为readme.txt
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result

这时需要解决冲突,可以打开 readme.txt, 发现git已经将冲突部分标识出来了

// <<<<<< 标记冲突开始,后面跟的是当前分支的代码
//  ====== 用于分隔
//  >>>>>> 与等号之间的部分是需要合并过来的分支的冲突代码

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

这时只需要手动修改冲突部分,可以将当前分支(master)的代码改成带合并过来的代码,然后在当前分支commit;然后再进行合并;

上图就是将master分支的代码进行了修改,与feature保持一致,然后将feature合并到master

 分支管理策略

团队开发时的分支策略如上图

master分支:应该是非常稳定的,仅仅用来发布新版本

dev:dev分支非常不稳定,用来在上面开发

每个人各自的分支:用于c克隆dev的代码,并在上面开发

另【个人理解】:master分支和dev分支应该是在本地和远程都有

Bug分支

场景如下:

master上上线的1.0版本;

你在自己的zqw分支上开发2.0版本,但只开发到一半;此时用户反映1.0版本有Bug,你需要紧急修复1.0中的Bug

你想直接切换到master分支,再在master分支的基础上创建Bug分支修复Bug,修复完再切回zqw分支继续开发2.0,但这样会留下一个不怎么好的没有意义的commit记录,还很麻烦;这么做可以但没必要;

或者你一想,干脆别commit了,直接切回master分支,再在master分支下创建Bug分支,再做修复等等操作....   【这样不可行】,理由是未经add 和 commit 的内容不属于任何一个分支,也就是对所有分支而言,工作区和stage区是公共的,你没commit就切换分支,之前工作区的一些编辑操作仍会保留

此时就需要用stash功能了,它可以把2.0的开发工作不经过add 和 commit 就保存下来;然后你就可以放心切换到master分支修复Bug,然后完成后再切换回2.0的zqw分支继续开发;

步骤如下

$git stash

然后切到master分支,创建bug分支改bug,合并会master分支

$git checkout dev    //切回开发分支
$git stash pop    //恢复现场

 多人协作

常用指令

$ git remote -v    //查看远程库信息

//本地新建的分支,如果不推送到远程,则对他人不可见;
//若是本地新建分支推送到远程,则会在远程新建同名的分支,但不会自动生成本地分支到远程对应分支的关联

$ git push origin branch-name    //从本地将branch-name分支推送到远程origin

$ git pull  <远程主机名> <远程分支名>:<本地分支名>    //从远程分支取回代码,并和本地分支做合并; <>内容都可省略,省略后则默认为当前本地分支,和其对应关联的远程分支

//git pull 等价于 git fetch + git merge,实际最好使用fetch+merge
$git fetch origin master:temp    //从远程master分支获取代码,并在本地建立temp分支
$git merge temp    //将temp分支合并到当前分支

$ git checkout -b branch-name origin/branch-name;    //在本地创建新分支,该分支和远程某分支对应

$ git branch --set-upstream-to branch-name origin/branch-name;    //建立本地分支和远程分支的关联

$git branch -vv    //查看本地分支与远程分支的关联情况

多人协作的模式:

首先用 git push origin your_branch 推送自己的修改;

如果推送失败,则说明远程分支比你的当前本地分支更新,你需要先将远程分支pull到本地做合并

如果合并有冲突,则手动修改解决冲突,并在本地提交;

然后再push到远程

原文地址:https://www.cnblogs.com/zhuqiwei-blog/p/10724950.html