gitStudy

Github详细学习笔记

  • @Author Tadm
  • @DateTime 2019-11-16 T08:45:24+0800
  • @return {Git Study}

Features

快照无差异

使用Git,每次提交或保存项目状态时,Git基本上都会拍下当时所有文件的快照,并存储对该快照的引用。

​ 为了提高效率,如果文件没有更改,Git不会再次存储该文件,而只是指向它已经存储的先前相同文件的链接。

几乎所有操作可以本地进行

具有完整性

​ Git中的所有内容在存储之前都经过校验和,然后由该校验和引用。
​ Git用于此校验和的机制称为SHA-1哈希。这是一个由40个字符组成的字符串,由十六进制字符(0–9和a–f)组成,并根据Git中文件或目录结构的内容进行计算。
​ SHA-1哈希看起来像这样:24b9da6552252987aa493b52f8696cd6d3b00373
​ 您会在Git的所有位置看到这些哈希值,因为它使用了很多哈希值。实际上,Git并不是通过文件名而是通过其内容的哈希值将所有内容存储在数据库中。

Git具有文件可以驻留的三个主要状态:

​ Modifyed

  • 已修改意味着您已更改文件,但尚未将其提交到数据库。

​ Staged

  • 暂存意味着您已标记了当前版本的已修改文件以进入下一个提交快照。

​ Commit:

  • 提交意味着将数据安全地存储在本地数据库中。

Git download and install is so easy!

Doing sth

查看所有设置以及这些设置的来源:

git config --list  //  show configurations
git config --list --show-origin

配置您的身份(–global 表示全局应用)

git config --global user.name "Liuhongwei3"
git config --global user.email "2873126657@qq.com"
git config --global color.ui true // 配置颜色

ssh-keygen -t rsa -C “2873126657@qq.com“
// click the enter three times
// Then copy the "id_rsa.pub" contents to your github account to add the ssh key(maybe new SSH key)

ssh -T git@github.com // test the connect success or not

// add remote repository
git remote add origin git@server-name:path/repo-name.git

git remote
// origin

git remote -v // show remote repository infos

Initiation

git init     //	 Generate .git directory(default info)
git add . // add all files or xxx(one file)
git commit -m 'this is the first commit'
// commit and write the description(important)

git push -u origin master // push to remote
// -u: "git push" replace "git push origin master"
// now we publish the process that local to remote~

git clone URL // Clone it to our computer from URL
// eg: git clone git@github.com:Liuhongwei3/xxx.git
// 如果想在克隆远程仓库的时候,自定义本地仓库的名字,可通过额外的参数指定新的目录名:
git clone https://github.com/libgit2/libgit2 mylibgit
// 这会执行与上一条命令相同的操作,但目标目录名变为了 mylibgit

// Sometimes maybe use pull(拉取并合并)
git pull origin master // origin master 分支

git status // show status (-s short)
git diff // show difference

More Args

.gitignore

​ 通常是自动生成的,是不希望Git自动添加甚至不显示给您的文件。例如日志文件或构建系统生成的文件。

cat .gitignore

*.[oa]
*~

# dependces
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
  • 第一行告诉Git忽略任何以“ .o”或“ .a”结尾的文件-对象和归档文件,它们可能是构建代码的产物。

  • 第二行告诉Git忽略名称以波浪号(~)结尾的所有文件,许多文本编辑器(例如Emacs)使用该文件来标记临时文件。您可能还包括一个log,tmp或pid目录。自动生成的文档;等等。

git rm: delete file

$ git rm PROJECTS.md

​ 下次提交时,文件将消失并且不再被跟踪。如果您修改了文件或已经将其添加到临时区域,则必须使用 -f 选项强制删除。
​ 这是一项安全功能,可防止意外删除尚未记录在快照中且无法从Git恢复的数据。

​ 您可能想要做的另一件事是将文件保留在工作树中,但将其从暂存区中删除。换句话说,您可能希望将文件保留在硬盘上,但不再需要Git对其进行跟踪。

