2018/03/15

git常用指令和问题总结以及工作流方式


配置文件

设置用户名:
git config --global user.name "Your Name"

设置用户邮箱:
git config --global user.email you@example.com

以上设置完成后会在用户根目录下创建配置文件 ~/.gitconfig,这是针对用户的配置而已。如果需求针对全系统配置则将 --global 改为 --system ,这样配置信息会写入到/etc/gitconfig 中,如果需要针对某个项目来配置,则进入到项目目录下的.git/config 文件进行配置。

配置优先级:
项目下.git/config > 用户下~/.gitconfig > 全局配置下/etc/gitconfig


工作代码提交

创建项目创库:
git init

git的三个概率:
工作区(work directory) 当前工作代码目录
版本库(repository) .git目录就是版本库
暂存区(stage) 执行 git add 存放到暂存区

工作代码提交:
git的思想是所有的提交都认为是修改,添加一行代码是修改,删除一行代码是修改,添加一个文件和删除一个文件也属于修改,我们所有的修改需要提交到版本库时,都要进行两步
1、添加修改到暂存区 git add file1,file2
2、将暂存区的内容加入到版本库 git commit -m "描述"


常规操作

撤销修改

git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

撤销3种情况

场景1:
当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file

场景2:
当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,
第一步用命令git reset HEAD file,就回到了场景1
第二步按场景1操作

场景3:
已经提交了不合适的修改到版本库时,想要撤销本次提交,可以进行版本回退 git reset --hard xxx


删除文件

1、删除文件 git rm file1
2、将此删除的暂存区内容加入到版本库 git commit -m "描述"

查看版本库日志

git log ,如果日志显示过多,可以通过按行显示减少输出 git log --pretty=oneline

查看历史操作版本库命令

git reflog

更多的日志查看方法可参考:
https://git-scm.com/book/zh/v1/Git-%E5%9F%BA%E7%A1%80-%E6%9F%A5%E7%9C%8B%E6%8F%90%E4%BA%A4%E5%8E%86%E5%8F%B2


版本号HEAD

版本库里每个版本都有一个唯一的16进制的commit id,用HEAD表示当前版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^, 当然往上100个版本写100个^比较容易数不过来,所以可以写成HEAD~100

所以,回退版本的几个快捷方法是:
回退到当前版本 git reset --hard HEAD
回退到上一个版本 git reset --hard HEAD^
回退到指定commit id的版本 git reset --hard a80966


分支管理

创建分支: git branch dev
切换分支: git checkout dev
查看当前分支: git branch
查看所有分支:git branch -a
删除分支: git branch -d dev
创建+切换分支: git checkout -b dev
刷新本地远程分支列表: git remote update origin --prune

跟当前版本比对某个文件的差异:
git diff HEAD readme.md

当某个版本比对某个文件的差异:
git diff a80966 readme.md

合并分支 git merge dev ,将dev分支合并到当前分支上。
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,我们没法知道这些提交是属于合并过来的。 如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit:
git merge --no-ff -m "merge with no-ff" dev
因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。我们通过log查看提交情况
git log --graph --pretty=oneline --abbrev-commit


储藏 stash

当我们需要修复一个bug时,会进行创建一个分支来修复它,但是当前正在dev上进行了相关工作还没提交,而且工作只进行到一半,还没法提交,此时就需要stash功能,可以把当前工作现场“储藏”起来,等以后恢复到现场后继续工作。
进行git stash后再执行git status会发现工作环境非常干净

git stash list 查看“储藏”列表
git stash apply 恢复现场到最近一次 “储藏” 环境
git stash drop 删除最近一次 “储藏” 环境
git stash apply stash@{0} 恢复工作现场到 stash@{0} 环境
git stash drop stash@{0} 删除 “储藏” stash@{0} 环境
git stash pop 恢复最近一次“储藏”环境,同时把stash内容删除


远程分支

克隆远程分支:
git clone xxx
Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin

查看远程库信息 git remote
查看远程库详细信息 git remote -v

git查看本地分支关联(跟踪)的远程分支之间的对应关系,本地分支对应哪个远程分支:
git branch -vv

创建远程origin的dev分支到本地
git checkout -b dev origin/dev


拉取远程库分支代码到本地:
git pull

如果拉取失败,提示"There is no tracking information for the current branch." 当前分支没有跟踪信息,也就是说当前本地的分支没有与远程库分支做关联 将当前dev分支关联远程库dev分支:
git branch --set-upstream dev origin/dev
关联后即可重新 git pull

从本地推送分支:
git push origin branch-name
如果推送失败,先用git pull抓取远程的新提交,我们执行push到远程分支时,git会自动去判断当前本地哪个分支与它进行了对应关系,然后会将本地此分支推送上去。所以就算我们在dev分支上执行 git push origin master,它也会去哪本地master分支做比较推送,而不会哪本地dev分支进行推送


只合并部分commit --cherry-pick

提前dev分支部分提交内容到master分支上,有时我们不能全部都需要将dev合并到master,而是将部分内容合并过来,那么我们就需要采用到 cherry-pick

我们知道每一次提交生成的commit id都是全世界唯一的,所以就可以利用起来进行使用

git checkout dev
git log --graph --pretty=oneline --abbrev-commit
*   854c9bf 解决分支合并冲突
|\
| * f4887e7 readme描述改为test
* | 990608b 简化readme
|/
*   701933d 解决冲突
|\
| * 7ec8d0e 修改readme
* | dbfa662 readme描述修改
|/
* 89f71ac add msg value
* 7962c61 修改输出
* 8c2d35b 添加demo文件
* a809660 添加composer文件
* 69fe9cb Initial commit

git checkout master
git log --graph --pretty=oneline --abbrev-commit
* f3161d9 !1 添加composer Merge pull request !1 from yangxgkem/dev
* 69fe9cb Initial commit

当前工作区是master
将dev分支 8c2d35b 合并到master上 git cherry-pick 8c2d35b
将dev分支 8c2d35b 合并到master上,并保留原来的提交者信息 git cherry-pick -x 8c2d35b
将dev分支 7962c61 到 89f71ac 的版本,左开右闭方式,合并到master上 git cherry-pick 8c2d35b..89f71ac
将dev分支 7962c61 到 89f71ac 的版本,左闭右闭方式,合并到master上 git cherry-pick 8c2d35b^..89f71ac

参考文档:https://www.jianshu.com/p/08c3f1804b36


冲突解决

有两种情况出现冲突,第一种就是多人协作开发同一个分支出现冲突;第二种是分支合并时出现冲突

多人协作开发冲突解决

a和b都在dev分支上开发,a修改了 README.md 文件推送到了远程分支,而b也修改了 README.md 文件准备进行推送,此时就会出现冲突报错

[root@localhost test]# git push
Enter passphrase for key '/root/.ssh/id_rsa':
To git@gitee.com:yangxgkem/test.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@gitee.com:yangxgkem/test.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.
[root@localhost test]#

提示我们未能进行推送,需要先将远程版本合并后才能再推送。因为你的小伙伴b的最新提交和你试图推送的提交有冲突, 解决办法也很简单,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送。

[root@localhost test]# git push
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

提示我们合并冲突了,冲突的文件是 README.md,告诉我们修复冲突,然后提交结果。我们也可以通过git status查看冲突的文件。

打开 README.md

<<<<<<< HEAD
测试111111
=======
测试2222222
>>>>>>> 7ec8d0e5cf0c541f446bb99bb3b1cb7d9808b39b

<<<<<<< HEAD 为当前版本的内容,分割线为 ======= ,后面的是远程库版本的内容 Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存

测试111111

然后添加到暂存区,提交到版本库,推送到远程库

git add README.md
git commit -m "解决冲突"
git push

git log --graph --pretty=oneline --abbrev-commit
*   701933d 解决冲突
|\
| * 7ec8d0e 修改readme
* | dbfa662 readme描述修改
|/

合并冲突解决

我们在分支上开发完毕后,需要将分支合并到master上,在合并时发现出现了冲突,此冲突跟协同开发一个道理的,主要是因为master分支上已经有修改了相同内容,导致git不知如何处理,报出冲突让我们人为来解决

还原合并冲突场景
git checkout -b test dev
修改README.md为
测试test
提交到版本库,然后切换到dev分支 在dev下修改README.md为
测试123
提交到版本库,然后开始合并test分支到dev

[root@localhost test]# git merge --no-ff -m "合并test分支" test
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

报出了冲突,此处告诉我们先解决冲突,然后再提交。
打开 README.md

# test
<<<<<<< HEAD
测试123
=======
测试test
>>>>>>> test

我们修改如下后保存

测试123

然后添加到暂存区,提交到版本库,推送到远程库

git add README.md
git commit -m "解决分支合并冲突"

git log --graph --pretty=oneline --abbrev-commit
*   854c9bf 解决分支合并冲突
|\
| * f4887e7 readme描述改为test
* | 990608b 简化readme
|/

可以看到解决冲突方式跟协同开发解决冲突是相同的。


使用git进行工作流开发

1、创建git远程仓库

2、创建两个远程分支 masterdev,master用于线上版本发布,dev用于开发

3、开发者在本地克隆远程仓库后,我们开发的代码应该都是基于dev的,每开发一个功能都需要基于dev在本地创建新分支,开发完毕后再将新分支合并到dev

4、在dev完成测试通过后,将dev合并到master中,然后master就可以发布到线上

5、如果出现了bug,我们应该基于master在本地创建bug分支,然后进行修复,修复完毕后合并到master,然后采用cherry-pick将修复提交的内容合并到dev分支中,然后其它开发人员从 dev 拉最新的代码即可,整个流程大概如下

git checkout master // 切换到 master 分支
git checkout -b fix_888 <master last hash id> // 基于 master 分支的最新hash id 创建分支
// 此时 fix some code 并且提交
git checkout master
git merge fix_888
// master 代码修复结束了
git checkout develop
git cherry-pick <fix_888 commit hash id> // 将 fix_888 提交的代码用到 develop 分支
// 如果有冲突修复,没有直接 push
// 其它开发人员从 develop 拉最新的代码即可。

我们可以根据情况是否需要将bug分支提交到远程库中保存bug分支,这样有利于后面回顾时查看bug的修复历史情况。
修复bug参考文档:http://blog.csdn.net/treesky/article/details/50126387