Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Git基础 #119

Open
xwchris opened this issue Oct 12, 2020 · 0 comments
Open

Git基础 #119

xwchris opened this issue Oct 12, 2020 · 0 comments

Comments

@xwchris
Copy link
Owner

xwchris commented Oct 12, 2020

GIT本质上是一个内容寻址文件系统。从核心来看就是简单的存储键值对。

GIT基本原理

在GIT中有远程仓库本地仓库暂存区工作区的概念。我们平时修改文件的地方就属于工作区,在文件修改完成后,我们使用git add .的命令将工作区内容放入缓存区。

在缓存区中,GIT会根据文件内容生成SHA-1值(校验和),作为文件的唯一ID,我们可以使用

git hash-object [filename]

来查看指定文件计算后的SHA-1值。GIT会使用校验和的前两个字符作为目录名称,用剩下的字符作为文件名,将文件以二进制的形式存储进当前GIT根目录的.git/objects文件夹,这里我们可以使用以下命令来查看所有保存的文件列表

find .git/objects -type f

注意这些文件里存储的只是文件内容,不包括文件的信息。查看二进制文件的内容可以使用

// 查看内容
git cat-file -p [sha-1]

// 查看类型
git cat-file -t [sha-1]

在使用git commit命令后,GIT会生成本次提交的快照,将其永久保存到本地仓库。那么GIT是如何表示快照和生成快照的?我们可以在提交后再次使用

find .git/objects -type f

来查看目录,会发现多了一些文件,其实除去我们的文件,剩下的都记录了我们的目录信息和提交信息。总的来说.git/objects里的文件分为三类

  1. blob:这就是我们的文件类型
  2. tree:tree有多条记录,每一条记录含有一个指向 blob 或子 tree 对象的 SHA-1 指针,并附有该对象的权限模式 (mode)、类型和文件名信息
  3. commit: 用来记录我们的提交信息,包含了根目录SHA-1值,以及提交者信息和提交信息

通过上面的解释我们会发现一个commit指向根目录,根目录指向各个子目录和文件。因此我们完全可以用commit来表示本次提交,保存提交的快照。当然GIT就是这么做的。

分支就是执行某次提交的一个指针,所以我们创建分支,就只是创建了一个指针,所以GIT进行分支创建非常迅速。

分支合并

在GIT中,分支的合并有两种方式,分别是git mergegit rebase。无论哪种都可能产生冲突,有冲突就需要先解决冲突才能完成合并。

分支合并

冲突是以两个分支的分歧点(merge base)进行定义的,如上图所示两个分支的分歧点为1。如果对比1来说两个分支相同的文件都与1不同,这时候就产生了冲突,其他情况有一个相同或者都相同,可以自动进行合并。

使用git merge的方式进行分支合并,在解决冲突后,会形成一次新的提交到目标分支上。这种方式不会修改历史提交记录。

还可以使用git rebase进行分支合并,与git merge方式不同,该方式会改变提交历史。该方式合并时GIT会把merge base以来的所有提交,一个个以补丁的形式打到目标分支上,在这里有冲突需要解决冲突。最终所有的提交会形成一条线。这种方式可以让分支更加干净整洁。

版本回退

如果我们某次提交完后,发现这次提交有问题,想要回到某次提交,有这么几种方式

可以使用

git revert [sha-1]

命令来反转某次提交,相当于取消这一次提交所提交的代码。这种方式是用一个新的提交来进行代码还原的,并不会改变分支提交的历史。

除了git revert,还可以使用git reset配合--soft--mixed--hard参数来进行回退,这三个参数的作用域依次增大,--soft会将HEAD指针指向某次提交,--mixed--soft相比就是多了个缓存区,--hard会完全将代码还原到当次提交,慎重使用。不过真的不小心用错了,可以使用

git reset --hard ORIG_HEAD

进行还原,或者使用

git reflog

命令找到当时HEAD的SHA-1值,将HEAD重置到该节点就可以了

BUG查找

