Git镜像:https://www.gitclone.com/、https://ghproxy.com/
Git入门 查看Git命令的帮助信息,git <command> –help
1.Git 工作区、暂存区和版本库(以本地举例)、远程仓库 stageindex
图中左侧为工作区,右侧为版本库。在版本库中标记为 “index” 的区域是暂存区(stage/index),标记为 “master” 的是 master 分支所代表的目录树。 图中我们可以看出此时 “HEAD” 实际是指向 master 分支的一个“游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。 图中的 objects 标识的区域为 Git 的对象库,实际位于 “.git/objects” 目录下,里面包含了创建的各种对象及内容。 当对工作区修改(或新增)的文件执行 git add 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。 当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。 当执行 git reset HEAD 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。 当执行 git rm –cached 命令时,会直接从暂存区删除文件,工作区则不做出改变。 当执行 git checkout . 或者 git checkout – 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区中的改动。 当执行 git checkout HEAD . 或者 git checkout HEAD 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
2.Git文件状态 在Git中文件大概分为四种状态:已修改(modified)、已暂存(staged)、已提交(committed)、未追踪(Untrack)
更新要提交的内容 拓展:status提示信息 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)
既然是Changes not staged for commit,就说明出现这个提示下的所有文件改动,都是存在于工作区的。stage是暂存区的意思,not stage说明都不在暂存区,那么说明在工作区。 (use “git add …” to update what will be committed)。执行这条命令就可以工作区里面的改变加入到暂存区。可以执行git add .把当前目录下所有改动加入暂存区。 (use “git checkout – …” to discard changes in working directory)。执行这条命令将丢弃在工作区的改动。可以执行git checkout *把当前目录下所有工作区的改动丢弃掉
Untracked files:
(use "git add <file>..." to include in what will be committed)
Untracked files,就说明出现这个提示下的所有文件都是当前HEAD没有被加入过的文件。这种改动也属于工作区。 (use “git add …” to include in what will be committed)。把Untracked files加入暂存区。
On branch master Your branch is ahead of ‘origin/master’ by 1 commit. (use “git push” to publish your local commits)
当前分支比远程分支多了一次commit
Your branch and ‘origin/master’ have diverged, and have 1 and 1 different commits each, respectively
pull报错了,查看状态显示这个,先留着待解决吧
3.HEAD是什么 HEAD是Git中非常重要的一个概念,你可以称它为指针或者引用,它可以指向任意一个节点,并且指向的节点始终为当前工作目录,换句话说就是当前工作目录(也就是你所看到的代码)就是HEAD指向的节点。
4.git重命名检测 Git 采用了不同的方法:它没有选择去存储与文件移动操作相关的信息,而是采用了重命名检测算法。在该算法中,如果一个文件在某一次提交中消失了,它依然会存在于其前次提交,而如果某个拥有相同名字或相似内容的文件出现在了另一个位置,Git 就会自动检测到。如果是这种情况,Git 就会假定该文件被移动过了。
Git项目文件说明 Git init后主要有两个重要的文件和目录:.git目录和.gitignore
- .gitignore .gitignore文件存在于根目录(与.git同级的目录)用于在将文件提交到git暂存区时,指定将哪些文件排除;
.gitignore git add .不会添加被.gitignore忽视的文件,而git add -f . 强制添加所有文件,即使是.gitignore忽视的文件也添加。
当.gitignore文件不是你编写的,但是它编写的不符合实际需求,你可以使用git check-ignore命令进行检查,看是哪一个规则有问题了
#检测 git check-ignore -v App.class #结果 .gitignore:3:*.class App.class .gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。解决方法就是先把本地缓存删除(改变成未track状态),然后再提交。
git rm -r –cached . git add . git commit -m ‘update .gitignore’
也可以手动指定一个文件作为git忽略文件
git config core.excludesfile ***
对于全局Git配置,可以使用如下命令对全部仓库进行配置。
git config –global core.excludesfile **/.gitignore(文件相对或绝对位置)
忽略规则如下:
空格不匹配任意文件,可作为分隔符,可用反斜杠转义 #开头的文件标识注释,可以使用反斜杠进行转义 ! 开头的模式标识否定,该文件将会再次被包含,如果排除了该文件的父级目录,则使用 ! 也不会再次被包含。可以使用反斜杠进行转义 / 结束的模式只匹配文件夹以及在该文件夹路径下的内容,但是不匹配该文件 / 开始的模式匹配项目跟目录 如果一个模式不包含斜杠,则它匹配相对于当前 .gitignore 文件路径的内容,如果该模式不在 .gitignore 文件中,则相对于项目根目录 ** 匹配多级目录,可在开始,中间,结束 ? 通用匹配单个字符 [] 通用匹配单个字符列表
- .git目录 任意文件夹中,用 git init 命令初始化仓库,即可在此文件夹下创建 .git 文件夹(.打头为隐藏文件夹,所以平时可能看不到)。这个文件夹之外的部分叫做工作区(Working Directory),.git 文件夹我们称做 Git仓库 (Git Repository)。 通常会有7个文件5个目录,常见目录如下:
COMMIT_EDITMSG HEAD ORIG_HEAD FETCH_HEAD config description index hooks/ info/ logs/ objects/ refs/
- 文件 COMMIT_EDITMSG 此文件是一个临时文件,存储最后一次提交的信息内容,git commit 命令之后打开的编辑器就是在编辑此文件,而你退出编辑器后,git 会把此文件内容写入 commit 记录。
实际应用: git pull 远程仓库后,新增了很多提交,淹没了本地提交记录,直接 cat .git/COMMIT_EDITMSG 就可以弄清楚最后工作的位置了。
- HEAD 此文件永远存储当前位置指针,就像 linux 中的 $PWD 变量和命令提示符的箭头一样,永远指向当前位置,表明当前的工作位置。在 git 中 HEAD 永远指向当前正在工作的那个 commit。(孤立HEAD?????)
HEAD 存储一个分支的 ref,Linux中运行:cat .git/HEAD 通常会显示:
ref: refs/heads/master
这说明你目前正在 master 分支工作。此时你的任何 commit,默认自动附加到 master 分支之上
git cat-file -p HEAD tree 4cbb261560348e1727b5137f3ab6eceae8e1f34d parent 22c457fe24f737505356edfb8696c7e50fd9d971 author Evan You <yyx990803@gmail.com> 1654857613 +0800 committer Evan You <yyx990803@gmail.com> 1654857613 +0800 chore: test pass
- ORIG_HEAD 正因为 HEAD 比较重要,此文件会在你进行危险操作时备份 HEAD,如以下操作时会触发备份
git reset git merge git rebase git pull
此文件应用示例
回滚到上一次的状态(慎用!!!)
git reset –hard ORIG_HEAD
FETCH_HEAD git pull/fetch/merge \( git fetch \) git merge FETCH_HEAD
显示如下>>>
- branch master -> FETCH_HEAD Updating f785638..59db1b2
此时会默默备份 HEAD 到 ORIG_HEAD
- config 此文件存储项目本地的 git 设置,典型内容如下:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
[remote “origin”]
url = git@gitlab.xxxx.com/xxx.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch “master”]
remote = origin
merge = refs/heads/master
[branch “v2.6.0”]
remote = origin
merge = refs/heads/v2.6.0
[branch “v2.8.0”]
remote = origin
merge = refs/heads/v2.8.0
[core] 执行以下命令:
git config user.name abc git config user.email abc@abc.com
会在 config 文件中追加以下内容:
… … [user] name = abc email = abc@abc.com
git config –global 影响的则是全局配置文件 ~/.gitconfig。
[remote] 段表示远程仓库配置
[branch] 段表示分支同步设置
假设当前在 master 分支,执行 git pull 若出现以下提示:
There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details.
git pull 就说明 .git/config 文件缺少对应的 [branch “master”] 字段。
解决方案为:
git branch -u origin/master master
或者执行一次 push
git push -u origin master
会出现提示:
Branch ‘master’ set up to track remote branch ‘master’ from ‘origin’.
其实就是生成以下内容在 .git/config中:
[branch “master”] remote = origin merge = refs/heads/master
手动编辑 .git/config,效果一样。这就是 upstream 的真正含义,即生成 config 中的这段配置。
- description 说明这个文件主要用于 GitWeb 的描述,如果要启动 GitWeb 可用如下命令:
确保lighttpd已安装: brew install lighttpd
$ git instaweb –start
默认会启动 lighttpd 服务并打开浏览器 http://127.0.0.1:1234 (试着改成对外IP并分享给别人?)
以下显示当前的 git 仓库名称以及描述,默认的描述如下:
description GitWeb
- hooks/目录 存放 git hooks,用于在 git 命令前后做检查或做些自定义动作。运行 ls -F1 .git/hooks
prepare-commit-msg.sample # git commit 之前,编辑器启动之前触发,传入 COMMIT_FILE,COMMIT_SOURCE,SHA1 commit-msg.sample # git commit 之前,编辑器退出后触发,传入 COMMIT_EDITMSG 文件名 pre-commit.sample # git commit 之前,commit-msg 通过后触发,譬如校验文件名是否含中文 pre-push.sample # git push 之前触发 pre-receive.sample # git push 之后,服务端更新 ref 前触发 update.sample # git push 之后,服务端更新每一个 ref 时触发,用于针对每个 ref 作校验等 post-update.sample # git push 之后,服务端更新 ref 后触发 pre-rebase.sample # git rebase 之前触发,传入 rebase 分支作参数 applypatch-msg.sample # 用于 git am 命令提交信息校验 pre-applypatch.sample # 用于 git am 命令执行前动作 fsmonitor-watchman.sample # 配合 core.fsmonitor 设置来更好监测文件变化
如果要启用某个 hook,只需把 .sample 删除即可,然后编辑其内容来实现相应的逻辑。
比如要校验每个 commit message 至少要包含两个单词,否则就提示并拒绝提交,将 commit-msg.sample 改为 commit-msg 后,编辑如下:
#!/bin/sh grep -q ‘Ss+S’ $1 || { echo ‘提交信息至少为两个单词’ && exit 1; }
这样当提交一个 commit 时,会执行 bash 命令: .git/hooks/commit-msg .git/COMMIT_EDITMSG,退出值不为 0,就拒绝提交。
- info/目录 此文件夹基本就有两个文件:
94e1a0d952f577fe1348d828d145507d3709e11e refs/heads/master
object hash # branch reference
- logs/目录 记录了操作信息,git reflog 命令以及像 HEAD@{1} 形式的路径会用到。如果删除此文件夹(危险!),那么依赖于 reflog 的命令就会报错。
文件夹 objects/ 此文件夹简单说,就是 git的数据库,运行 tree .git/objects,可以看到目录结构:
.git/objects/
|– 0c
| -- d370696b581c38ee01e62b148a759f80facc2d
|-- 59
|
– 3d5b490556791212acd5a516a37bbfa05d44dd
|– 61
| -- be44eedde61d723e5761577a2b420ba0fc2794
|-- 64
|
– c0aed8ddcbb546bdcec2848938fc82348db227
|– d4
| -- 9904676ce8ddde276bdbfa9bbec313e90e0f50
|-- info
– pack
|-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx
`-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack
这些文件分两种形式:pack压缩包 形式放在 pack/ 目录下,除此之外都是 hash文件 形式,被叫做 loost objects。
这个文件夹以及相应的算法,我没找到独立的名称,就叫它 hash-object 体系吧。因为确实有个 git hash-object 命令存在,是一个底层的负责生成这些 loost objects 文件,如果要看到这些文件各自的含义,执行以下命令:
git cat-file –batch-check –batch-all-objects
可以看到
04c87c65f142f33945f2f5951cf7801a32dfa240 commit 194 098217953a6ca169bed33d2be8a07d584fcdaf30 tree 31 0cd370696b581c38ee01e62b148a759f80facc2d commit 245 2a810017bfc85d7db2627f4aabdaa1583212bda3 blob 19 3920a07c1d5694df6b8658592b0939241d70e9e5 tree 93 593d5b490556791212acd5a516a37bbfa05d44dd tag 148 61be44eedde61d723e5761577a2b420ba0fc2794 tree 154 … …
但你会发现这个列表里有些值在文件夹中并不存在,因为除了 loost objects 它还汇总了 pack 文件中的内容。
hash文件 loose object 按文件内容可分为四种类型:commit, tree, blob, tag,若执行以下命令会生成所有四种类型:
echo -en ‘xxn’ > xx # 共 3 个字符 git add . git commit -m ‘update xx’ git tag -a ‘v1.0’ -m ‘release: 1.0.0’
经过以上操作后,对比一下文件树,发现多了四个 hash文件:
|– 0c
| -- d370696b581c38ee01e62b148a759f80facc2d
|-- 18
|
– 143661f96845f11e0b4ab7312bdc0f356834ce
|– 30
| -- 20feea86d222d83218eb3eb5aa9f58f73df04d
|-- 59
|
– 3d5b490556791212acd5a516a37bbfa05d44dd
|– 61
| -- be44eedde61d723e5761577a2b420ba0fc2794
|-- 64
|
– c0aed8ddcbb546bdcec2848938fc82348db227
|– ad
| -- f4c9afac7afae3ff3e95e6c4eefe009d547f00
|-- cc
|
– c9bd67dc5c467859102d53d54c5ce851273bdd
|– d4
| -- 9904676ce8ddde276bdbfa9bbec313e90e0f50
|-- info
– pack
|– pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx
`– pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack
这四个 hash文件 分别是:
cc/c9bd67dc5c467859102d53d54c5ce851273bdd # blob 30/20feea86d222d83218eb3eb5aa9f58f73df04d # commit ad/f4c9afac7afae3ff3e95e6c4eefe009d547f00 # tree 18/143661f96845f11e0b4ab7312bdc0f356834ce # tag
其实这些文件都经过了压缩,压缩形式为 zlib。先安装一下解压工具 macOS 版 brew install pigz 或 windows 版 pigz,后执行:
$ pigz -d < .git/objects/cc/c9bd67dc5c467859102d53d54c5ce851273bdd
BLOB类型,显示结果为>>>>(注意xx后有个n)
blob 3xx
$pigz -d < .git/objects/30/20feea86d222d83218eb3eb5aa9f58f73df04d
COMMIT类型,显示结果为>>>>
commit 248tree adf4c9afac7afae3ff3e95e6c4eefe009d547f00 parent 0cd370696b581c38ee01e62b148a759f80facc2d author jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562044880 +0800 committer jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562044880 +0800 update xx $ pigz -d < .git/objects/ad/f4c9afac7afae3ff3e95e6c4eefe009d547f00
TREE类型,显示结果为>>>>
tree 154100644 abc*???]}?bJ?ڡX2??100644 asdf???CK?)?wZ???S?100644 iou???CK?)?wZ???S?100644 xx?ɽg?FxY-S?L?Q‘;?100644 yy???CK?)?wZ???S?
$ pigz -d < .git/objects/18/143661f96845f11e0b4ab7312bdc0f356834ce
TAG类型,显示结果为>>>>
tag 155object 3020feea86d222d83218eb3eb5aa9f58f73df04d type commit tag v1.0 tagger jamesyang.yjm <jamesyang.yjm@alibaba-inc.com> 1562045942 +0800 release: 1.0.0
会发现,显示结果都是 type size+内容 形式,这就是 object 文件的存储格式:
[type] [size][NULL][content]
type type git cat-file -t ccc9bd67dc5c467859102d53d54c5ce851273bdd
显示结果为>>>>
blob git cat-file -p ccc9bd67dc5c467859102d53d54c5ce851273bdd
显示结果为>>>>
xx
所以 blob 文件就是对原文件内容的全量拷贝,同时前面加了 blob size�,而文件名称的 hash 值计算是计算整体字符的 SHA-1 值:
echo -en ’blob 3�xxn‘ | shasum
显示结果为>>>>
ccc9bd67dc5c467859102d53d54c5ce851273bdd -
知道原理后,其它类型格式请自行参考 斯坦福 Ben Lynn 所著的 GitMagic。
git show 3020feea86d222d83218eb3eb5aa9f58f73df04d 3020feea86d222d83218eb3eb5aa9f58f73df04d 这个 committree object: adf4c9afac7afae3ff3e95e6c4eefe009d547f00 这就是 objects/ 文件夹作为 git数据库 被使用的真实例子。
pack文件 为什么会有 .pack 文件?
由于每次 commit 都会生成许多 hash文件,并且由于 blob 文件都是全量存储的,导致 git 效率下降,于是有了 pack-format,优势:
对于大仓库存储效率高 利于网络传输,便于备份 增量存储,优化磁盘空间 将 .git/objects 下的部分文件打包成 pack格式
\( tree .git/objects/ | wc -l 311 \) git gc Enumerating objects: 288, done. Counting objects: 100% (288⁄288), done. Delta compression using up to 4 threads Compressing objects: 100% (287⁄287), done. Writing objects: 100% (288⁄288), done. Total 288 (delta 131), reused 90 (delta 0) $ tree .git/objects/ | wc -l 12
可以看到文件数量减小了不少,其中大部分文件被打到一个 .pack 包中,并且是增量存储,有部分变更的文件只存储 基础hash + 变更内容,磁盘空间优化很明显。
git gc 其实运行了两条命令:git repack 用来打包 和 git prune-packed 用来移除已打包的 hash文件;
11.文件夹refs refs 可以理解成文件系统中的 symbol link,看下结构:
\( tree .git/refs/ .git/refs |-- heads | `-- master `-- tags `-- v1.0 \) cat .git/refs/heads/master 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 \( cat .git/refs/tags/v1.0 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 \) git cat-file -t 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 commit
可以看到 master 和 v1.0 都指向 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 这个 commit。
git branch git show-ref –heads git show-ref –tags 如下:
\( git branch abc
\) tree .git/refs/
.git/refs/
|– heads
| |– abc
| -- master
– tags
`– v1.0
$ cat .git/refs/heads/abc
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
说明新建分支其实就是生成了一个指向某个 commit 的 symbol link,当然在这里叫做 ref。
git tag git branch git tag -a xx 使用以下命令来拿到 refs 文件夹存储的信息:
$ git show-ref –head –dereference 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.0 5e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}
我们来看这些信息如何变化的:
\( touch new_file && git add . && git commit -m 'add new_file' [master 44b0d05] add new_file 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 new_file \) git show-ref –head –dereference 44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 HEAD 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc 44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 refs/heads/master 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.0 5e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}
diff 一下可以看到:
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master
这两行发生了变化。也就是每次 commit 时,HEAD 与 heads 都会自动更新。
- index文件 文件保存成二进制对象以后,还需要通知 Git 哪些文件发生了变动。所有变动的文件,Git 都记录在一个区域,叫做“暂存区”(英文叫做 index 或者 stage)。等到变动告一段落,再统一把暂存区里面的文件写入正式的版本历史。
git update-index命令用于在暂存区记录一个发生变动的文件。
$ git update-index –add –cacheinfo 100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad test.txt
上面命令向暂存区写入文件名test.txt、二进制对象名(哈希值)和文件权限。
git ls-files命令可以显示暂存区当前的内容。
$ git ls-files –stage 100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0 test.txt
上面代码表示,暂存区现在只有一个文件test.txt,以及它的二进制对象名和权限。知道了二进制对象名,就可以在.git/objects子目录里面读出这个文件的内容。
git status命令会产生更可读的结果。
$ git status 要提交的变更:
新文件: test.txt
上面代码表示,暂存区里面只有一个新文件test.txt,等待写入历史。
Git远程仓库 Git 并不像 SVN 那样有个中心服务器。目前我们使用到的 Git 命令都是在本地执行,如果你想通过 Git 分享你的代码或者与其他开发人员合作。 你就需要将数据放到一台其他开发人员能够连接的服务器上。
1.添加远程仓库 git remote add [shortname] [url] #添加远程仓库 git remote rm name # 删除远程仓库 git remote rename old_name new_name # 修改仓库名 2.查看远端仓库 \( git remote origin \) git remote -v origin git@github.com:tianqixin/runoob-git-test.git (fetch) origin git@github.com:tianqixin/runoob-git-test.git (push)
3.获取远端仓库代码 git fetch
只能fetch到一个空白的分支,然后可以手动merge
$ git fetch <远程主机名> <远程分支名>:<本地分支名> 不填的话都是默认
4.拉取 git pull git pull <远程主机名> <远程分支名>:<本地分支名>
允许合并不相关的分支
$ git pull –allow-unrelated-histories git pull操作其实是git fetch 与 git merge 两个命令的集合。 git fetch 和 git merge FETCH_HEAD 的简写。
5.推送 git push
基本
$ git push <远程主机名> <本地分支名>:<远程分支名>
强制推送
$ git push –force origin master
删除远程分支
$ git push origin –delete master
允许合并不相关的分支
$ git push –allow-unrelated-histories
将整个仓库镜像推送到远程仓库
$ git push –mirror 如果本地分支名与远程分支名相同,则可以省略冒号,带上-u 参数相当于记录了push到远端分支的默认值,这样当下次我们还想要继续push的这个远端分支的时候推送命令就可以简写成git push即可。
Git 分支 1.创建分支命令 git branch
创建分支
$ git branch <branch>
创建分支并跟踪远程分支
$ git branch -u o/master foo 2.切换分支命令 git checkout 第一作用是切换分支,第二个是撤销修改。
切换指定分支
$ git checkout <branch>|<hash>|<tag>
创建一个的分支,它跟踪远程分支
$ git checkout -b 本地分支名x origin/远程分支名x
从暂存区恢复到工作区
$ git checkout . 实际测试:
假设分支1 a文件未提交,分支2 a文件已提交。切换到到分支2时会替换 分支1的a文件。1切换到2时也会替换a文件;
两个分支都已经提交的 切换时会互相替换。一个提交一个没提交时,从a到b,b会保持a的暂存区和工作区
3.合并分支命令 git merge
合并指定分支到当前分支
$ git merge <branch>
4.删除分支 git branch -d
删除指定分支
$ git branch -d <branch>
5.分支列表 git branch
列出所有分支
$ git branch
查看远程所有分支
$ git branch -r
查看本地和远程所有分支
$ git branch -a
列出分支时,带*号的分支为当前活动分支
5.重命名分支 git branch -M
重命名指定分支
不填old默认重命名当前分支
$ git branch -m old new
强制重命名指定分支
$ git branch -M old new git rebase 变基 1.介绍 Git rebase,通常被称作变基或衍合, 可以理解为另外一种合并的方式,与merge 会保留分支结构和原始提交记录不同,rebase 是在公共祖先的基础上,把新的提交链截取下来,在目标分支上进行重放,逐个应用选中的提交来完成合并。
不同公司,不同情况有不同使用场景,不过大部分情况推荐如下:
自己单机的时候,拉公共分支最新代码的时候使用rebase,也就是git pull -r或git pull –rebase。这样的好处很明显,提交记录会比较简洁。但有个缺点就是rebase以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了嘛,所以看个人需求了。
往公共分支上合代码的时候,使用merge。如果使用rebase,那么其他开发人员想看主分支的历史,就不是原来的历史了,历史已经被你篡改了。举个例子解释下,比如张三和李四从共同的节点拉出来开发,张三先开发完提交了两次然后merge上去了,李四后来开发完如果rebase上去(注意李四需要切换到自己本地的主分支,假设先pull了张三的最新改动下来,然后执行,然后再git push到远端),则李四的新提交变成了张三的新提交的新基底,本来李四的提交是最新的,结果最新的提交显示反而是张三的,就乱套了。
正因如此,大部分公司其实会禁用rebase,不管是拉代码还是push代码统一都使用merge,虽然会多出无意义的一条提交记录“Merge … to …”,但至少能清楚地知道主线上谁合了的代码以及他们合代码的时间先后顺序
2.原理 变基操作的工作原理很简单:Git 会让我们想要移动的提交序列在目标分支上按照相同的顺序重新再现一遍。这就相当于我们为各个原提交做了个副本,它们拥有相同的修改集、同一作者、日期以及注释信息。
3.命令
可以是commit 版本号、分支名称,合并多个提交到一个版本
$ git rebase -i [startpoint] [endpoint]
变基发生冲突时,解决后继续变基
$ git rebase –continue
无视冲突,继续变基操作
$ git rebase –skip
发生冲突时中断变基
$ git rebase –abort“ -i的意思是–interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit(注:该区间指定的是一个前开后闭的区间)。
Git 标签 Git 中的tag指向一次commit的id,通常用来给开发分支做一个标记,如标记一个版本号。
1.添加标签 git tag -a version -m ”note“
注解:git tag 是打标签的命令,-a 是添加标签,其后要跟新标签号,-m 及后面的字符串是对该标签的注释。
2.提交标签到远程仓库 git push origin -tags
注解:就像git push origin master 把本地修改提交到远程仓库一样,-tags可以把本地的打的标签全部提交到远程仓库。
3.删除标签 git tag -d version
注解:-d 表示删除,后面跟要删除的tag名字
4.删除远程标签 git push origin :refs/tags/version
git push origin :branch_1 5.查看标签 git tag或者git tag -l
Git补丁 1.git fromat-patch git format-patch 命令用于将 Git 仓库中的提交记录转换为补丁文件。补丁文件可以用于将更改应用到其他 Git 仓库或版本控制系统中。
生成单个补丁文件
git format-patch <commit>
<start> 和 <end> 是要生成补丁文件的提交范围
git format-patch <start>..<end>
-n 选项可以指定要生成的补丁文件数量。
git format-patch -n <number> <start>..<end>
–stdout 选项可以将补丁文件输出到标准输出中,包括提交信息和更改内容。
git format-patch –stdout <commit>
–no-signature 选项可以生成不包含提交信息的补丁文件,只包含更改内容
git format-patch –no-signature <commit>
2.git apply
应用单个补丁文件,将补丁文件中的更改应用到工作目录中。
git apply <patch-file>
应用多个补丁文件
git apply <patch-file-1> <patch-file-2> …
–ignore-space-change 选项可以忽略补丁文件中的空格更改。
git apply –ignore-space-change <patch-file>
应用补丁文件并忽略空格和换行符:
git apply –ignore-whitespace <patch-file>–ignore-whitespace
–index 选项可以将更改添加到暂存区中,而不是仅仅应用更改到工作目录中。
git apply –index <patch-file>
3.git am
git am 和 git apply 命令都用于将补丁文件应用到 Git 仓库中,但它们之间有一些区别。 git am 命令可以自动创建一个新的提交,而 git apply 命令只会将更改应用到工作目录中,需要手动创建一个新的提交。 git am 命令可以应用交互式补丁,以便手动选择要应用的更改,而 git apply 命令只能应用非交互式补丁。 git am 命令可以应用多个补丁文件,并自动创建多个新的提交,而 git apply 命令只能应用一个补丁文件。 git am 命令通常用于应用由 git format-patch 命令生成的补丁文件,而 git apply 命令可以应用由其他方式生成的补丁文件。
Git存储 git stash将本地未提交代码作为一个本地缓存。作用范围为本地工作区以及本地暂存区。
1.使用场景
当正在dev分支上开发某个项目,这时项目中出现一个bug,需要紧急修复,但是正在开发的内容只是完成一半,还不想提交,这时可以用git stash命令将修改的内容保存至堆栈区,然后顺利切换到hotfix分支进行bug修复,修复完成后,再次切回到dev分支,从堆栈中恢复刚刚保存的内容。 由于疏忽,本应该在dev分支开发的内容,却在master上进行了开发,需要重新切回到dev分支上进行开发,可以用git stash将内容保存至堆栈中,切回到dev分支后,再次恢复内容即可。
2.git stash
保存当前工作区、暂存区的所有未提交代码
执行存储时,添加备注,方便查找
git stash save “save message”
只有git stash 也是可以的,但查找时不方便识别。
3.查看stash列表
查看stash了哪些存储
$ git stash list
显示缓存的详细信息,n为第几个,默认数第一个,也就是0
$ git stash show stash@{0} 4.恢复缓存 git stash pop
命令恢复之前缓存的工作目录,将缓存堆栈中的对应stash删除,并将对应修改应用到当前的工作目录下,默认为第一个stash,即stash@{0},如果要应用并删除其他stash,命令:
git stash pop stash@{$num}
比如应用并删除第二个:
git stash pop stash@{1}
5.git stash apply 使用apply命令恢复,stash列表中的信息是会继续保留的,而使用pop命令进行恢复,会将stash列表中的信息进行删除。参数同pop
6.删除缓存 git stash drop stash@{num}
删除某个保存,num是可选项,通过git stash list可查看具体值
7.删除所有缓存
删除所有缓存的stash
$ git stash clear
8.导出到文件 使用 git stash list 命令查看当前的 stash 栈,并确定要导出的 stash 的索引号。
使用 git stash show -p > stash.diff 命令将 stash 中的修改导出到一个文件中。其中, 是要导出的 stash 的索引号,-p 参数表示显示修改的差异,stash.diff 是导出的文件名。
导出stash到文件
git stash show 1 -p > stash.diff
Git常用命令,基于使用步骤 提示:正常步骤应该是:先commit 然后pull 再 push ;
1.git config,配置Git 用于查看和修改Git配置信息,当安装Git后首先要做的事情是设置用户名称和email地址。这是非常重要的,因为每次Git提交都会使用该用户信息
#设置用户信息 git config –global user.name “未进化的程序猿” git config –global user.email “486566947@qq.com” #读取配置信息 git config –list git config user.name
/etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 –system 选项,读写的就是这个文件。 ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 –global 选项,读写的就是这个文件。 当前项目的 Git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。
2.初始化仓库,git init Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。
在执行完成 git init 命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变。
使用当前目录作为 Git 仓库,我们只需使它初始化。
git init
该命令执行完后会在当前目录生成一个 .git 目录。
使用我们指定目录作为Git仓库。
git init newrepo
初始化后,会在 newrepo 目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。
3.将文件纳入版本控制 ,git add 如果当前目录下有几个文件想要纳入版本控制,需要先用 git add 命令告诉 Git 开始对这些文件进行跟踪,然后提交:
当前目录下.c结尾的所有文件(通配符)
$ git add *.c
指定README等多个文件文件
$ git add README README.md
.git add all无论在哪个目录执行都会提交相应文件。
$ git add –all
.git add .只能够提交当前目录或者它后代目录下相应文件。
$ git add .
- 提交文件 ,git commit 暂存区保留本次变动的文件信息,等到修改了差不多了,就要把这些信息写入历史,这就相当于生成了当前项目的一个快照(snapshot)。
项目的历史就是由不同时点的快照构成。Git 可以将项目恢复到任意一个快照。快照在 Git 里面有一个专门名词,叫做 commit,生成快照又称为完成一次提交。
git commit -m ’初始化项目版本,记录提交信息‘ git commit -am ”新增提交说明“ 设置了用户名和 Email,保存快照的时候,会记录是谁提交的。
- 克隆仓库,git clone 我们使用 git clone 从现有 Git 仓库中拷贝项目,Git仓库一般是一个远程连接
git clone <repo> <directory>
repo:Git 仓库。 directory:本地目录。
- 当前提交状态,git status git status 命令用于查看在你上次提交之后是否有对文件进行再次修改。
$ git status On branch master Initial commit Changes to be committed: (use ”git rm –cached <file>…“ to unstage) new file: README
new file: hello.php
-s $ git status -s AM README A hello.php
AM 状态的意思是这个文件在我们将它添加到缓存之后又有改动。
- git diff,比较差异 git diff 命令比较文件的不同,即比较文件在暂存区和工作区的差异。(-c 上下文格式的diff、-u 合并格式diff)
git diff 命令显示已写入暂存区和已经被修改但尚未写入暂存区文件的区别。
git diff 有两个主要的应用场景。
尚未缓存的改动:git diff 查看已缓存的改动: git diff –cached 查看已缓存的与未缓存的所有改动:git diff HEAD 显示摘要而非整个 diff:git diff –stat
显示暂存区和工作区的差异:
$ git diff [file]
显示两次提交之间的差异:
\( git diff --cached [file] 或 \) git diff –staged [file]
显示暂存区和上一次提交(commit)的差异:
$ git diff [first-branch]…[second-branch]
分支比较
直接将两个分支上最新的提交做diff
\( git diff topic master [file] \) #或 $ git diff topic..master [file]
输出自topic和master分别开发以来,master分支上的变更。
$ git diff topic…master
查看简单的diff结果,可以加上–stat参数
–name-only输出时只显示文件名
- 回退版本,git reset git reset 命令用于回退版本,可以指定退回某一次提交的版本。只对本地分支有效,对远程分支无效
git reset [–soft | –mixed | –hard] [HEAD]
–mixed 为默认,可以不用带该参数,用于重置暂存区的文件与上一次的提交(commit)保持一致,工作区文件内容保持不变。
git reset [HEAD]
实例:
\( git reset HEAD^ # 回退所有内容到上一个版本 \) git reset HEAD^ hello.php # 回退 hello.php 文件的版本到上一个版本 $ git reset 052e # 回退到指定版本
–soft $ git reset –soft HEAD~3 # 回退上上上一个版本
–hard \( git reset –hard HEAD~3 # 回退上上上一个版本 \) git reset –hard bae128 # 回退到某个版本回退点之前的所有信息。 $ git reset –hard origin/master # 将本地的状态回退到和远程的一样
HEAD 表示当前版本 HEAD^ 上一个版本 HEAD^^ 上上一个版本 HEAD^^^ 上上上一个版本 HEAD~0 表示当前版本 HEAD~1 上一个版本 HEAD^2 上上一个版本 HEAD^3 上上上一个版本
9.删除文件,git rm 将文件从暂存区和工作区中删除:
git rm <file> 如果想把文件从暂存区域移除,但仍然希望保留在当前工作目录中,换句话说,仅是从跟踪清单中删除,使用 –cached 选项即可:
git rm –cached <file>
可以递归删除,即如果后面跟的是一个目录做为参数,则会递归删除整个目录中的所有子目录和文件:
git rm –r *
- 移动目录、文件,git mv git mv 命令用于移动或重命名一个文件、目录或软连接。
git mv [file] [newfile]
如果新文件名已经存在,但还是要重命名它,可以使用 -f 参数:
git mv -f [file] [newfile]
11.查看提交历史git log、git blame
所有历史记录
$ git log
简洁版
$ git log –oneline
拓扑图版
$ git log –graph
反向显示
$ git log –reverse
指定提交者
$ git log –author=lius
时间范围
$ git log –before={3.weeks.ago} –after={2010-04-18}
反向显示
$ git log –reverse
指定文件
git blame <file>
以上多种参数可以组合使用
git show
查看分支最后一次提交或版本在代码层面的改动
$ git show <bracnch>|<commit>
git reflog reflog是reference log的缩写,含义是引用日志,它会记录下HEAD节点和分支引用所指向的历史。可以使用git reflog命令来查看引用日志
git gc gc 命令(gc 指的是垃圾回收)可用于清理版本库,移除所有不属于当前分支的提交对象。
git ls-files 查看一下git跟踪了哪些文件,此命令就可以列出所有git正在跟踪的文件
撤销文件 git restore
将在 工作区 但是 不在暂存区 的文件撤销更改
$ git restore <file>
作用是将 暂存区的文件从暂存区撤出,但不会更改文件
$ git restore –staged
问题记录 1.push和commit的区别 git commit操作的是本地库,git push操作的是远程库。
git commit是将本地修改过的文件提交到本地库中。 git push是将本地库中的最新信息发送给远程库。
如果本地不commit的话,修改的纪录可能会丢失,而有些修改当前是不需要同步至服务器的,所以什么时候同步过去由用户自己选择。什么时候需要同步再push到服务器
- pull requests 和 merge requests github可以对不同的用户赋予不同的分支权限,例如Gitlab中的:
Guest:可以创建issue、发表评论,不能读写版本库 Reporter:可以克隆代码,不能提交,QA、PM可以赋予这个权限 Developer:可以克隆代码、开发、提交、push,RD可以赋予这个权限 Master:可以创建项目、添加tag、保护分支、添加项目成员、编辑项目,核心RD负责人可以赋予这个权限 Owner:可以设置项目访问权限 - Visibility Level、删除项目、迁移项目、管理组成员,开发组leader可以赋予这个权限
github基于fork的模式下,PR用于请求分支管理员 合并自己提交的代码(理解为请求拉取自己的代码),merge requests同理;
- fetch和pull的区别 git在本地会保存两个版本的仓库,分为本地仓库和远程仓库。
fetch 只能更新远程仓库的代码为最新的,本地仓库的代码还未被更新,我们需要通过 git merge origin/master 来合并这两个版本,你可以把它理解为合并分支一样的。
pull 操作是将本地仓库和远程仓库(本地的)更新到远程的最新版本。fetch+merge,自动进行合并
4.checkout和reset时的变化 签出切换分支、版本、标签时文件的变化
当执行 git reset HEAD 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。加上–hard时会强制替换工作区、暂存区的内容; git restore会清除暂存区的修改内容,例如修改了test.vue ,会变为未修改时的内容; 当执行 git rm –cached 命令时,会直接从暂存区删除文件,工作区则不做出改变。
- 保存远程时输入的账号密码 在 git bash 里输入命令:
git config –global credential.helper store
然后执行 git 操作,输入一遍密码后就会记录密码,以后就不用输入了。
要更改记录的用户名和密码,只需要更改用户目录下的 .git-credentials 文件即可。
6.移除文件的版本控制
还没有git add,在 .gitignore中添加 已经git add,先 git rm -r –cached 文件,然后在 .gitignore中添加 已经加到版本控制中,先 git rm -r –cached 文件,然后在 .gitignore中添加,最后 gti commit -m ’提交.gitignore‘
7.Git问题解决记录
git config –global credential.helper store,解决SSl连接错误
8.Github常规目录说明:
dist 是指编译后的文件,可以理解为压缩发布版 src 源码文件,适合动手能力强的童鞋 docs 文档 examples 示例文件 test 测试脚本 .gitignore 告诉git哪些文件不要上传到 GitHub,比如数据库配置文件等 LICENSE.txt 授权协议 README.md 自述文件,整个项目的简介、使用方法等 bower.json Bower 包管理器配置文件 package.json npm 包管理器配置文件
git做不到一个文件一个分支有一个分支没有 例如一个数据库配置文件,本地和线索不一样,把它从暂存区拉出来,取消追踪变成deleted状态,本地文件实际还存在,同步到分支,远程分支当前版本已经没有这个文件了。
Git拉取指定分支 git clone 仓库地址 //默认master分支 git clone -b 分支名 仓库地址 //指定分支
git 常见的输出内容
已经提交的改变
$ Changes to be committed:
暂未提交的改变
$ Changes not staged for commit:
常见状态
deleted、modified
12.报错non-fast-forward non-fast-forward
Dealing with “non-fast-forward” errors:(From time to time you may encounter this error while pushing) To prevent you from losing history, non-fast-forward updates were rejected. Merge the remote changes before pushing again. See the ’non-fast forward‘ section of ’git push –help‘ for details. This error can be a bit overwhelming at first, do not fear. Simply put, git cannot make the change on the remote without losing commits, so it refuses the push. Usually this is caused by another user pushing to the same branch. You can remedy this by fetching and merging the remote branch, or using pull to perform both at once. In other cases this error is a result of destructive changes made locally by using commands like git commit –amend or git rebase. While you can override the remote by adding –force to the push command, you should only do so if you are absolutely certain this is what you want to do. Force-pushes can cause issues for other users that have fetched the remote branch, and is considered bad practice. When in doubt, don’t force-push.
以上时较为官方的解释,简单说就是push之前需要先同步远程版本。pull会自动合并,所以要改为fetch手动合并;
问题分析 可以这样理解这个问题就是:别人上传到远程仓库后,你没有及时的同步(、拉取)到本地,但是你同时又添加了一些内容(提交),以致于你在提交时,它会检测到你之前从远程仓库拉取的时候的仓库状态和现在的不一样。于是,它为了安全起见拒绝了你的提交(然后就报了这个错误)。
“不能快速前进”的原因是因为路不一样了,变得不好走了;体现在git里面就是提交历史出现分叉,主线不再是一条直线,而是在前端出现了分叉,git不知道该如何前进,所以报错了,让你来觉得走哪条路!说的简单点,就是远程仓库和本地仓库不同步了
解决问题 1.先合并之前的历史,再进行提交——提倡使用
先把git的东西fetch到你本地然后merge后再push,(如果有冲突就要解决冲突后再合并,冲突问题比较复杂,这里就不详细说了),这样就可以使远程仓库和你本地仓库一致了,然后就可以提交修改了。
\( git fetch origin master \) git merge origin FETCH_HEAD
提示:这2句命令等价于,但是使用git fetch + git merge 更加安全。
$ git pull origin master
然后执行,重新定基,可以使得历史更加统一,即提交历史趋向于一条直线。
$ git pull –rebase origin master
2.丢弃之前的历史,强推(谨慎使用)
利用强覆盖方式用你本地的代码替代git仓库内的内容,远程仓库的东西会被本地仓库覆盖!!!
\( git push -f 或者 \) git push –force
This flag disables these checks, and can cause the remote repository to lose commits; use it with care. 不仅在此处,在平时使用时,也要非常注意,除非你真的是想覆盖远程仓库,不然最好不要强制执行。
13.彻底清理历史版本 先创建一个分支,添加所有文件,删除其他所有分支。
14.回退远程仓库的版本 先把远程仓库指定分支拉下来,手动回退,然后再强制推送上去(拉回来的远程版本库同时带了这个分支的所有历史版本);
Gitee和Github 1.查看fork的子仓
github点击 Insights -> fork gitee点击fork边上的数字
2.保护分支
github默认只有分支创建者和仓库管理员有push的权限,其他人可以提交PR gitee需要自己设置分支为保护分支,并在保护分支的设置内设置相应的保护级别和权限。 github可以邀请别人成为协作者,个人仓库的协作者可以拉取(读取)仓库的内容并向仓库推送(写入)更改。
Markdown语法 斜体文本 斜体文本 粗体文本 粗体文本 粗斜体文本 粗斜体文本
- 第一项
- 第二项
- 第三项
- 第一项
- 第二项
- 第三项
- 第一项
- 第二项
- 第三项
- 第一项
- 第二项
- 第三项 > 最外层 > > 第一层嵌套 > > > 第二层嵌套 这个链接用 1 作为网址变量 [Google][1] 这个链接用 runoob 作为网址变量 [Runoob][runoob] 然后在文档的结尾为变量赋值(网址) 文字 [1]: http://www.google.com/ [runoob]: http://www.runoob.com/ | 左对齐 | 右对齐 | 居中对齐 | | :—–| —-: | :—-: | | 单元格 | 单元格 | 单元格 | | 单元格 | 单元格 | 单元格 |