​ 如果您忘记向.gitignore文件中添加某些内容并意外地将其暂存,例如大型日志文件或一堆已.a编译的文件,这将特别有用。为此,可以使用–cached选项:

git rm --cached README
git rm log/\*.log
// 反斜杠是必要的,因为Git会执行自己的文件名扩展,除了扩展Shell的文件名。此命令将删除目录中所有具有.log扩展名的log/文件。
git rm \*~
// 此命令将删除所有名称以~结尾的文件。

git add

git add file/directory/.
  • 开始跟踪新文件

  • 已跟踪的文件放到暂存区

  • 合并时把有冲突的文件标记为已解决状态等。

    将这个命令理解为“精确地将内容添加到下一次提交中”而不是“将一个文件添加到项目中”要更加合适。

git commit

git commit 
// 把暂存区的所有内容提交到当前分支
git commit -m 'commit info'
git commit -a -m 'commit info'
// git add file && git commit -m ''

git commit --amend
// 将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令), 那么快照会保持不变,而你所修改的只是提交信息。
当你在修补最后的提交时,并不是通过用改进后的提交原位替换掉旧有提交的方式来修复的。从效果上来说,就像是旧有的提交从未存在过一样,它并不会出现在仓库的历史中。

git push

git push <远程主机名> <本地分支名> <远程分支名>

git push // branch-->origin(default)
// 默认只推送当前分支

git push origin master
// 将本地分支推送到与之存在追踪关系的远程分支(通常两者同名),如果该远程分支不存在,则会被新建

git push -u origin master
// 指定一个默认主机,以后可以直接使用 git push

git push --force origin
// 强制 push 到远程服务器

git push <remote> <branch>

git push --all origin
// 不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机

git push origin master:refs/for/master
// 将本地的 master 分支推送到远程主机 origin 上的对应 master 分支

git push origin :refs/for/master
// 省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支,等同于 git push origin --delete master

// refs/for 的意义在于我们提交代码到服务器之后是需要经过code review 之后才能进行merge的,而refs/heads 不需要

git push origin :refs/tags/v1.4-tadm
// 将冒号前面的空值推送到远程标签名,从而高效地删除它

git push origin --delete <tagname>
// delete the remote tag directly

git reset

git reset HEAD <file>...
// 取消暂存某个文件

git mv—rename

$ git mv README.md README
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README

// same as the following
mv README.md README
git rm README.md
git add README

git remote

git remote		//	show remote branch
git remote -v // show remote branch more infos

git remote add [shortname] [url] // shortname replace url
git remote add origin git@server-name:path/repo-name.git

git remote show origin
// Gives some information about the remote <name>

git remote rename origin my
// rename remote repository name

git remote remove my
// remove my remote repository

git fetch

git fetch origin
// 拉取远程仓库到本地但是并不会合并

git pull

git pull origin master	//  origin master 分支
git pull <repository> // 拉取并合并某个仓库到本地,往往在远程服务器更新了,但是本地没有更新的情况下经常使用,后续会有提及

git tag

git tag [-l/--list]		//	show the tag list
git tag v1.5.0 // make a tag: v1.5.0---light
git tag -a v1.4-tadm -m "my version 1.4" // full
git tag -a v1.2 9fceb02
// 给提交历史中的某个校验和(complete or parital)打标签
git tag -d v1.4-tadm // delete the specified local tag
git push origin :refs/tags/v1.4-tadm
// 将冒号前面的空值推送到远程标签名,从而高效地删除它

git push origin --delete <tagname>
// delete the remote tag directly

git show v1.2 // show the tag infos
git push origin v1.5 // push v1.5 to remote
git push origin --tags // push all tags to remote
  • light

    轻量标签只是某个特定提交的引用,本质上是将提交校验和存储到一个文件中——没有保存任何其他信息.

  • full

    附注标签是存储在 Git 数据库中的一个完整对象, 它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证。 通常会建议创建附注标签,这样你可以拥有以上所有信息.

git status && git diff