如果某次我们发现有问题,又找不到bug在那次引入的,这时就可以使用git bisect命令帮助我们进行查找。

使用的命令如下

git bisect start

git bisect bad HEAD
git bisect good v4.1

我们只需要使用git bisect goodgit bisect bad进行标记,git bisect会使用二分法帮助我们定位直到找到bug引入的源头。如果操作很简单,可以是使用来执行脚本进行操作

git bisect run test/run.sh

本文参考廖雪峰的git教程,整理学习,尊重原创

配置git

git config用来配置git

git config --global user.name 'xxxxx'
git config --global user.email '[email protected]'

生成密钥使用

// 使用rsa方式生成密钥
ssh-keygen -t rsa

在linux中密钥默认存在用户的.ssh文件里。可以使用ssh-copy-id命令来讲公钥拷贝到远程的.ssh/authorized_keys文件中

// -i 用来指定认证文件(公钥)
ssh-copy-id -i ~/.ssh/id_ras.pub [email protected]

创建版本库

git init用来把一个目录变成git仓库,该文件夹可以不是空的

git init

git add添加文件,可以使用通配符.添加所有文件。git add是添加到缓存区,缓存区(stage)是gitsvn不同的地方,可以简单理解为将修改完的通通放到缓存区,最后可以一次性提交

git add .

git commit 一次性的把所有缓存区的内容提交到分支,-m 后面跟提交信息

git commit -m "fix: fixed some bugs"

版本文件管理

git status可以查看仓库当前状态

git status

git diff查看工作区和版本库最新版的不同(目前还不太会用)

git diff

版本回退

git log可以显示从最近到最远的提交,如果想要精简输出可以在后面加上--pretty=oneline参数

git log --pretty=oneline

结果显示的3628164...882e1e0等一大串数字字母组合是commit id(版本号),是SHA1计算出来的,可以避免冲突

git reset可以用来回退版本,在gitHEAD表示最新版,HEAD^表示上一版本,HEAD^^表示上上版本,当然往上100个版本,数不过来可以写成HEAD~100,同时git reset --hard commitId可以回到指定的版本

git reset --hard HEAD^
git reset --hard 3628164

git reflog 查看commit id

git reflog

撤销修改

git checkout -- file 会丢弃工作区的修改,让文件回到最后一次git commitgit add的状态

git checkout -- readme.txt

这个命令里面的--很重要,没有--就变成了切换到另一个分支的命令
git reset HEAD file将缓存区回退到工作区

git reset HEAD readme.txt

删除文件

git rm可以用来删除文件,相当于先在工作区删除,再添加到缓存区,最后再使用git commit就可以删除分支的文件了

git rm

远程仓库

关联远程库

git remote add origin url关联远程的仓库

git remote add origin [email protected]:用户名/仓库名称.git

git push把本地库的内容推送到远程,第一次推送时加上-ugit不但会把本地的master分支内容推送到远程的master分支,还会把本地的master分支和远程的master分支关联起来,在以后推送和拉取的时候就可以简化命令,直接使用git push origin master

git push -u origin master

从远程库克隆

git clone从远程克隆库到本地

git clone [email protected]:用户名/仓库名.git

分支管理

创建和合并分支

git branch查看当前的所有分支和当前分支,
后面跟名字可以创建新的分支,git checkout <branch>用来切换分支

git branch dev
git checkout dev

这两个命令可以简写为git checkout -b <branch>

git checkout -b dev

表示创建并切换到新的分支

git merge <branch>将某分支与当前分支合并

git merge dev

git merge -d <branch>合并并删除某分支

git merge -d dev

解决冲突

git merge合并文件发生冲突的时候,我们可以用git status查看文件,然后手动解决冲突后再添加-提交-合并
git log --graph可以看到分支合并图

