1. 说明
- 从原理到实际
- 从场景出发
- 先本地
- 在远程
1.1 概念/原理
- git 关心的是“文件快照”,而非“差异比较”。SVN等关心的是“差异”。简单理解就是,git每次提交都是完整文件的保存。不会比较同一文件在这次提交有什么变化。
- git 大多数的操作都在“本地”
- git 每次commit都是在移动
。HEAD
相当于指针,指向某次提交HEAD
- 每个文件都会有三种状态:
、未追踪
、暂存
已入库
- git 所有命令都是围绕文件的状态,与仓库的状态来工作
- git 三个地方,对应文件三种状态。
- Workspace:
,代码目录。包含了所有状态的文件工作区
- Index / Stage:
,git add了但没有提交的文件暂存区
- Repository:
,git commit入库后的存储本地仓库
- Remote:
远程仓库
- Workspace:
2. 本地代码一般操作
2.1 本地代码库初始化
- 进入本地代码根目录执行,系统会默认创建"master"分支
//假设代码目录有文件不需要这一步 $touch 1.txt //初始化git环境 $git init
- 添加本地文件到仓库并提交(两个命令)。两个&的含义是,前面一个命令执行出错,后面的就不会执行。注意: -m 表示本次提交的说明。一定要有
- 查看分支,master已经出现并作为默认分支
$git branch * master
2.2 创建与查看本地分支,*号代表当前代码所在分支
- 从master作为“父分支”创建本地分支dev。注意继承关系。从哪个分支创建子分支,代码就从哪个分支继承
$git branch dev
- 切换到本地分支dev
$git checkout dev
- 查看本地分支(因为checkout到dev了,所以*号在dev分支上
$git branch * dev master
2.3 修改代码并提交到本地分支“dev”
- 写入"123"到a.txt,如果a.txt不存在系统会创建
$echo "123" > a.txt
- 执行status命令,查看代码状态。可以看到提示“要把文件加入仓库追踪”
$git status On branch dev Untracked files: (use "git add <file>..." to include in what will be committed) a.txt nothing added to commit but untracked files present (use "git add" to track)
- 根据提示把文件加入追踪并提交到本地
- 再次执行status,提示工作区是干净的
$git status On branch dev nothing to commit, working directory clean
2.4 代码修改完毕后,合并到master分支
通过2.3我们改变了dev,改动完毕,这时候要合并代码到master。但在合并到master之前需要先更新master代码到dev。
目前通行且最佳的方案是采用rebase方案。rebase方案与merge的优劣我们不在这里讨论,请自行搜索。
简单理解就是:rebase使得提交路线更加清晰,而merge会因为“分支”过多,合并过多导致提交路线杂乱追踪起来比较难。但rebase会丢失很多提交细节,也不是完美合并。
- 总之:非常难单一的使用哪个就能解决所有问题!
- 先统一名称:上游、下游
- 如果
是从dev
创建出来的分支。那么,master就是dev的master
,dev就是master的上游
。下游
- 下游合并上游,要尽可能的保持提交路径简洁,那么采用"Rebase"
- 上游合并下游代码,要尽可能的保持提交细节,那么采用"merge"
想了解更多帮助理解,参考两篇文章
- 动画图解 Git 命令
- GIT使用rebase和merge的正确姿势
2.4.1 实际例子
- dev是从master创建的分支
$git branch dev $git checkout dev
- dev开发一段时间后,master已经被其它同事修改了一些内容。dev本身也做了一些提交。这时,需要更新master代码才能做dev的测试。这时候就是:“下游合并上游”。那么采用
rebase
$git rebase master
- dev开发完毕了,这时候需要合并到master。这时候就是:“上游合并下游”。那么采用
merge
$git checkout master $git merge dev
3. 将现有代码纳入管理
3.1 本地代码从未纳入过仓库,仓库也是空的
这种最简单,按照如下执行即可
- 设置邮箱,用户名
$git config --global user.name "smokelee" $git config --global user.email "[email protected]"
- 进入代码的根目录
//初始化本地代码仓库 $git init //将远程仓库主机命名为"origin",并与本地的master建立追踪关系 //origin 是远程主机`本地别名`,名字可以自定义的,不会影响到远端 $git remote add origin ssh://gi[email protected]:10022/lihao/gitlearn.git //把本地代码加入到代码追踪 $git add . //把本地代码提交到本地仓库 $git commit -m "Initial commit" //推送本地代码到远端主机“origin”的"master" //git push <远程主机名> <本地分支名>:<远程分支名> //远程分支被省略,如上则表示将本地分支推送到与之存在追踪关系的远程分支。一般本地与 //远程分支会同名 $git push -u origin master
这个要注意,第一次提交时采用了git push -u
,含义是同时设置本地分支“master”与远端的"origin/master",建立追踪关系。那么以后再-u
代码时,直接使用push
就可以了。git push
3.2 本地代码,某个提交错了,向撤回提交
用git reset 命令实现回退,但怎么回退是个学问。
- 直接扔掉回退间的代码是一种(Hard)
- 回退但保留指定回退间产生代码是一种(Soft)
- 本质是移动git的HEAD指向
3.2.1 先来硬的
何为硬?不管 暂存区
有没有追踪但未提交的文件,不管上次提交改了什么,通通丢弃。对!就是你理解的丢掉。
- 想撤回
本次提交的上1个提交到
$git reset --hard HEAD^
- 想撤回
本次提交的上N个提交到
$git reset --hard HEAD~N
- 撤回
某个提交到
$git reset --hard 98abc5a
- 误操作怎么办?reflog(30天内)
$git reflog b7057a9 HEAD@{0}: reset: moving to b7057a9 98abc5a HEAD@{1}: commit: more stuff added to foo
就是被98abc5a
掉的提交。硬
再次git log查看。丢掉的内容回来了!git reset --hard 98abc5a
$git log 98abc5a (HEAD, master) more stuff added to foo
3.2.2 再来软的
何为软?与硬不同的是,软只回退提交,但中间产生的文件不丢弃!
操作与hard一致,不做更多说明
4. 连接远程
4.1 从远程下载代码,注意:只会下载 master
代码
master
git clone ssh://xxxxxx/xxxx/prj.git
4.2 下载后切换到分支
- 查看所有分支
$git branch -a master remotes/origin/HEAD -> origin/master remotes/origin/dev remotes/origin/master
- 切换到
分支,并建立dev
与远程分支
关联本地分支
$git checkout -b dev origin/dev
- 更新远程分支到本地
如果没有走1、2两步,不要用3。其它后面会讲$git pull --rebase
4.3 本地建立分支,修改,并提交"远程分支"
前提条件
dev
已经与远程
origin/dev
建立关联。请看4.2
- 在
上建立feature分支并修改dev
$git branch feature<jira编号>-smokelee-20200423 $git checkout feature<jira编号>-smokelee-20200423 $touch newfeature.java $git add . && git commit -m "add feature"
- 该下班了,还没改完,提交到远程服务器(小变化无需上传)
$git push origin feature<jira编号>-smokelee-20200423
- 第二天修改完了,合并到
。先更新dev
dev
$git checkout dev $git pull --rebase
- 合并feature到dev
$git merge f1 $git push
- 合并成功,没有问题。删除远程分支
$git checkout feature<jira编号>-smokelee-20200423 $git push origin :feature<jira编号>-smokelee-20200423
注意5中的第二条命令冒号前是空的,复习:git push <远程主机名> <本地分支名>:<远程分支名> 本地分支名不设置,就表示删除远程分支
4.4 干拉一个分支到本地
不从任何建立
本地分支
分支。并且不建立
feature
,
本地
远程
分支关联。
要建立关联也简单,git pull后设置
git branch --set-upstream-to=origin/dev feaure-xxx-xxxx
这里纯粹在演示完整用法,就是字打的多点
- git pull origin <远程分支名>:<本地分支名>
$git pull origin master:feaure-xxx-xxxx //关联远程与本地分支 $git branch --set-upstream-to=origin/dev feaure-xxx-xxxx $git checkout feaure-xxx-xxxx
- 修改
并提交feaure-xxx-xxxx
$touch f1.java $git add . && git commit -m "dev1"
- 修改代码测试完毕准备提交到
远程分支
在关联了$git pull origin dev:feaure-xxx-xxxx --rebase $git push origin feaure-xxx-xxxx.local:dev
与远程
分支情况下语句简单多了。本地
$git pull --reabase $git push