git status		//	show files status
// 凡是被 'git add .' 添加的文件都会被git跟踪
git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
新添加的未跟踪文件前面有 ?? 标记,
新添加到暂存区中的文件前面有 A 标记,
修改过的文件前面有 M 标记。
// 输出中有两栏,左栏指明了暂存区的状态,右栏指明了工作区的状态。上面的状态报告显示: README 文件在工作区已修改但尚未暂存,而lib/simplegit.rb 文件已修改且已暂存。 Rakefile 文件已修,暂存后又作了修改,因此该文件的修改中既有已暂存的部分,又有未暂存的部分。

git diff
// 查看尚未暂存的文件更新的部分
git diff --staged
// 查看已暂存的将要添加到下次提交里的内容,将比对已暂存文件与最后一次提交的文件差异
git diff --cached
// 查看已经暂存起来的变化

git log—show log

git log
// the lastest is the top

git log -p -2
// show last twice commits

git log --since=2.weeks
// limit the log\'s origin time
// 可以是类似 "2008-01-15" 的具体的某一天,也可以是类似 "2 years 1 day 3 minutes ago" 的相对日期

git log --stat
// 查看每次提交的一些简短统计信息

git log --shortstat
// 只显示 --stat 中最后的行数修改添加移除统计

git log --pretty=oneline
// 使用不同于默认格式的方式展示提交历史,oneline( --pretty=oneline --abbrev-commit 合用的简写) 选项将每个提交打印在一行上,另外还有 short,full 和 fuller 选项,它们展示信息的格式基本一致,但是详尽程度不一

git log --pretty=oneline --graph
// 在日志旁以 ASCII 图形显示分支与合并历史,更加清楚形象

git log --pretty=oneline --graph --abbrev-commit
// 仅显示 SHA-1 校验和所有 40 个字符中的前几个字符

git log --pretty=format:"%h - %an, %ar : %s"
// 自定义显示格式,some use frequently args:
%H 提交的完整哈希值
%h 提交的简写哈希值
%T 树的完整哈希值
%t 树的简写哈希值
%P 父提交的完整哈希值
%p 父提交的简写哈希值
%an 作者名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 --date=选项 来定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期(距今多长时间)
%s 提交说明

git log --oneline --decorate
// 查看各个分支当前所指的对象

Alias

git config --global alias.last 'log -1 HEAD'
// 以后就可用 'git last' replace 'git log -1 HEAD'

Advanced

Branch Explanation

Git 保存的不是文件的变化或者差异,而是一系列不同时刻的 快照

在进行提交操作时,Git 会保存一个提交对象(commit object)。该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象, 而由多个分支合并产生的提交对象有多个父对象.

在 Git 中,HEAD是一个特殊指针,指向当前所在的本地分支(译注:将 HEAD 想象为当前分支的别名),默认为 master 分支;指向该分支上的最后一次提交(可看做该分支上的最后一次提交 的快照)。

git branch

git branch		//	show all branches
git branch -v // 查看每一个分支的最后一次提交
git branch test // 创建 test 新分支但不会自动切换到新分支
// 只是为你创建了一个可以移动的新的指针
git log --oneline --decorate // 查看各个分支当前所指的对象

git branch -d test // delete test branch
git branch -D test // 想要删除分支并丢掉那些工作

git branch --merged(--no-merged)
// 看哪些分支已经合并到当前分支(未合并)

git branch --set-upstream-to=progit/master master
// 将 master 分支设置为从 progit 远端抓取

git checkout

git checkout test		//	切换到 test 分支
// 此时 HEAD 指针指向 test 分支,此时对某些文件做一些修改并提交,则会使得 test 分支向前移动,但是此时 master 分支仍处于修改前的状态
git checkout master
// 此时发生了两件事:一是使 HEAD 指回 master 分支,二是将工作目录恢复成 master 分支所指向的快照内容,此时项目就处于修改前的状态。这个时候我们再次修改文件时,就会产生分叉,因为此时是在另一个分支做了些许修改;也就是说我们这两次的修改是不会相互影响:接着就可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。
git log --oneline --decorate --graph --all
// 查看提交历史、各个分支的指向以及项目的分支分叉情况
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the

