Git详解

前言

Git是目前世界上最先进的分布式版本控制系统。本文从头详细地介绍了Git的各种用法。

配置本机用户和邮箱

λ git config --global user.name 名字
λ git config --global user.email 邮箱 

注意:git config--global参数代表本机上所有git仓库都用这个配置。
若想为某个仓库配置其它的用户名和邮箱:

λ git config -user.name 名字
λ git config ser.email 邮箱 

查看全局用户名和邮箱:

λ git config --global user.name
λ git config --global user.email

查看某仓库用户名和邮箱同理,上述语句去掉--global就可以。

创建仓库

将当前目录编程Git可以管理的仓库:

λ git init
Initialized empty Git repository in C:/Users/hwx/Desktop/learngit/.git/

C:/Users/hwx/Desktop/learngit是我的目录,.git目录用来跟踪管理版本库,不用管它。

将文件添加到版本库

注意:所有版本控制系统只能跟踪文本文件的改动,可以告诉你每次改动的细节。但是不能跟踪图片、视频这些二进制文件的变化,只能知道文件大小的变化。

下面我们添加一个readme.txt到仓库,分两步:

1.用命令git add将文件添加到暂存区:

λ git add readme.txt

该命令执行成功不会有任何显示,可以同时add多个文件,中间用空格隔开。

2.用命令git commit将文件从暂存区提交到仓库:

λ git commit -m "add a readme text"
[master (root-commit) 6fd4dfc] add a readme text
 1 file changed, 2 insertions(+)
 create mode 100644 readme.txt

-m后面是本次提交的说明。
注意:git commit只会提交暂存区中的修改,没放进暂存区的不会提交。

git status

查看仓库状态。

1.仓库中有文件没add

λ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        .1.txt.swp

nothing added to commit but untracked files present (use "git add" to track)

2.仓库中有文件修改过了,但是没add

λ 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")

3.仓库中有文件add了,但是没有commit

λ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   readme.txt

4.仓库该add的都add了,该commit的都commit了

λ git status
On branch master
nothing to commit, working tree clean

git diff

工作区是目录,仓库是版本库。
git diff 比较的是工作区文件与暂存区文件(上次git add 的内容)的区别
git diff --cached比较的是暂存区的文件与仓库文件(上次git commit 的内容)的区别
git diff HEAD -- 比较的是工作区文件和仓库文件

git log

查看仓库历史:

λ git log                                                       
commit e1f285812d2202a32fed025f309fb6e7a68fde49 (HEAD -> master)
Author: Wenxing Hu <[email protected]>                            
Date:   Sat Dec 22 14:22:32 2018 +0800                          
                                                                
    readme add forth line                                       
                                                                
commit 859c43221f414215c34c95004ab3930c407caee6                 
Author: Wenxing Hu <[email protected]>                            
Date:   Sat Dec 22 14:14:04 2018 +0800                          
                                                                
    readme add 3 line                                           
                                                                
commit 6fd4dfc32c1ba2bec1fe5e61b85b721c5ed62d2c                 
Author: Wenxing Hu <[email protected]>                            
Date:   Sat Dec 22 13:57:17 2018 +0800                          
                                                                
    add a readme text                                           

从下到上是按照时间排序的每次commit的信息。

信息太多了?加上--pretty=oneline参数:

λ git log --pretty=oneline
e1f285812d2202a32fed025f309fb6e7a68fde49 (HEAD -> master) readme add forth line
859c43221f414215c34c95004ab3930c407caee6 readme add 3 line
6fd4dfc32c1ba2bec1fe5e61b85b721c5ed62d2c add a readme text

每行前面的一大串是commit id。git log --oneline可以只显示前7位id。

git reset

回退到某一个版本
HEAD是个指针,代表当前的版本。上个版本是HEAD^,上上个是HEAD^^,往上n个版本可以写成HEAD~n

λ git reset --hard HEAD~
HEAD is now at 859c432 readme add 3 line

注意:执行git reset --hard HEAD^可能会出来个more?这是因为命令行中^是换行符,所以还是用HEAD~1吧。

也可以根据commit id来进行版本变换:

git reset --hard e1f2
HEAD is now at e1f2858 readme add forth line

这里commit id输入四个字符就可以了。

注意:git log只能看到HEAD指向的版本及之前的版本。
版本变换多了,弄晕了,不要紧,命令git log --reflog用来查看所有版本。还有命令git reflog可以查看每次执行的命令。

撤销修改

1.工作区的文件弄坏了,git checkout -- 文件名这条命令可以使它回到最新的add或commit时的状态,俗称一键还原。
2.工作区的文件弄坏了,还add到了暂存区,用命令git reset HEAD 文件名,可以将其方会工作区,这样就回到了1的情况,再执行1就可以了。

删除文件

git rm 文件名,再git commit

λ git rm test.txt
rm 'test.txt'

λ git commit -m "delete test.txt"
[master ee28494] delete test.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 test.txt

远程仓库

