git分为本地仓库和远程仓库。
git本地仓库的核心分为三部分,工作区、缓存区(或暂存区)、仓库本身,依次对应 Working Directory、index、HEAD,详情戳这里👈。
在文档中总会使用<url>
这样的标记,<>中表示某种对象,整体表示的是对象值。
比如url表示的是网址,<url>
表示的就是具体的那个网址,也就是那一长条的字符串。
如果英文可以直接体现含义,将不再单独解释。
推荐一个git可视化网页游戏
.git/refs/heads
放置本地分支信息
.git/refs/remotes/origin
放置远程仓库origin的分支信息
.git/refs/tags
放置tags信息
.git/logs/refs/heads
放置本地各个分支commit日志信息
.git/logs/refs/remotes/origin
放置远程仓库origin的各个分支commit日志信息
.git/HEAD
记录本地当前分支
.git/objects
存储对象信息,包括数据对象、树对象、提交对象、标签对象等等。git采用无损压缩算法,将原文件内容压缩和还原,这也就是为什么切换分支后,对应的文件内容也会更新。git会将这些压缩的内容用SHA-1得到摘要,这就是object hash,摘要的前两个字符作为目录,剩下的字符作为文件名,压缩后的内容就放在这些文件里。
查看object内容的方法
git cat-file -p <object-hash>
.git/index
这个文件就是暂存区
查看该文件的方法
git ls-files
- 在github上创建远程仓库,自动生成
main
分支,拷贝仓库地址url
, 拷贝authenticate token
; - 进入本地项目目录下执行
git init
, 自动生成master
分支; - 新建分支
git branch dev
; - 切换到新分支
git checkout dev
; - 提交项目所有文件到本地缓存
git add --all
; - 把本地缓存提交到本地仓库
git commit -m <message>
; - 添加远程仓库名称
git remote add origin <url>
; - 执行
git push --set-upstream origin
; - 输入github账户名和
<authenticate token>
; - 推送到远程仓库
git push origin dev
; - 远程仓库将出现dev分支,包含项目中的所有代码;
- 在github上拷贝仓库地址
url
; - 进入本地存储该项目的文件夹下,执行
git clone <url>
; - 拉取远程仓库分支的信息
git fetch origin
; - 在本地新建一个远程仓库的同名分支develop,并和该分支关联
git checkout develop
也可以执行
git checkout --track origin/develop
还可以给本地分支换个名字
git checkout -b mydevelop origin/develop
git config --global user.name <your_name>
git config --global user.email <your_email>
git config --global credential.helper store
执行后,第一次调用git pull 或者 git push,输入密码,之后就不需要输入密码了
对于github,已经改为
authenticate_token
验证方式,不再使用密码。
假设你位于project文件夹下,且该文件夹就是你的项目根目录
此时project仅仅是文件系统的文件夹而已,并不是git本地仓库
执行 git init
结果:
- 将project初始化为本地仓库
- 建立一个默认分支master
假设你知道远程仓库地址
url
在后续 push 或者 pull 操作中,总会使用这个url
因为url
本身是很长的字符串,非常不方便记忆和命令行输入
所以,类似ip地址
和域名
的做法,给远程仓库地址取个别名使用
执行
git remote add origin <url>
origin
就是别名,你可以取一个你喜欢的名字
后续使用远程仓库地址的地方,就可以使用 origin
代替
对于支持用户名和密码方式访问的git服务端,可以在url中加入用户名和 密码信息,可避免git push 或者 git pull 时再输入用户名、密码。
假设url是"https://bbhub.com/bbb.git",用户名为xiao,密码为ak47,
则加入用户名和密码的url为"https://xiao:[email protected]/bbb.git".最后执行
git remote add origin <url>
即可
后续执行和origin相关的push和pull操作,就不会再用输入用户名、密码
假设当前远程仓库地址别名是 origin,但出于某种原因,必须要改为 own
执行
git remote rename origin own
很好理解,把某个名字重命名为另一个名字,旧名字当然在前,新名字在后啦
假设当前仓库地址别名是 origin, 但是远程仓库地址由<url>
改成了 <new_url>
考虑到兼容性,你想继续使用 origin作为远程仓库新地址的别名
执行
git remote set-url origin <new_url>
假设当前仓库地址别名是 origin,但你想不起来origin对应的远程仓库地址是什么了
执行
git remote get-url origin
git remote -v
git clone <url>
url是远程仓库地址.
假设远程仓库的默认分支是main,在上述指令执行完毕后
- 你将拥有一个本地仓库
- 本地仓库会有一个main本地分支, 该分支会与远程main分支关联
- 你将得到一个自动创建的远程仓库的别名origin
- 你将获取远程仓库所有分支的分支信息,用
git branch -r
可查看
如果你想更进一步,在拷贝完远程仓库后,不留在main分支,而是新建 一个和远程分支同名且关联的本地分支,然后留在这个新的本地分支
可执行
git clone -b <branch-name> <url>
分支名一定要和远程仓库众多分支中的一个保持一致!
git branch <branch-name>
将从当前分支拉出一个新分支,名字为<branch-name>
你也可以基于tag创建分支:
git branch <branch-name> <tag-name>
假设你处于分支master,想切换到分支dev
执行 git checkout dev
假设你处于分支master,想创建一个新分支dev,并切换到dev分支
执行 git checkout -b dev
假设你处于分支dev, 并知道远程仓库的一个分支名dev_remote_1
你发现本地没有一个分支关联这个远程分支
你想创建一个新的本地分支关联这个远程分支
- 执行
git checkout --track origin/dev_remote_1
在本地创建一个dev_remote_1分支,并切换到该分支,且 该分支和远程的dev_remote_1分支关联
等效于
git branch -t dev_remote_1 origin/dev_remote_1
git checkout dev_remote_1
-
执行
git checkout dev_remote_1
效果同上, 但本地不能有叫做 dev_remote_1的分支,否则 会直接切换到该分支,不会创建新分支
-
执行
git checkout -b dev_1 origin/dev_remote_1
本地创建一个叫 dev_1的分支,该分支与远程分支dev_remote_1 关联,并切换到dev_1分支
如果上述执行失败,请执行
git fetch origin -a
, 再重试
git branch -d <branch-name>
分支
<branch-name>
必须和远程分支完成充分的合并,或者根本 没有关联到远程分支。
或者
git branch -D <branch-name>
强制删除。
假设你位于 dev 分支,要删除的是远程 dev33分支。
方法一:
git push origin : <remote-branch-name>
即
git push origin :dev33
;
会删除remotes/origin/dev33 分支记录,还会删除远程仓库origin的dev33分支;
方法二:
git push -d origin <remote-branch-name>
即
git push -d origin dev33
;
注意
git branch -d -r origin/dev33
只会删除remotes/origin/dev33 分支记录,不会影响到远程分支; 本地分支也可以命名origin/dev33,所以用 -r 表示后边的分支指代远程仓库分支记录, 是remotes/origin/dev33分支记录; 如果省略
-r
,只会删除本地名为origin/dev33的分支。
如果在bash中:
$ git branch -a
* master
remotes/origin/master
remotes/origin/master 是远程仓库origin的master分支,在本地仓库的一个记录;
git branch -m <old-branch-name> <new-branch-name>
很好理解,把某个分支移动为另一个分支,因此旧的分支名在前, 新的分支名在后
git branch
git branch -r
r 就是 remote
git branch -a
a 就是 all
git branch -vv
可获取 本地分支名、本地分支关联的远程分支名、本地分支版本是否领先远程分支;
For example:
iss53 7e424c3 [origin/iss53: ahead 2] forgot the brackets
master 1ae2a45 [origin/master] deploying index fix
*serverfix f8674d9 [teamone/server-fix-good: ahead 3,
behind 1] this should do it
testing 5ea463a trying something new
head 2
表示本地分支有2个提交没有同步到远程分支
behind 1
表示远程分支有1个提交还没有合并到本地分支
*
号表示当前分支
假设:
- 你处于分支dev
- 远程仓库名你已经设定为origin
- 你想将分支dev和远程分支develop关联
执行 git branch -u origin/develop
-u 是 --set-upstream-to 的缩写
设置关联的好处:
当你想push或者pull的时候,只需执行git push origin
或 git pull origin
,
关于远程分支的信息可省略
假设你处于分支dev,该分支已经和远程分支develop关联,你想取消关联
执行 git branch --unset-upstream
-
执行
git fetch origin dev
拉取远程分支dev的分支信息到本地,执行
git branch -a
将会看见remotes/origin/dev
-
执行
git fetch origin -a
拉取远程仓库origin所有分支的分支信息到本地
项目中的文件在最开始只是停留在文件系统中,git没有对它进行版本跟踪
需要将文件添加到本地仓库缓存区后,git才会对它进行版本跟踪
执行
git add <文件存储路径>
第一次执行时,文件会被git跟踪
之后执行时,文件的改动信息会被存储到本地仓库缓存区
对于所有已被追踪的文件,可以一步到位
git add --all
你执行完 git add指令,将一些文件的修改加入到本地仓库的缓存区后,觉得
不妥,想撤销刚才的 git add操作,
执行
git reset HEAD <filename>
指定的文件就会被取消已发生的 git add操作
git commit -m <message>
message 是本次提交的一些说明,方便以后查看提交记录排查问题、锁定重要版本代码
还有另一种情况极为常见
- 你执行了一次git commit操作
- 之后,你发现提交的message有问题,或者代码修改仍存在问题
你很可能按照 git add、git commit的流程再来一遍,这样你会提交了 两次,但是你也可以只提交一次
你要做的就是
- 继续修改文件,改完后 git add
- 提交的时候执行
git commit --amend
这样,你不会重新提交一次,而是将上一次和本次的提交合并为一个来处理
在开发过程中,固然可以借助编辑器的撤销指令回退,但你无法确定回退到哪一步
刚好是上次提交后的情形。如果你想将文件的内容恢复到上次提交后的样子,执行
git checkout -- <filename>
注意,这将改变本地文件系统中的文件,而文件在本地仓库缓存区中的记录不会受到 影响
git rm --cached <filename>
文件将会从本地仓库的缓存区中移除,不再接收git的版本追踪,但不会从文件系统中删除
如果想更进一步,把文件也删除掉,执行 git rm <filename>
当你修改文件名称后,缓存区的信息将和文件不一致,此时你还要重新git add,
当然,有一种更简单的方法,执行
git mv <filename> <file_newname>
git diff
返回本分支文件在缓存区和工作区的差异
你修改一个文件,执行git add,之后你继续修改这个文件,你在某个 时间点想知道手头上的文件,和上次git add时有什么差异,就可以用此命令
git diff --cached
你使用git commit 完成了一次提交,此时所有文件的状态记作A, 你又开始工作,修改了一些文件,之后执行git add将改动加入缓存 区,此时缓存区中所有文件的状态记作B,这时你想看看A和B之间有什么 差异,就可以用该命令
git diff <branchname1> <branchname2>
git diff <branchname1> <branchname2> <filename>
git diff <branchname1> <branchname2> --stat
在完成了 git commit后,就可以将本地分支推送到远程仓库
假设你位于本地分支main, 并准备推送到远程分支develop
git push origin main:develop
git push
默认会选择推送给origin
;- 把某个分支推送到另一个分支,本地分支名当然在前,远程分支名当然在后啦
- 如果省略
:develop
,会将main分支推送到它所关联的远程分支, 没有关联的远程分支的话,就会在远程仓库创建一个新的同名分支,并推给这个分支。本地会生成origin/main
虚拟分支。- 如果省略
main
,相当于删除远程分支develop- 如果本地分支已经关联到远程分支develop且二者同名,
可简写为git push origin
- 尽管本地分支和远程分支关联, 但他们的分支名字不同,git会考虑安全问题选择拒绝执行push。
当远程分支有更新,你需要让本地分支与远程分支同步一下
假设你位于本地分支main,要去同步的远程分支为develop
git pull origin develop:main
- 把某个分支拉到另一个分支上合并,远程分支名当然在前,本地分支名当然在后啦
- 如果省略
:main
,会将远程分支develop拉取到当前本地分支- 如果当前本地分支已经关联远程分支develop且二者同名,可简写为
git pull origin
如果想让两个本地分支合并,可以使用git merge 命令:
git merge jack
将jack分支合并到当前分支git cherry-pick <commit-id>
将某次提交所做的改动合并到当前分支上,也可以是多次提交,提交ID号用空格隔开即可。
假设你位于本地分支main,想让该分支每次git push操作推送给远程分支develop
git push --set-upstream origin develop
对于git pull的情形,同理有
git pull --set-upstream origin develop
设置本地分支和远程分支相关联,其实就是同时完成了上边的两个设置
-
执行
git tag <tagname>
, 会根据当前本地分支上一次的git commit结果创建一个标签或者,更进一步,你可以为标签添加一些解释信息
git tag -a <tagname> -m <message>
你也可以根据某一次的commitID创建标签
git tag <tagname> <commitID>
实际上
git tag <tagname>
是git tag <tagname> HEAD
的缩写 -
推送到远程仓库
git push origin <tagname>
或者git push --tags origin
在github上的tags列表就能看到你新创建的标签
git tag -l
or git tag
git ls-remote --tags origin
git tag -d <tagname>
git push origin --delete <tagname>
git checkout -b <branchname> <tagname>
你开发完v1.0.0的代码,打好一个标签v1.0.0
你要接着v1.0.0的代码开发v2.0.0的代码
你就可以使用上述指令创建一个新的分支,在该分支上继续你的开发
git log
你可以从输出看到每一次提交信息,最重要的是commitID
;
该指令还有一些拓展用法
-
git log -2
只显示最近两次的提交记录 -
git log --since=2.weeks
只显示最近两周内的提交记录 -
git log --util=2.weeks
只显示两周之前的提交记录 -
git log --author=jack
只显示提交记录中作者是jack的所有记录 -
git log --committer=jack
只显示提交记录中提交者是jack的所有记录 -
git log --grep=mm
只显示提交记录message中包含 mm的所有记录 -
git log -S straw
只显示添加或者删除内容匹配straw的提交记录 -
git log --stat
额外列出修改的文件名 -
git log --shortstat
额外显示--stat中修改的行数信息 -
git log -p
额外列出被修改的文件,改前和改后的差异 -
git log --pretty=oneline
每一个提交的记录单行输出,除了oneline还可以有full
short
fuller
format
-
git log --pretty=format:"%h %an %ar %s"
采用format,自定义输出信息格式
%h 提交的简写hash码
%H 提交的完整hash码
%an 作者名字
%ae 作者邮箱
%ad 作者修订的日期
%ar 作者多久前修订的
%cn 提交者名字
%ce 提交者邮箱
%cd 提交者修订的日期
%cr 提交者多久前修订的
%s 提交的信息
使用git log
找到某一版本的commitID
-
执行
git reset <commitID>
缓存区和本地仓库数据会回滚到该版本,工作区不受影响
-
执行
git reset --sort <commitID>
本地仓库数据会回滚到该版本,工作区和缓存区不受影响
-
执行
git reset --hard <commitID>
本地仓库、缓存区、工作区数据回滚到该版本
在此基础上,执行一步git push操作即可完成远程分支版本回滚
假设你在dev_1分支上编写代码,出于某种原因,你要切换到dev_2分支
修改一些代码,可是,你没有git add和git commit,无法完成分支切换,
这种情况下,你可以
git stash
git checkout dev_2
- 解决dev_2的代码问题,在commit后,切换回dev_1分支
git stash apply
orgit stash pop
- 继续修改dev_1分支上未完成的编码工作
当你执行 git status
后,如果发现终端无法显示正常的中文字符,你可以
这样解决:
执行 git config --global core.quotepath false
当你要提交的文件中,出现单文件超过100MB的文件时,会被github远程拒绝,而你尝试删除该文件后,发现依旧会被拒绝,这是因为在过去的commit记录中,保留该文件的信息。
为了解决这个问题,你需要:
brew install git-filter-repo
# PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA要替换成大文件的路径,注意是路径,不能只包含文件名哦
# 如果运行不成功,需要增加 --force
git filter-repo --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
# 运行成功后,远程仓库的地址可能会被删除,重新加入一下即可