git checkout -b(branch) test1 // 新建 test1 分支并切换到该分支

git checkout -- CONTRIBUTING.md
// 撤销对 CONTRIBUTING.md 文件的修改

// 为区分于git checkout -- file ---撤销命令 创建并切换到新的dev分支
$ git switch -c dev

// 直接切换到已有的master分支
$ git switch master

由于 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。 创建一个新分支就相当于往一个文件中写入 41 个字节(40 个字符和 1 个换行符).

这与过去大多数版本控制系统形成了鲜明的对比,它们在创建分支时,将所有的项目文件都复制一遍,并保存到一个特定的目录。 完成这样繁琐的过程通常需要好几秒钟,有时甚至需要好几分钟。所需时间的长短,完全取决于项目的规模。 而在 Git 中,任何规模的项目都能在瞬间创建新分支。 同时,由于每次提交都会记录父对象,所以寻找恰当的合并基础(译注:即共同祖先)也是同样的简单和高效。 这些高效的特性使得 Git 鼓励开发人员频繁地创建和使用分支。

git merge

git merge test1		//	此时在 master 分支
// 将在把 test1 分支下的修改合并到 master 分支上

git merge --no-ff -m 'merge with no ff' dev
// 禁用 Fast-forward 模式合并,而是生成一个新的 commit

​ 在分支合并的时候,会有下列几种情况:

  • 如果想要合并的分支所指向的提交被合并到的分支所指向的提交直接后继则会使得合并采用 Fast-forward 的模式进行合并,就会非常简单,相当于只是简单将被合并的指针推到欲合并的指针处。
  • 如果不是直接后继,也就是我们在切换到新分支后并做了多次提交后,这样他们的直接祖先就不再是同一个,此时git就需要将两个分支的末端快照他们的工作祖先进行三方合并,接着会将合并的结果生成一个新的快照并自动创建一个新的提交指向他。
  • 有冲突的合并,Git 会将其合并,但是不会自动地创建一个新的合并提交,因为需要我们去解决合并产生的冲突,此时使用 git status 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件;为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。

文字可能会显得太过生板,你可以参考这篇文章来加深理解。

分支开发工作流

​ 在 master 分支上保留完全稳定的代码

​ 在develop 或者 next 的平行分支来做后续开发或者测试稳定性

​ 在feature分支上开发新功能

git rebase

变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。

也可以进行分支合并,而且效果非常的”棒”,因为稍稍带点复杂,他只是会使得我们的提交历史更加整洁一条直线,没有分叉),实际效果和 merge 一样。在这种情况下,你首先在自己的分支里进行开发,当开发完成时你需要先将你的代码变基(重新播放,也就是更改部分重新应用)到 origin/master 上,然后再向主项目提交修改。 这样的话,该项目的维护者就不再需要进行整合工作,只需要快进合并便可。

git checkout test
git reabse master
git checkout master
git merge test

但是当我们的分支很多的时候,比如从一个主题分支里再分出一个主题分支的提交,如果按照merge合并虽然可以做到,但是却会出现很多分支。

git rebase --onto master server client
// 取出 client 分支,找出它从 server 分支分歧之后的补丁, 然后把这些补丁在 master 分支上重放一遍,让 client 看起来像直接基于 master 修改一样

更多关于变基可以阅读这篇文章,非常的形象和详细。

Github Fork

如果想参与开源项目的 bug 修复,则需要进项目主页面fork,将其clone到自己的仓库,然后再从自己仓库clone到本地,这样才有权限push,若要项目方接受你的修改需要pull request,当然最后是否OK取决于项目方是否同意你的方案并采纳。

More: Read there for detailed~

Do your contribution: Read there

End

到此我们算是已经非常熟悉 Git 的使用了,但是至于深层次的原理可以日后再做查阅和研究。

参考资料

https://git-scm.com/book/zh/v2

https://www.cnblogs.com/qianqiannian/p/6008140.html

Author: 𝓣𝓪𝓭𝓶
Link: https://liuhongwei3.github.io/2019/11/16/GitStudy/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.