远程仓库这里以GayHub为例,当然你也可以自己搭建一个服务器运行Git。
1.将本地仓库和远程仓库通过SSH连接。

λ ssh-****** -t rsa -C "你的邮箱"
然后根据提示一路回车。。。

运行成功后,可以在用户主目录里找到.ssh目录,里面有id_rsaid_rsa.pub两个文件,这两个就是SSHKey的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。
然后在GitHub中添加SSH key,把公钥添加进去就ok了。

2.关联本地仓库和远程仓库
首先在GitHub中新建一个仓库(Repository):
Git详解
按它的提示,在本地仓库执行命令:

git remote add [email protected]:649453932/git-test.git

将本地仓库推送到远程仓库的命令:

git push -u origin master

其中-u只在第一次推送时加,origin代表远程仓库,注意本地仓库只有commit了的文件才会推送到远程仓库。

之后每次推送时**git push origin 分支名**,你可以推送本地的mastet到远程仓库,当然也可以选择本地的其他分支推送。注意:远程仓库也可以有好多分支。

移除关联的远程仓库:

git remote rm origin

3.从远程仓库克隆

git clone [email protected]:649453932/git-test.git

可以随便从GitHub中clone仓库,clone回来就成了你的一个本地库,但保留了该仓库之前的各种记录。

分支管理

刚开始,只有一个分支,名为master,可以再创建分支,分支之间平时互不影响,可以并行开发。
查看所有分支:git branch,当前分支前会有个*
创建分支:git branch 名字
切换到分支:git checkout 名字
创建并切换到分支:git checkout -b 名字
合并指定分支到当前分支:git merge 名字
删除分支:git branch -d 名字

冲突处理

若master分支和另外一个分支同时对一个文件做了修改,那就不能merge了。
这就需要我们手动解决了:

λ cat readme.txt      
This is a readme text.
This is second line.  
This is third line.   
forth                 
fifth                 
sixth                 
seventh               
eighth_master_dev1    
<<<<<<< HEAD          
master !!!            
=======               
dev2 !!!              
>>>>>>> dev2          

Git用<<<<<<<=======>>>>>>>标记出不同分支的内容我们把这些符号删掉,改成我们想要的内容。再在master分支上add和commit,主分支就改好了,另外一个分支现在就可以merge了。

图形化分支合并情况

git log --graph命令可以查看分支合并情况:

λ git log --graph --pretty=oneline --abbrev-commit
*   99fdeac (HEAD -> dev2, master) conflict fixed!
|\
| * 47c854e ADD eighth
* | 53ba977 ADD eighth_maser
|/
* e555cd8 branch test
* ee28494 (origin/master) delete test.txt
* 0ec907e add test.txt
* cd95b35 fidth line
* 6fd4dfc add a readme text

分支merge技巧

通常,合并分支merge时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
使用--no-ff可以禁用Fast forward,merge时会生成新的commit,就可以从log看到分支。
git merge --no-ff -m "merge with no-ff" 要merge的分支名

λ git merge --no-ff -m "merge noff" dev7
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

然后我们看log,dev6是 Fast forwar 模式进行merge的,而dev7是--no-ff模式进行merge的:

λ git log --graph --pretty=oneline --abbrev-commit
*   5571146 (HEAD -> master) merge noff
|\
| * dbc506e (dev7) dev7
|/
* 2122b61 (dev6) dev6
|
...

git stash

该命令可以将工作现场存储起来,之后可以再恢复。用于临时处理其它分支的事务,处理完再回来将自己存储的工作现场恢复。

λ git stash
Saved working directory and index state WIP on dev6: 2122b61 dev6

git stash可以查看存储的工作现场。
git stash apply恢复现场,但是之后还需要用git stash drop来删除存储的。可以直接用git stash pop来恢复现场并删除存储。

注意:只有被add过的文件才会被git stash存储,注意是add ,只要曾经add过就行。

删除分支

git branch -d dev1

λ git branch -d dev1
error: The branch 'dev1' is not fully merged.
If you are sure you want to delete it, run 'git branch -D dev1'.

若该分支没有被合并,删除就会丢失该分支上的修改,强制删除命令:git branch -D 分支名

多人协作

git remote -v:显示详细信息

λ git remote -v
origin  [email protected]:649453932/learngit.git (fetch)
origin  [email protected]:649453932/learngit.git (push)

上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。

从本地推送时用下面的命令,将本地分支推送到远程仓库的分支:

git push origin 分支名

若两个人同时处理一个远程分支,一个人推送了,另外一个人再推送就会失败,得先用git pull将最新的提交从origin/分支名抓下来与本地合并,再推送。
也有可能两人修改了同一个文件,pull下来有冲突,需要手动解决,解决方式见前面的冲突处理

忽略特殊文件

建一个.gitignore文件。
里面这样写:

# 这是注释
这里是你想忽略的文件
*可以代指任何字符串
...

这样每次git status就不会提示这些文件了。

References:

Git教程——廖雪峰