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.com
git 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 dev
2个命令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 upstream
git checkout master
git merge upstream/master
git 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 .idea
cached不会把本地的.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]"
参考文章