git log --graph`

分支管理策略

git中,如果可以的话,git会默认使用Fast forward模式,但在这种模式下,删除分支后,会丢掉分支信息,强制禁止'Fast forward'就可以在merge时,生成一个新的commit,从分支历史上就可以看出分支信息,禁止Fast forward使用--no-ff

git merge --no-ff -m 'merge with --no-ff' dev

这样使用git log就可以看到曾经做过合并,否则使用Fast forward是看不到曾经做过合并的
在实际开发中,应该按照以下几个基本原则进行分支管理:

  1. master是非常稳定的,仅仅用来发布新版本
  2. 平时大家都在dev上干活,也就是说,dev是不稳定的,到版本发布的时候,再把dev合并到master
  3. 每个人都有自己的分支,时不时的往dev上合并就可以了

bug分支

git stash可以把当前工作现场储存起来

git stash

git stash list列出所有存储的现场

git stash list

git stash apply可以恢复现场,而且可以恢复指定的现场

git stash apply
git stash apply stash@{0}

现场恢复后,stash并不删除,需要用git stash drop来删除

git stash apply
git stash drop

也可以用git stash pop代替那两个命令,直接恢复并删除

git stash pop

Feature分支

开发新的功能最好建立一个新的分支,然后合并,删除分支。如果现在新功能取消,只能删除掉该分支,但由于该分支还没有合并,会导致删除失败,只能强行删除,这时就要用git branch -D <name>强行删除

git branch -D new-fearture

多人协作

当从远程仓库克隆到本地之后,远程的master分支就和本地的master分支对应起来了,并且远程仓库的默认名称是origin。要查看远程仓库可以用git remote

git remote

查看更详细的信息,后面加-v参数

git remote -v

会显示可以抓取和推送的地址,如果没有推送权限就看不到push地址。
并不一定所有的分支都需要推送

  • master是主分支,因此要时刻与远程保持同步
  • dev是开发分支,团队所有成员都在上面工作,因此也需要与远程同步
  • bug分支只用于在本地修复bug没必要推送
  • feature是否推送,取决于是否与他人合作开发

当从远程克隆到本地时只能看到master分支,由于要在dev分支上开发,所以可以创建与远程dev分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地分支与远程分支名称最好一致

git checkout -b dev origin/dev

从本地推送到远程时最后先git pull,避免冲突,如果pull的时候出现no tracking information说明本地分支与远程分支没有建立关系,这时可以使用git branch --set-upstream branch-name origin/branch-name命令

git branch --set-upstream dev origin/dev

标签管理

标签就是指向某个commit的指针,跟分支很像,不过分支可以移动而标签tag不能,为了快速找到版本,而commit id又不好记,这时就可以用tag取一个有意义的名字

创建分支

git tag <tagname>用来创建分支

git tag v1.0

默认是打在最新的commit上面的,如果要给以前的commit打标签,就可以用git log找到当时的commit id然后用git tag <tagname> <commitid>来就可以了

git tag v0.9 622494

git tag可以查看所有标签,它们是按字母排序的

git tag

git show <tagname>可以查看标签的详细信息

git show v0.1

还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字

git tag -a v1.0 -m "version 1.0 release"

还可以通过-s用私钥签名一个标签

$ git tag -s v0.2 -m "signed version 0.2 released" fec145a

操作标签

标签只会存在本地,因此可以在本地安全的删除,使用git tag -d <tagname>

git tag -d v1.0

要推送标签到远程使用git push origin <tagname>

git push origin v1.0

或者一次性推送所有未推送的本地标签
git push origin --tags

git push origin --tags

推送到远程再删除就要两步,先用git tag -d <tagname>删除本地的,在用git push origin :refs/tags/<tagname>

git push origin :ref/tags/v1.0

自定义git

忽略特殊文件

如果不想提交git中的某些文件可以,在根目录下添加一个.gitignore文件,把要忽略的文件填进去,git就会自动忽略这些文件。github准备了各种配置文件

设置别名

有时候为了方便可以设置一些别名使用alias

git config --global alias.st status

这样git status就可以直接用git st调用,在配置git的时候--global是对当前用户起作用的,如果不加只能对当前仓库起作用。配置文件都放在.git/config
另一个别名例子

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"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant