Git 学习笔记

268 阅读11分钟

安装Git

sudo apt-get install git

创建版本库

版本库可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改,删除Git都能够跟踪,或者在将来某个时刻可以还原

1. 如何创建版本库?
    git init
2. 如何把文件添加到仓库?
    git add readme.txt,可以反复多次使用,添加多个文件到仓库
3. 如何把文件提交到仓库?
    git commit -m "xxx"

版本回退

Git内部含有一个指向当前版本的HEAD指针,当回退版本的时候Git仅仅修改了HEAD的指针指向,然后顺便把工作区的文件更新了
1. git reset --hard HEAD^
2. git reset -- hard "commit id"

已经回退到上个版本了,现在后悔了,如何回到最新的版本?
使用git reflog查看最新版本的commit id,然后使用git reset -- hard "commit id"命令

工作区与暂存区

1. 什么是工作区?
    电脑里能看到的目录
2. 版本库里含有什么?
    在工作区中有一个.git目录,他就是git的版本库
    Git的版本库中含有许多重要的东西,其中最重要的是称为stage的暂存区,Git自动为我们创建的一个master分支,以及指向分支的指针HEAD

3. 文件修改提交到仓库的步骤?
    1. 使用git add把文件添加进去,实际上是把文件修改添加到暂存区
    2. 使用git commit提交更改,实际上是把暂存区的所有内容提交到当前分支
    可以简单理解为,需要提交的文件修改通通放到暂存区,然后一次性提交暂存区的所有修改。一旦提交后,那么工作区就是"干净的"

管理修改

Git跟踪并管理的是修改,并非是文件
第一次修改->git add -> 第二次修改 -> git commit 
当使用git add 命令后,在工作区的第一次修改被放入暂存区,准备提交。但是第二次修改没有放入到暂存区,git commit只负责把暂存区的修改提交了,也就是只提交了第一次的修改,第二次的修改不会被提交
第一次修改->git add -> 第二次修改 -> git add -> git commit

撤销修改

git checkout -- file 丢弃工作区的修改
git reset HEAD file  可以把暂存区的修改撤销掉,git reset既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。

场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。

场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

文件删除

1. 删除一个文件的命令?
    rm file
2. 如何恢复删除的文件?
    git checkout -- file
3. 如何从版本库中删除该文件?
    git rm file / git add file
    git commit

远程仓库

由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:
1. 创建SSH Key。在用户主目录下查看有没有.ssh目录,再看看这个目录下有没有id_rsa 和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key
    ssh_keygen -t rsa -C "youremail@example.com"
2. 在gitHub的设置中国添加SSH Key(id_rsa.pub文件的内容)
问题: 为什么gitHub需要SSH Key呢?
    因为gitHub需要识别出来你推送的提交确实是你推送的,而不是别人冒充的,而git支持ssh协议,所以gitHub只要知道了你的公钥,就可以确认只有你自己才能推送

添加远程库

1. 怎么添加远程库?
    git remote add origin "xxxxxx"
2. 本地库的内容第一次推送到远程库
    git push -u origin master
    把本地库的内容推送到远程,实际上是把当前分支master推送到远程
    由于远程仓库是空的,我们第一次推送master分支时,加上-u参数,git不但会把本地的master分支内容推送到远程新的master分支,还会把本地的master分支与远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令
3.从远程仓库克隆一个本地库
    git clone "xxxxx"

创建与合并分支

1. 什么是分支?
    每次提交,git都会把他们串成一条时间线,这条时间线就是一个分支。
2. 分支的理解

HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以HEAD指向的是当前分支
    
当我们创建并切换新的分支,例如dev时,git新建了一个dev指针指向master相同的提交点,再把HEAD指向dev,就标识当前分支在dev上

假如我们在dev上的工作完成了,就可以把dev合并到master上。git怎么合并呢,最简单的方法就是把master指向dev的当前提交,就完成了合并

合并完分之后,甚至可以删除dev分支,删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支

3.如何创建,合并,删除分支?
    git checkout -b dev 或者 git switch -c dev  创建dev分支并切换到dev分支
    git branch dev 创建分支
    git checkout dev 或者 git switch dev 切换分支
    git merge dev 合并某分支到当前分支上
    git branch -d dev 删除分支

解决冲突

master分支与feature分支分别有新的提交

把俩个分支的修改进行合并就可能造成冲突
git merge feature
必须手动解决冲突后在提交
通过手动解决冲突 -> git add -> git commit

用带参数的git log也可以看到分支合并情况
git log --graph --pretty=oneline --abbrev-commit
--pretty=online --abbrev-commit 的简写 --oneline
$ git log --graph --pretty=oneline --abbrev-commit
*   cf810e4 (HEAD -> master) conflict fixed
|\  
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/  
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file
git merge --abort是什么意思?
该命令仅仅在合并后导致冲突使用,git merge --abort将会抛弃合并的过程并尝试重建合并之前的状态。但是合并之前如果存在未commit的文件,git merge --abort将无法重建合并之前的状态。建议使用git -stash命令将未commit的文件暂存起来,并在解决冲突之后使用git stash pop把这些未commit文件还原出来

总结:
    当git无法自动合并分支时,就必须先解决冲突,解决冲突之后再提交
    解决冲突就是把合并失败的文件手动解决成我们希望的内容,在提交
    用git log --graph命令可以查看分支合并图

