CVS作为最早的开源而且免费的集中式版本控制系统,直到现在还有不少人在用。由于CVS自身设计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。同样是开源而且免费的SVN修正了CVS的一些稳定性问题,是目前用得最多的集中式版本库控制系统。
除了免费的外,还有收费的集中式版本控制系统,比如IBM的ClearCase(以前是Rational公司的,被IBM收购了),特点是安装比Windows还大,运行比蜗牛还慢,能用ClearCase的一般是世界500强,他们有个共同的特点是财大气粗,或者人傻钱多。
微软自己也有一个集中式版本控制系统叫VSS,集成在Visual Studio中。由于其反人类的设计,连微软自己都不好意思用了。
分布式版本控制系统除了Git以及促使Git诞生的BitKeeper外,还有类似Git的Mercurial和Bazaar等。这些分布式版本控制系统各有特点,但最快、最简单也最流行的依然是Git
// 在GitHub的官网名后加 .cnpmjs.org这个镜像后缀,例如git clone https://github.com.cnpmjs.org/user/bose.git
一、设置git的user name和email
如果你是第一次使用,或者还没有配置过的话需要操作一下命令,自行替换相应字段。
git config --global user.name "xxx"
git config --global user.email "[email protected]"
说明:git config --list 查看当前Git环境所有配置,还可以配置一些命令别名之类的。
二、检查是否存在SSH Key
cd ~/.ssh
ls 或者 ll
//看是否存在 id_rsa 和 id_rsa.pub文件,如果存在,说明已经有SSH Key
如果没有SSH Key,则需要先生成一下
ssh-keygen -t rsa -C "[email protected]"
三、获取SSH Key
cat id_rsa.pub
//拷贝秘钥 ssh-rsa开头
四、GitHub添加SSH Key
五、验证和修改
测试是否成功配置SSH Key
ssh -T [email protected]
//运行结果出现类似如下
Hi CC! You've successfully authenticated, but GitHub does not provide shell access.
123
之前已经是https的链接,现在想要用SSH提交怎么办?
直接修改项目目录下 .git
文件夹下的config
文件,将地址修改一下就好了。
切换分支使用git checkout
,撤销修改则是git checkout --
,同一个命令,有两种作用,确实有点令人迷惑。
实际上,切换分支这个动作,用switch
更科学。因此,最新版本的Git提供了新的git switch
命令来切换分支:
创建并切换到新的dev
分支,可以使用:
$ git switch -c dev
直接切换到已有的master
分支,可以使用:
$ git switch master
使用新的git switch
命令,比git checkout
要更容易理解。
#rebase操作可以把本地未push的分叉提交历史整理成直线;
#rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。
$ git rebase
在 .gitignore 文件中,每一行的忽略规则的语法如下:
1)空格不匹配任意文件,可作为分隔符,可用反斜杠转义
2)以“#”开头的行都会被 Git 忽略。即#开头的文件标识注释,可以使用反斜杠进行转义。
3)可以使用标准的glob模式匹配。所谓的glob模式是指shell所使用的简化了的正则表达式。
4)以斜杠"/“开头表示目录;”/“结束的模式只匹配文件夹以及在该文件夹路径下的内容,但是不匹配该文件;”/“开始的模式匹配项目跟目录;如果一个模式不包含斜杠,则它匹配相对于当前 .gitignore 文件路径的内容,如果该模式不在 .gitignore 文件中,则相对于项目根目录。
5)以星号""通配多个字符,即匹配多个任意字符;使用两个星号"*” 表示匹配任意中间目录,比如a/**/z
可以匹配 a/z, a/b/z 或 a/b/c/z等。
6)以问号"?“通配单个字符,即匹配一个任意字符;
7)以方括号”[]“包含单个字符的匹配列表,即匹配任何一个列在方括号中的字符。比如[abc]表示要么匹配一个a,要么匹配一个b,要么匹配一个c;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配。比如[0-9]表示匹配所有0到9的数字,[a-z]表示匹配任意的小写字母)。
8)以叹号”!“表示不忽略(跟踪)匹配到的文件或目录,即要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。需要特别注意的是:如果文件的父目录已经被前面的规则排除掉了,那么对这个文件用"!"规则是不起作用的。也就是说”!“开头的模式表示否定,该文件将会再次被包含,如果排除了该文件的父级目录,则使用”!"也不会再次被包含。可以使用反斜杠进行转义。
需要谨记:git对于.ignore配置文件是按行从上到下进行规则匹配的,意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效;
.gitignore忽略规则简单说明
# 表示此为注释,将被Git忽略
*.a 表示忽略所有 .a 结尾的文件
!lib.a 表示但lib.a除外
/TODO 表示仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/ 表示忽略 build/目录下的所有文件,过滤整个build文件夹;
doc/*.txt 表示会忽略doc/notes.txt但不包括 doc/server/arch.txt
bin/: 表示忽略当前路径下的bin文件夹,该文件夹下的所有内容都会被忽略,不忽略 bin 文件
/bin: 表示忽略根目录下的bin文件
/*.c: 表示忽略cat.c,不忽略 build/cat.c
debug/*.obj: 表示忽略debug/io.obj,不忽略 debug/common/io.obj和tools/debug/io.obj
**/foo: 表示忽略/foo,a/foo,a/b/foo等
a/**/b: 表示忽略a/b, a/x/b,a/x/y/b等
!/bin/run.sh 表示不忽略bin目录下的run.sh文件
*.log: 表示忽略所有 .log 文件
config.php: 表示忽略当前路径的 config.php 文件
/mtk/ 表示过滤整个文件夹
*.zip 表示过滤所有.zip文件
/mtk/do.c 表示过滤某个具体文件
被过滤掉的文件就不会出现在git仓库中(gitlab或github)了,当然本地库中还有,只是push的时候不会上传。
需要注意的是,gitignore还可以指定要将哪些文件添加到版本管理中,如下:
!*.zip
!/mtk/one.txt
唯一的区别就是规则开头多了一个感叹号,Git会将满足这类规则的文件添加到版本管理中。为什么要有两种规则呢?
想象一个场景:假如我们只需要管理/mtk/目录中的one.txt文件,这个目录中的其他文件都不需要管理,那么.gitignore规则应写为::
/mtk/*
!/mtk/one.txt
假设我们只有过滤规则,而没有添加规则,那么我们就需要把/mtk/目录下除了one.txt以外的所有文件都写出来!
注意上面的/mtk/*不能写为/mtk/,否则父目录被前面的规则排除掉了,one.txt文件虽然加了!过滤规则,也不会生效!
----------------------------------------------------------------------------------
还有一些规则如下:
fd1/*
说明:忽略目录 fd1 下的全部内容;注意,不管是根目录下的 /fd1/ 目录,还是某个子目录 /child/fd1/ 目录,都会被忽略;
/fd1/*
说明:忽略根目录下的 /fd1/ 目录的全部内容;
/*
!.gitignore
!/fw/
/fw/*
!/fw/bin/
!/fw/sf/
说明:忽略全部内容,但是不忽略 .gitignore 文件、根目录下的 /fw/bin/ 和 /fw/sf/ 目录;注意要先对bin/的父目录使用!规则,使其不被排除。
注意:
如果你不慎在创建.gitignore文件之前就push了项目,那么即使你在.gitignore文件中写入新的过滤规则,这些规则也不会起作用,Git仍然会对所有文件进行版本管理。简单来说出现这种问题的原因就是Git已经开始管理这些文件了,所以你无法再通过过滤规则过滤它们。所以大家一定要养成在项目开始就创建.gitignore文件的习惯,否则一单push,处理起来会非常麻烦。
如果合并了两个不同的开始提交的仓库,在新的 git 会发现这两个仓库可能不是同一个,为了防止开发者上传错误,于是就给下面的提示
$ fatal: refusing to merge unrelated histories
如我在Github新建一个仓库,写了License,然后把本地一个写了很久仓库上传。这时会发现 github 的仓库和本地的没有一个共同的 commit 所以 git 不让提交,认为是写错了 origin
,如果开发者确定是这个 origin
就可以使用 --allow-unrelated-histories
告诉 git 自己确定
遇到无法提交的问题,一般先pull 也就是使用 git pull origin master
这里的 origin
就是仓库,而 master
就是需要上传的分支,因为两个仓库不同,发现 git 输出 refusing to merge unrelated histories
无法 pull 内容
因为他们是两个不同的项目,要把两个不同的项目合并,git需要添加一句代码,在 git pull
之后,这句代码是在git 2.9.2版本发生的,最新的版本需要添加 --allow-unrelated-histories
告诉 git 允许不相关历史合并
假如我们的源是origin,分支是master,那么我们需要这样写git pull origin master --allow-unrelated-histories
如果有设置了默认上传分支就可以用下面代码
$ git pull --allow-unrelated-histories
也可以通过强制覆盖的方法覆盖当前分支
$ git push -u origin master -f
warning: LF will be replaced by CRLF in
原因是存在符号转义问题
windows中的换行符为 CRLF, 而在linux下的换行符为LF,所以在执行add . 时出现提示,解决办法:
git config --global core.autocrlf false
只需要设置Git忽略ssl证书错误即可,使用下面的命令:
git config --global http.sslVerify "false"
再次clone。
空文件夹是项目的子模块,需要用submodule下载。
@后面的数字是哈希值,用于确定唯一的提交状态。文件push时会压缩,然后上传同时生成一串检验字符串。at后面的数字就是检验字符串的前面部分
是带有子模块的项目;需要进入下载的文件下,运行命令 git submodule update --init --recursive 即可。
当然也可以使用 git clone --recursive 直接把主模块和子模块项目同时拉取下来,不过很可能由于网络问题导致拉取失败。
注意加上指定深度拉取。
有些时候不小心上传了一些敏感文件(例如密码), 或者不想上传的文件(没及时或忘了加到.gitignore里的),
而且上传的文件又特别大的时候, 这将导致别人clone你的代码或下载zip包的时候也必须更新或下载这些无用的文件,
因此, 我们需要一个方法, 永久的删除这些文件(包括该文件的历史记录).
首先, 可以参考 github 的帮助:
https://help.github.com/articles/remove-sensitive-data
注意以下操作完后,如果远程库中无文件,则会删除主分支。
步骤一: 从你的资料库中清除文件
以Windows下为例(Linux类似), 打开项目的Git Bash,使用命令:
$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch path-to-your-remove-file' --prune-empty --tag-name-filter cat -- --all
其中, path-to-your-remove-file 就是你要删除的文件的相对路径(相对于git仓库的跟目录), 替换成你要删除的文件即可. 注意一点,这里的文件或文件夹,都不能以 ‘/’ 开头,否则文件或文件夹会被认为是从 git 的安装目录开始。
如果你要删除的目标不是文件,而是文件夹,那么请在 git rm --cached' 命令后面添加 -r 命令,表示递归的删除(子)文件夹和文件夹下的文件,类似于
rm -rf` 命令。
此外,如果你要删除的文件很多, 可以写进一个.sh文件批量执行, 如果文件或路径里有中文, 由于MinGW或CygWin对中文路径设置比较麻烦, 你可以使用通配符号, 例如: sound/music_.mp3, 这样就把sound目录下以music_开头的mp3文件都删除了.
例如这样, 新建一个 bash 脚本文件,del-music-mp3.sh:
$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch projects/Moon.mp3' --prune-empty --tag-name-filter cat -- --all
$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch sound/Music_*.mp3' --prune-empty --tag-name-filter cat -- --all
如果你看到类似下面这样的, 就说明删除成功了:
Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (266/266)
# Ref 'refs/heads/master' was rewritten
如果显示 xxxxx unchanged, 说明repo里没有找到该文件, 请检查路径和文件名是否正确.
注意: 补充一点, 如果你想以后也不会再上传这个文件或文件夹, 请把这个文件或文件夹添加到.gitignore文件里, 然后再push你的repo.
步骤二: 推送我们修改后的repo
以强制覆盖的方式推送你的repo, 命令下:
$ git push origin master -f
这个过程其实是重新上传我们的repo, 比较耗时, 虽然跟删掉重新建一个repo有些类似, 但是好处是保留了原有的更新记录, 所以还是有些不同的. 如果你实在不在意这些更新记录, 也可以删掉重建, 两者也差不太多, 也许后者还更直观些.
执行结果类似下面:
Counting objects: 4669, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4352/4352), done.
Writing objects: 100% (4666/4666), 35.16 MiB | 51 KiB/s, done.
Total 4666 (delta 1361), reused 0 (delta 0)
To https://github.com/defunkt/github-gem.git
+ beb839d...81f21f3 master -> master (forced update)
为了能从打了 tag 的版本中也删除你所指定的文件或文件夹,您可以使用这样的命令来强制推送您的 Git tags:
$ git push origin master --force --tags
步骤三: 清理和回收空间
虽然上面我们已经删除了文件, 但是我们的repo里面仍然保留了这些objects, 等待垃圾回收(GC), 所以我们要用命令彻底清除它, 并收回空间.
命令如下:
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
Counting objects: 2437, done.
# Delta compression using up to 4 threads.
# Compressing objects: 100% (1378/1378), done.
# Writing objects: 100% (2437/2437), done.
# Total 2437 (delta 1461), reused 1802 (delta 1048)
$ git gc --aggressive --prune=now
Counting objects: 2437, done.
# Delta compression using up to 4 threads.
# Compressing objects: 100% (2426/2426), done.
# Writing objects: 100% (2437/2437), done.
# Total 2437 (delta 1483), reused 0 (delta 0)