CVS及SVN都是集中式的版本控制系统,而Git是分布式版本控制系统
先说集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟,这还不得把人给憋死啊。
分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。
CVS作为最早的开源而且免费的集中式版本控制系统,直到现在还有不少人在用。由于CVS自身设计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。同样是开源而且免费的SVN修正了CVS的一些稳定性问题,是目前用得最多的集中式版本库控制系统。
Git是一款免费、开源的分布式版本控制系统,
项目管理适合 SVN, 代码管理适合 Git
当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址。 这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改:
git config --global user.name "xxx"git config --global user.email xxx@xxx.comgit cofnig --list 显示所有配置git config --global color.ui true 让Git显示颜色,会让命令输出看起来更醒目
基础命令
pwd显示当前目录git reflog用来记录你的每一次命令git log命令显示从最近到最远的提交日志
git log --pretty=oneline简化日志信息
git log --graph命令可以看到分支合并图
一大串类似3628164...882e1e0的是commit id(版本号),是一个SHA1计算出来的一个非常大的数字,用十六进制表示
远程仓库
远程仓库的默认名称是origin
克隆仓库
git clone git@github.com:xxx/xxx.git克隆仓库到本地git push origin master把本地分支的最新修改推送远程
本地新建仓库
git init使用当前目录作为Git仓库git remote add origin git@github.com:xxx/xxx.git把一个已有的本地仓库关联一个远程库git push -u origin master把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来git push origin master从本地推送到远程分支,如果推送失败,先用git pull origin master抓取远程的新提交
其他
git remote -v查看远程库信息,显示了可以抓取和推送的origin的地址
分支
Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
git branch [-a]会列出所有分支,当前分支前面会标一个*号。git branch dev在当前分支基础上创建 dev 分支git checkout dev切换到 dev 分支git checkout -b dev在当前分支基础上创建并切换到dev分支 相当于git branch dev和git checkout dev2个命令git branch -d dev删除dev分支git branch -D dev强制删除dev分支
合并分支 merge
git merge dev用于合并 dev 到当前分支git merge --no-ff -m "merge with no-ff" dev其中--no-ff表示禁用Fast forward本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。
临时储藏 stash
git stash可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作git stash list查看工作现场- 恢复
git stash apply,恢复后,stash内容并不删除,你需要用git stash drop来删除git stash pop,恢复的同时把stash内容也删了
远程分支
git fetch [branch-name]取回所有分支(branch)的更新。如果只想取回特定分支的更新,可以指定分支名,将某个远程主机的更新,全部取回本地git checkout -b branch-name origin/branch-name在本地创建和远程分支对应的分支,本地和远程分支的名称最好一致git branch --set-upstream branch-name origin/branch-name建立本地分支和远程分支的关联
标签管理
Git 标签 tag 就是一个让人容易记住的有意义的名字,它就是指向某个commit的指针
git tag查看所有标签,标签不是按时间顺序列出,而是按字母排序的git show <tagname>查看标签信息git tag <name> [commit id]就可以打一个新标签,默认标签是打在最新提交的commit上git tag -a v0.1.0 -m "release 0.1.0 version"打一个有描述信息的标签git push origin <tagname>推送某个标签到远程git push origin --tags一次性推送全部尚未推送到远程的本地标签git tag -d <tagname>可以删除一个本地标签git push origin :refs/tags/<tagname>可以删除一个远程标签。git push origin --delete tag <tagname>可以删除一个远程标签。
如果标签已经推送到远程,要删除远程标签,先从本地删除: git tag -d v0.9 然后,从远程删除: git push origin :refs/tags/v0.9
gitignore 忽略特殊文件
cd项目根目录touch .gitignore在文件夹就生成了一个.gitignore文件- 编辑
- 提交到Git
GitHub下fork后同步源的更新
git remote -v查看,如果没有原作者的源git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git添加原作者源git fetch upstreamgit checkout mastergit merge upstream/mastergit push origin master
Submodule 子模块
常用命令
1 | git clone <repository> --recursive 递归的方式克隆整个项目 |
克隆带子模块的版本库
初始化 submodule
初始化只需要做一次,之后每次只需要直接update就可以了,需要注意submodule默认是不在任何分支上的,它指向父项目存储的submodule commit id。
- clone父项目
git clone xxxx.git - 初始化submodule
git submodule init - 最后更新submodule
git submodule update
递归 clone
采用递归参数 --recursive 需要注意同样submodule默认是不在任何分支上的,它指向父项目存储的submodule commit id。git clone xxxx.git --recursive
日常使用
git clone git地址克隆远程仓库到当前位置git status查看文件状态(显示变更的文件)git diff对比文件差异git add .添加所有文件, 实际上就是把要提交的所有修改放到暂存区(Stage)git commit -m '本次提交的说明'把暂存区的所有内容提交到当前分支git pull origin master同步文件git push origin master推送文件
使用 rebase 合并多次 commit
查看记录 git log
1 | commit fc595c13010b4267e422de385febe1223796ca35 (HEAD -> dev) |
git rebase -i 0511f5c357 将 2 / 3 / 4 合并 为 5, -i 的参数是不需要合并的 1 的 hash 值(0511f5c357d924179d8c1566a34aa9e578ff338d)
进入 vi 窗口pick 的意思是要会执行这个 commitsquash 的意思是这个 commit 会被合并到前一个commit
除第一个 pick 外, 将其他的 pick 改为 s / squash
1 | pick 405f54d 2 |
保存退出(:wq)后进入 commit 窗口, 填写 commit 注释保存退出
1 | # This is a combination of 3 commits. |
再次查看 log
1 | commit 9f077d1de022062381e8ada7774adf73c13dc329 (HEAD -> dev) |
如果需要强制推送远程仓库使用 git push --force
删除 某一次 commit
查看 log
1 | commit 71b4cd9c932ed421c6a0446ab47aaf2246c79ff3 (HEAD -> dev) |
git rebase -i 0511f5c357 删除 This is a combination of 3 commits. 提交记录
将要删除的 commit 的 pick 改为 drop
1 | pick 9f077d1 This is a combination of 3 commits. |
处理冲突后, 这里需要重新提交 6
git rebase --continue处理冲突继续 rebasegit rebase --abort取消 rebase
再次 log 发现 提交记录/代码 已经没有 This is a combination of 3 commits.
撤销修改
修改文件后需要还原文件
git reset --hard HEAD^回退到上一个版本,用HEAD表示当前版本,上一个版本就是HEAD^- git add 后撤销:
撤销所有add文件git reset HEAD .
撤销单个add文件git reset HEAD -filename git checkout .撤销所有本地改动代码
回退 详情查看 reset
git reset主要是取消上一次的操作
git reset --hard HEAD^回退到上一个版本,用HEAD表示当前版本,上一个版本就是HEAD^git reset --hard 3628164回退到指定版本,版本号没必要写全,前几位就可以了,Git会自动去找
reset命令有3种方式:
git reset –mixed:此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,回退commit和index信息git reset –soft:回退到某个版本,只回退了commit的信息,保留修改代码, 不会恢复到index file一级。如果还要提交,直接commit即可git reset –hard:彻底回退到某个版本,不保留修改代码, 本地的源码也会变为上一个版本的内容
撤销本地修改 checkout
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
git checkout -- readme.txt把readme.txt文件在工作区的修改全部撤销git checkout .撤销所有本地改动代码
先提交文件, 再添加 ignore
先提交了文件, 才添加了 .gitignore 配置
git rm -r --cached .ideacached不会把本地的.idea删除git commit -m 'delete .idea dir'git push -u origin master
合并分支文件冲突
git merge dev 用于合并 指定分支dev 到当前分支 文件冲突,必须手动解决冲突后再提交。git status 也可以告诉我们冲突的文件 Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容
BUG分支(stash 使用)
每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
- 在 dev分支上把当前工作现场“储藏”起来
git stash - 切换到master分支,基于master创建临时分支
1
2git checkout master
git checkout -b bug002 - 修复bug并提交到 bug002
- 切换到master分支,完成合并,删除 bug002 分支
1
2
3
4git checkout master
git merge --no-ff -m "merged bug " bug002
git branch -d bug002
git pull / push - 回到dev分支 恢复 stash
1
2git checkout dev
git stash pop
git checkout
- 切换分支
git checkout <branchname> - 放弃对某个文件的修改
git checkout -- fielname.xx - 新建一个分支并切换到该分支
git checkout <-b> <branchname>
删除不在版本库的文件
git clean -d -fx
x删除忽略文件已经对git来说不识别的文件d删除未被添加到git的路径中的文件f强制运行
日志输出参数
查看帮助 man git log
– author=“Alex Kras” ——只显示某个用户的提交任务
– name-only ——只显示变更文件的名称
– oneline ——将提交信息压缩到一行显示
– graph ——显示所有提交的依赖树
– reverse ——按照逆序显示提交记录(最先提交的在最前面)
– after ——显示某个日期之后发生的提交
– before ——显示发生某个日期之前的提交
如:git log --oneline --graph
在每周五提交周报 git log --author="Alex Kras" --after="1 week ago" --oneline
查看文件的详细变更
git log -p filename 显示提交说明、提交者以及提交日期、每次提交实际修改的内容
查看尚未合并的变更
git log --no-merges master.. 尚未合并到master分支。--no-merges 标志意味着只显示没有合并到任何分支的变更master.. 是指显示没有合并到master分支的变更(在master后面必须有..)
git show --no-merges master.. 输出结果 == git log -p --no-merges master..
查看其他分支中的文件
git show devBranch:readMe.md 可以很方便地查看其他分支上的文件而无需切换到那个分支。
git show devBranch:readMe.md > temp.md 将输出重定向到一个临时文件
git diff devBranch readMe.md 查看另一个分支上文件与当前分支上文件的差异
关于变更基线的几点说明
在远程分支上工作会有大量的合并提交。使用 git rebase 可以避免这些提交
git pull –rebase
假设你正在master分支的一个本地版本上工作,你已经向仓库提交了一小部分变更。与此同时,也有人向master分支提交了他一周的工作成果。当你尝试推送本地变更时,git提示你需要先运行一下 git pull , 来解决冲突。你运行了一下git pull ,并且git自动生成了Merge remote-tracking branch ‘origin/master’的提交信息。尽管这不是什么大问题,也完全安全,但是不太有利于历史记录的聚合。
这种情况下,git pull --rebase 是一个不错的选择。
这个命令会迫使git将远程分支上的变更同步到本地,然后将尚未推送的提交重新应用到这个最新版本,就好象它们刚刚发生一样。这样就可以避免合并以及随之而来的丑陋的合并信息了。
忽略本地文件
使用场景: 拉取了服务端配置, 修改为本地配置, 不提交此处的修改,但是在’git status’中看到文件修改
在本机忽略本地文件修改的办法:
git update-index --assume-unchanged project/path/file.swift
要恢复的话,方法如下:
git update-index --no-assume-unchanged project/path/file.swift
如果想知道当前被忽略修改的本地文件列表,使用如下命令:
git ls-files -v | grep -e "^[hsmrck]"
参考文章