Git教程
Git介绍 git命令思维导图:
分布式 :Git版本控制系统是一个分布式的系统,是用来保存工程源代码历史状态的命令行工具。
保存点 :Git的保存点可以追踪源码中的文件, 并能得到某一个时间点上的整个工程项目的状态;可以在该保存点将多人提交的源码合并, 也可以回退到某一个保存点上。
Git离线操作性 :Git可以离线进行代码提交,因此它称得上是完全的分布式处理,Git所有的操作不需要在线进行;这意味着Git的速度要比SVN等工具快得多,因为SVN等工具需要在线时才能操作,如果网络环境不好, 提交代码会变得非常缓慢。
Git****基于快照 :SVN等老式版本控制工具是将提交点保存成补丁文件,Git提交是将提交点指向提交时的项目快照,提交的东西包含一些元数据(作者,日期,GPG等)。
Git****的分支和合并 :分支模型是Git最显著的特点,因为这改变了开发者的开发模式,SVN等版本控制工具将每个分支都要放在不同的目录中,Git可以在同一个目录中切换不同的分支。
分支即时性 :创建和切换分支几乎是同时进行的,用户可以上传一部分分支,另外一部分分支可以隐藏在本地,不必将所有的分支都上传到GitHub中去。
分支灵活性 :用户可以随时创建、合并、删除分支,多人实现不同的功能,可以创建多个分支进行开发,之后进行分支合并,这种方式使开发变得快速、简单、安全。
Git工作流程
从远程仓库中克隆 Git 资源作为本地仓库。
从本地仓库中checkout代码然后进行代码修改
在提交前先将代码提交到暂存区。
提交修改。提交到本地仓库。本地仓库中保存修改的各个历史版本。
在修改完成后,需要和团队成员共享代码时,可以将代码push到远程仓库。
Git安装 官网下载地址:https://git-scm.com/download
Windows 下载相应git版本,默认安装即可
默认使用Git Bash即可
选择提交的时候换行格式
(1)检查出windows格式转换为unix格式:将windows格式的换行转为unix格式的换行再进行提交。
(2)检查出原来格式转为unix格式:不管什么格式的,一律转为unix格式的换行再进行提交。
(3)不进行格式转换 : 不进行转换,检查出什么,就提交什么。
Linux 1 sudo apt-get install git
常用配置 用户、邮件配置 1 2 3 4 5 6 7 git config --list git config -e [--global] git config --global user.email "666.com" git config --global user.name "rain"
设置大小写敏感 Windows上的Git默认是大小写不敏感的。
1 git config core.ignorecase false
远程仓库 GitHub 创建仓库
SSH Github支持两种同步方式“https”
和“ssh”
。
如果使用https
很简单基本不需要配置就可以使用,但是每次提交代码和下载代码时都需要输入用户名和密码。
如果使用ssh
方式就需要客户端先生成一个密钥对,即一个公钥一个私钥。然后还需要把公钥放到githib的服务器上。
SSH(Secure Shell):安全外壳协议
基于密匙的安全验证
使用ssh协议通信时,推荐使用基于密钥的验证方式。你必须为自己创建一对密匙,并把公用密匙放在需要访问的服务器上。 如果你要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。服务器收到请求之后,先在该服务器上你的主目录下寻找你的公用密匙,然后把它和你发送过来的公用密匙进行比较。 如果两个密匙一致,服务器就用公用密匙加密“质询”(challenge)并把它发送给客户端软件。 客户端软件收到“质询”之后就可以用你的私人密匙解密再把它发送给服务器。
ssh密钥生成 Windows下ssh秘钥路径:C:\Users\用户名\.ssh
秘钥文件:
id_rsa 私钥
id_rsa.pub 公钥
如果没有秘钥文件,就打开命令行工具生成:
ssh密钥配置 进入 Settings 页面:
在key部分将 id_rsa.pub 文件内容添加进去,然后点击 “Add SSH key” 按钮完成配置。
ssh多个私钥管理 使用本地托管多个个ssh的密钥,不同的账号用不同的密匙。
生成SSH Key 1 2 3 4 5 6 7 $ ssh-keygen -t rsa -C "your_email@youremail.com" Enter file in which to save the key (/c/Users/zhaoyafei/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again:
识别新的私钥 默认SSH只会读取id_rsa,所以为了让SSH识别新的私钥,需要将其添加到SSH agent。
1 2 3 4 5 $ ssh-add ~/.ssh/id_rsa_work ssh-agent bash ssh-add -l
修改config文件
/etc/.gitconfig 文件:包含了适用于系统所有用户和所有库的值。传递参数选项’–system’ ,读和写这个文件。
~/.gitconfig 文件 :具体到用户。传递–global 选项使Git 读或写这个特定的文件。
.git/config 文件:局部配置文件,在.git/config中的值覆盖了在/etc/gitconfig中的同一个值。
该文件用于配置私钥对应的服务器。
1 2 3 4 5 6 7 8 9 10 11 Host github.com HostName github.com User git IdentityFile ~/.ssh/id_rsa Host github_work HostName github.com User git IdentityFile ~/.ssh/id_rsa_work
这样配置,也就是使用hostname为github.com会根据用户名的不同,去使用不用的private key。github上,也可以添加对应的公钥。
Git在$HOME目录中查找.gitconfig文件(通常位于C:\Documents and Settings$USER下)。
将SSH key输入到GitHub网站中 将生成的id_rsa_work.pub输入到GitHub网站中。
注意: github根据配置文件的user.email来获取github帐号显示author信息,所以对于多帐号用户一定要记得将user.email改为相应的email
1 2 3 git config user.name "xxxxxx" git config user.email "xxx@xxx.com"
Git常用命令 命令列表
版本管理 远程同步 查看状态 1 2 3 4 5 git remote -v git remote show [remote]
从远程仓库克隆 1 2 3 4 5 6 git remote add [shortname] [url] git clone [git.url] git fetch [remote]
从远程仓库拉取 1 2 git pull [remote] [branch]
推送到远程仓库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 git push <remote 名字> <本地分支的名字> : <远程库的名字> git push origin HEAD:refs/for /master git push (-u) [remote] [branch] git push [remote] --force git push [remote] --all
关联远程仓库 1 2 3 4 5 6 git init git remote add origin [git.url] git branch –-set-upstream-to=origin/master master
查看命令 查看状态 1 2 3 4 5 6 7 8 9 10 11 12 git status git blame [file] git show [commit] git show --name-only [commit] git show [commit]:[filename] git reflog
查看日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 git log git log --stat git log -S [keyword] git log [tag] HEAD --pretty=format:%s git log [tag] HEAD --grep feature git log --follow [file] git whatchanged [file] git log -p [file] git log -5 --pretty --oneline git shortlog -sn
查看差异 1 2 3 4 5 6 7 8 9 10 git diff git diff --cached [file] git diff HEAD git diff [first-commit] [second-commit] git diff --shortstat "@{0 day ago}"
1 2 3 4 5 6 7 8 9 10 git status git diff * git log git reflog git log --pretty=oneline
撤销 reset 撤销某次提交,但是此次之后的修改都会被退回到暂存区,把HEAD向后移动了一下。
1 2 3 4 5 6 7 8 9 10 11 12 git reset [file] git reset --hard git reset [commit] git reset --hard [commit] git reset --keep [commit] git reset --hard HEAD~N
revert 用一次新的commit来回滚之前的commit,HEAD继续前进。
1 2 3 4 git revert [commit] git revert –hard a167
checkout 1 2 3 4 5 6 7 git checkout [file] git checkout -- [file] git checkout [commit] [file] git checkout .
stash 1 2 3 git stash git stash pop
添加文件 1 2 3 4 5 6 7 8 git add [file1] [file2] ... git add [dir] git add . git add -p file
提交 提交到本地库 1 2 3 4 5 6 7 8 git commit -m [message] git commit [file1] [file2] ... -m [message] git commit -a git commit -v
追加修改 1 2 3 4 5 git commit --amend -m [message] git commit --amend [file1] [file2] ...
提交到远程库 删除 删除工作区文件 1 2 git rm [file1] [file2] ...
停止追踪指定文件
改名文件 1 2 git mv [file-original] [file-renamed]
删除未添加到版本中的文件或者文件夹 1 2 3 4 5 6 7 8 9 10 git clean -f git clean -fd git clean -xfd git clean -nxfd git clean -nf git clean -nfd
隐藏的文件 1 2 3 4 5 6 7 8 9 10 git stash git stash list git stash apply git stash drop git stash pop
分支管理 查看分支 1 2 3 4 5 6 git branch git branch -a git branch -r
创建分支 1 2 3 4 5 6 7 8 9 10 11 12 git branch [branch-name] git checkout -b [branch-name] git branch [branch] [commit] git branch --track [branch] [remote-branch] git branch -m dev [branch-name] git branch --set-upstream [branch] [remote-branch]
删除分支 1 2 3 4 5 git branch -d [branch-name] git push origin --delete [branch-name] git branch -dr [remote/branch]
切换分支 1 2 3 4 git checkout [branch-name] git checkout -
推送分支 1 2 3 4 git push origin dev git push --set-upstream origin dev
拉取分支 1 2 3 4 5 6 7 8 git checkout -b 本地分支名x origin/远程分支名x git fetch origin 远程分支名x:本地分支名x git clone xxx.git "指定目录" git clone -b branchname xxx.git
合并分支 1 2 3 4 git merge [branch] git cherry-pick [commit]
通常合并分支时,git一般使用”Fast forward”
模式,在这种模式下,删除分支后,会丢掉分支信息,现在我们来使用带参数 –no-ff来禁用”Fast forward”
模式。
1 git merge –-no-ff -m “注释” dev
合并过程:
1 2 3 4 5 6 7 8 9 10 git checkout develop git pull git checkout master git merge develop git push
冲突解决 根据提示冲突文件解决冲突。
标签管理 查看tag 1 2 3 4 git tag git show [tag]
新建tag 1 2 3 4 5 6 git tag [tag] git tag [tag] [commit] git checkout -b [branch] [tag]
提交tag 1 2 3 4 git push [remote] [tag] git push [remote] --tags
删除tag 1 2 3 4 git tag -d [tag] git push origin :refs/tags/[tagName]
其它
Git分支管理策略 主分支Master 代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。
Git主分支的名字,默认叫做Master。它是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。
开发分支Develop 主分支只用来分布重大版本,日常开发应该在另一条分支上完成。把开发用的分支,叫做Develop。
这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在Master分支上,对Develop分支进行”合并”(merge)。
1 2 3 4 5 6 git checkout -b develop master git checkout master git merge --no-ff develop
默认情况下,Git执行”快进式合并”(fast-farward merge),会直接将Master分支指向Develop分支。
使用–no-ff参数后,会执行正常合并,在Master分支上生成一个新节点。
临时性分支】 临时性分支,用于应对一些特定目的的版本开发。临时性分支主要有三种:
1 2 3 * 功能(feature)分支 * 预发布(release)分支 * 修补bug(fixbug)分支
功能分支 功能分支,是为了开发某种特定功能,从Develop分支上面分出来的。开发完成后,要再并入Develop。
1 2 3 4 5 6 7 git checkout -b feature-x develop git checkout develop git merge --no-ff feature-x git branch -d feature-x
预发布分支 预发布分支,是指发布正式版本之前(即合并到Master分支之前),可能需要有一个预发布的版本进行测试。
预发布分支是从Develop分支上面分出来的,预发布结束以后,必须合并进Develop和Master分支。
1 2 3 4 5 6 7 8 9 10 11 12 git checkout -b release-1.2 develop git checkout master git merge --no-ff release-1.2 git tag -a 1.2 git checkout develop git merge --no-ff release-1.2 git branch -d release-1.2
修补bug分支 修补bug分支。软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补。
修补bug分支是从Master分支上面分出来的。修补结束以后,再合并进Master和Develop分支。
1 2 3 4 5 6 7 8 9 10 11 git checkout -b fixbug-0.1 master git checkout master git merge --no-ff fixbug-0.1 git tag -a 0.1.1 git checkout develop git merge --no-ff fixbug-0.1 git branch -d fixbug-0.1
忽略文件 .gitignore .gitignore 配置文件用于配置不需要加入版本管理的文件。
配置语法:
“/“ 开头表示目录; “?” 通配单个字符; “[]” 包含单个字符的匹配列表; “!” 表示不忽略(跟踪)匹配到的文件或目录; 说明:git 对于 .ignore 配置文件是按行从上到下进行规则匹配的,意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效;
编辑 .gitignore 文件:
1 2 3 4 5 6 7 8 foder/* /foder/* /*!.gitignore !/fw/bin/ !/fw/sf/
注意问题: .gitignore文件只对还没有加入版本管理的文件起作用,如果之前已经用git把要忽略的文件纳入了版本库,就不起作用了。
git 打补丁 patch与diff
.diff:git diff生成的UNIX标准补丁文件,只记录文件改变的内容,不带有commit记录信息,多个commit可以合并成一个diff文件。
.patch:git format-patch生成的Git专用.patch 文件,带有记录文件改变的内容,也带有commit记录信息,每个commit对应一个patch文件。
创建patch与diff 创建patch文件 1 2 3 4 # 1、某次提交(含)之前的几次提交的patch git format-patch 【commit sha1 id】-n # 2、某两次提交之间的所有patch git format-patch 【commit sha1 id】..【commit sha1 id】
创建diff文件 1 git diff 【commit sha1 id】 【commit sha1 id】 > 【diff文件名】
应用patch与diff
检查patch/diff是否能正常打入
1 2 git apply --check 【path/to/xxx.patch】 git apply --check 【path/to/xxx.diff】
打入patch/diff
1 2 3 4 5 git apply 【path/to/xxx.patch】 git apply 【path/to/xxx.diff】 # 或者 git am 【--s --signoff】 【path/to/xxx.patch】 git am 【--whitespace=fix】 【path/to/xxx.patch】
冲突解决
使用 以下命令行,自动合入 patch 中不冲突的代码改动,同时保留冲突的部分:
1 git apply --reject xxxx.patch
同时会生成后缀为 .rej 的文件,保存没有合并进去的部分的内容,可以参考这个进行冲突解决。
解决完冲突后删除后缀为 .rej 的文件,并执行git add.
添加改动到暂存区.
继续冲突合入
1 2 3 git am --resolved # 或者 git am --continue
合入冲突:
1 2 3 4 # 跳过此次冲突,也可以执行 git am --skip # 回退打入patch的动作,还原到操作前的状态 git am --abort
更改某个提交内容 可以创建一个分支,用于rebase:
1 git checkout -b rebase_tmp
rebase 要指定parent commitID 。
直接更改某次提交 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 git rebase -i 0bdf89^ git add file_mod git commit --amend git rebase --continue git add . git commit --amend git rebase --continue
将工作空间中的改动追加到某次提交上 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 git stash git rebase f744c32^ --interactive git stash pop git add file_mod git commit --amend git rebase --continue git add . git commit --amend git rebase --continue
Git常见问题 window与linux的换行符问题 1 2 3 git config --global core.autocrlf false git config --global core.filemode false git config --global core.safecrlf true
AutoCRLF :
1 2 3 4 5 6 # 提交时转换为LF,检出时转换为CRLF git config --global core.autocrlf true # 提交时转换为LF,检出时不转换 git config --global core.autocrlf input # 提交检出均不转换 git config --global core.autocrlf false
修改文件权限后文件状态修改 1 2 3 4 5 6 # 1、修改文件夹权限 chmod -R 755 test_dir # 2、查看 git 仓库状态,提示文件夹下所有文件被修改 git status # 3、配置文件模式,忽略文件或文件夹权限修改,解决 git config core.filemode false
拉取仓库中某个目录 1 2 3 4 git config core.sparsecheckout true echo firstDir/* >> .git/info/sparse-checkout
Github访问速度慢 优化思路:通过绕过DNS解析,直接在本地绑定host
查询DNS DNS查询网站:
https://www.ipaddress.com/
http://tool.chinaz.com/dns
查询 github 相关网址域名
1 2 github.com github.global .ssl .fastly .net
修改hosts windows下打开文件:C:\Windows\System32\drivers\etc\hosts
末尾追加内容:
1 2 140.82.112.3 github.com 199.232.69.194 github.global.ssl.fastly.net
刷新DNS windows下刷新DNS:
切换git 命令提示中文到英文 1 2 3 4 5 echo "alias git='LANG=en_GB git'" >> ~/.bashrcsource ~/.bashrc
git push警告
解决办法:‘matching’ 参数是 Git 1.x 的默认行为,如果你执行 git push 但没有指定分支,它将 push 所有你本地的分支到远程仓库中对应匹配的分支。而 Git 2.x 默认的是 simple,意味着执行 git push 没有指定分支时,只有当前分支会被 push 到你使用 git pull 获取的代码。 根据提示,修改git push:
1 git config --global push.default matching
再次执行git push。
当前分支没有追踪信息 There is no tracking information for the current branch… 则说明本地分支和远程分支的链接关系没有创建,用命令:
1 git branch --set-upstream branch-name origin/branch-name
冲突,推送失败 ![rejected] dev -> dev (non-fast-forward) … Updates were rejected because the tip of your current branch 推送失败,因为远程代码的最新提交和你试图推送的提交有冲突,解决办法:
先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送。
OpenSSL SSL_read OpenSSL SSL_read: Connection was reset, errno 10054
修改设置,解除ssl验证
1 git config --global http.sslVerify false
不能追踪某个文件夹下的修改 fatal: Pathspec ‘xxx’ is in submodule
1 git rm -rf --cached folder
检测不到文件变化 git 提交之后,在git上某个文件有后缀@b4c4u7之类的。
本地 git status 的时候检测不到文件的变化:
1 2 3 4 5 # 从.git/index中删除所有变更过的文件最后再正常提交 git rm -r --cached . git add . git commit -m "Initial commit" git push origin master
Git - svn SVN是集中式版本控制系统,版本库是集中放在中央服务器的。
Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库。
安装 1 sudo apt-get install git-svn
下载svn代码
这个操作会从第一个版本开始同步,若版本迭代太多会很费时,优化处理是检出最近的n个版本:
1 2 3 4 svn info url git svn clone -r m:HEAD url
更新svn仓库到本地
本地修改提交到svn仓库
提交冲突解决 1 2 3 4 5 6 7 8 9 10 11 12 13 14 git svn dcommit git svn rebase git rebase –continue git add conflict.c git rebase –continue git log git svn dcommit
查看log 1 2 3 4 5 6 7 8 9 10 git svn log -v git log git log -p git log --stat (可以查看每次提交log 和对应的修改文件) git log --author=
svn 常用命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 svn blame (praise, annotate, ann) svn changelist (cl) svn cleanup svn copy (cp) svn export svn import svn lock svn mergeinfo svn mkdir svn move (mv, rename, ren) svn propdel (pdel, pd) svn propedit (pedit, pe) svn propget (pget, pg) svn proplist (plist, pl) svn propset (pset, ps) svn resolved svn switch (sw) svn unlock
添加
拉取代码
更新
提交 1 2 3 svn ci svn commit -m 指定提交修改备注
删除文件 1 svn delete (del, remove, rm)
撤销
合并
查看 查看状态 1 2 3 svn st/stat svn status
“ ” 无修改
“A” 新增 “C” 冲突 “D” 删除 “G” 合并 “I” 忽略 “M” 改变 “R” 替换 “X” 未纳入版本控制,但被外部定义所用 “?” 未纳入版本控制 “!” 该项目已遗失 (被非 svn 命令所删除) 或是不完整
查看日志 1 2 svn log -l number 指定只显示最近的几条日志
查看信息
查看差异 1 2 3 4 5 6 7 8 9 svn di svn diff svn diff -r 版本号 文件名 svn diff -r 23:24 svn diff -r 23:24 文件名
查看指定版本某文件内容
查看目录列表 1 2 3 4 svn list(ls) svn list -v
查看帮助
属性 获取属性
设置属性 1 2 3 svn propset(ps,pset) svn ps svn:ignore -F .svnignore .
编辑属性 1 svn propedit(pe,pedit) svn:ignore 目录名称
恢复属性 在不影响内容更改的情况下还原所有属性更改:
1 svn revert `svn status | grep '^ M' | sed 's/^ M \+//g' `
忽略不必要的文件和文件夹 propset(ps,pset) 用于设置属性的值;**propget(pg,pget) **用于获取属性的值。
svn pg svn:ignore #获取属性值
svn ps svn:ignore ‘value’ path #设置属性值
忽略文件夹 1 2 3 4 5 6 7 8 9 10 11 vi ~/.bash_profile export SVN_EDITOR=vim source ~/.bash_profilesvn propset svn:ignore 'test' ./ property 'svn:ignore' set on '.' svn propset svn:ignore -R *.class .
设置忽略目录或文件: svn propedit svn:ignore . #最后的点号表示当前目录不能少 此时会打开vim编辑器,编辑这个文件,即告诉svn哪些目录或者目录应该被忽略,可能会有报错: svn: None of the environment variables SVN_EDITOR, VISUAL or EDITOR are set, and no ‘editor-cmd’ run-time configuration option was found 说明没有给svn的忽略目录设置文件指定使用什么来编辑,可执行如下命令,或者将这行命令写入到启动脚本: ~/.bash_profile 中 export SVN_EDITOR=vim 也可以直接修改SVN的配置文件:修改这行为:editor-cmd=vim 即可实现。 如写到启动脚本保存退出,则可执行命令 source ~/.bash_profile 来使配置文件立即生效。成功打开文件后一行编写一个忽略的目录或文件即可。保存后会有提示svn:ignore已经修改,此时再执行svn commit提交即可。
清空忽略内容 1 2 3 4 5 svn ps svn:ignore '' ./ property 'svn:ignore' set on '.' svn status --no-ignore
提交空文件夹 提交文件夹 忽略文件夹内容(前提是文件夹未在版本控制内)
1 2 svn propset svn:ignore '*' test svn ci -m 'adding "test1" and ignore its contents.'
忽略已提交的文件夹 已经创建了文件夹,并加入了版本控制,现在想忽略这个文件夹,但要保持文件夹的内容
1 2 3 4 5 6 7 8 9 10 11 12 svn export test1/ ./test1-tmp Export complete. svn rm test1 svn ci -m 'delete test1' mv ./test1-tmp/ ./test1 svn ps svn:ignore 'test1' ./ property 'svn:ignore' set on '.'
忽略多个目录 给属性设置多个值 1 2 3 4 5 6 7 8 9 svn ps svn:ignore " > d1 > d2 > d3 > " .property 'svn:ignore' set on '.' svn pg svn:ignore
通配符 属性值也可以使用通配符,但是通配符不可以加在末尾,只能加在前面。
1 2 3 4 5 6 svn ps svn:ignore 'd*' . 'd2' is not under version controlsvn ps svn:ignore '*2' . property 'svn:ignore' set on 'test2'