分支管理策略

通常合并分支时,如果可能,git会用Fast forward模式,但这种模式下,删除分支后会丢掉分支信息。
如果强制禁用Fast forward模式,git就会在merge时生成一个新的commit,这样从分支历史上就可以看出分支信息
git merge --no-ff -m "merge with no-ff" dev
小结:
    合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并看不出来曾经做过合并
    
    在实际开发中,我们应该按照几个原本原则进行分支管理
    首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不在上面干活
    干活都在dev分支上,也就是说,dev分支时不稳定的,到某个时候,比如1.0版本发布时,在把dev分支合并到master上,在master上发布版本
    你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

bug分支

修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除
当手头工作没有完成时,先把工作现场git stash一下,然后修复bug,修复后在git stash pop回到工作现场
在master分支上修复的bug, 想要合并到当前dev分支,可以用git cherry-pick commit命令,把bug提交的修改复制到dev分支上,避免重复劳动

feature分支

开发一个新feature,最好新建一个分支
如果要丢弃一个没有被合并过的分支,使用 git branch -D name强行删除

多人协作

多人协作的工作模式
1.首先,可以尝试git push origin branch-name推送自己的修改
2.如果推送失败,则因为远程分支比本地分支更新,需要使用git pull进行合并
3.如果合并有冲突,则解决冲突,并在本地提交
4.没有冲突或者解决掉冲突后,再用git push origin barnch-name推送就能成功
如果git pull提示no tracking information,则说明本地分支与远程分支的没有创建关联关系,使用git branch --set-upstream-to branch-name origin/branch-name
小结:
    1.如何查看远程库信息?
        使用git remote -v
    2.本地新建的分支如果不推送到远程仓库,对其他人都是不可见的
    3.从本地推送分支,使用什么命令?
        在推送之前使用 git pull抓取下远程的新提交
        使用git push origin branch-name推送本地分支
    4.如何从本地创建和远程分支对应的分支?
    git checkout -b branch-name origin/branch-name
    5. 如何建立本地分支与远程分支的关联?
    git branch --set-upstream-to branch-name origin/branch-name

Rebase

整合不同分支的修改主要有俩种方式: merge与rebase
有master,dev俩个分支,在master分支修改a.txt,dev分支中修改b.txt

merge方式

它会把俩个分支的最新快照以及二者最近的共同祖先进行三方合并,合并的结果生成一个新的快照并提交
dev分支:
➜ git_test git:(dev) vi b.txt 
➜ git_test git:(dev) ✗ git ci -am"add world"
[dev 5b5422e] add world
 1 file changed, 1 insertion(+)
master分支:
➜ git_test git:(master) vi a.txt
➜ git_test git:(master) ✗ git ci -am"add hello"
[master 9b2082e] add hello
 1 file changed, 1 insertion(+)
在master合并dev分支
➜ git_test git:(master) git merge dev
Merge made by the 'recursive' strategy.
 b.txt | 1 +
 1 file changed, 1 insertion(+)
使用git log 查看效果,提交历史记录是分叉的
git log --graph --oneline
*   41ee203 (HEAD -> master) Merge branch 'dev'
|\  
| * 5b5422e (dev) add world
* | 9b2082e add hello
|/  
* ab2bd84 create file a.txt,b.txt

rebase方式

使用rebase命令将提交到某一分支上的所有修改都移至另一分支上
master分支:
➜ git_test git:(master) vi a.txt 
➜ git_test git:(master) ✗ git ci -am"add hello"
[master cc4082e] add hello
 1 file changed, 1 insertion(+)
➜ git_test git:(dev) vi b.txt 
➜ git_test git:(dev) ✗ git ci -am"add world"
[dev 86f2346] add world
 1 file changed, 2 insertions(+)
 ➜ git_test git:(dev) git rebase master
First, rewinding head to replay your work on top of it...
Applying: add world
切换到master分支,合并dev分支
git checkout master
➜ git_test git:(master) git merge dev
Updating cc4082e..8d57446
Fast-forward
 b.txt | 2 ++
 1 file changed, 2 insertions(+)
使用git log 查看效果,提交历史是一条直线,没有分叉
git log --graph --oneline
* 8d57446 (HEAD -> master, dev) add world
* cc4082e add hello
* ab2bd84 create file a.txt,b.txt
一般我们这样做的目的是为了确保在向远程分支推送时能保持提交历史的整洁。

创建标签

git tag <tagname>用于新建一个标签,默认为HEAD,也可以指定commit id 创建标签
命令git tag -a <tagname> -m "说明文字" 
命令git tag可以查看所有标签
命令git show <tagname>可以查看标签信息

操作标签

git push origin <tagName> 推送标签到远程 
git push origin --tags 推送所有为推送的本地标签
git tag -d <tagName> 本地删除标签 
git push origin :refs/tags/<tagName> 远程删除标签

配置别名

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch

把暂存区的修改撤销
git config --global alias.unstage 'reset HEAD'
显示最后一次提交信息
git config --global alias.last 'log -1'
显示日志
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

忽略文件

已经被提交的文件如何被忽略?
git update-index --assume-unchanged C.md

原文地址-廖雪峰Git教程