Git本地结构

git

Workspace: 工作区,实际操作的目录。

Index / Stage: 暂存区,数据暂时存放的区域,可在工作区和版本库之间进行数据的友好交流。

Repository: 仓库区(或本地仓库)工作区有一个隐藏目录.git,这个不属于工作区,这是版本库。版本库里面存了很多东西,其中最重要的就是暂存区,还有Git为我们自动创建了第一个分支master,以及指向master的一个指针HEAD。

Remote: 远程仓库,github上的仓库或其他服务上的仓库。

Git远程代码库

Git 与 Github 远程代码库,进行操作。

团队协作:

git-github

跨团队协作:

git-github

Git设置签名

设置签名的主要作用是区分不同开发人员的身份 ,需要注意的是,这里设置的签名和登录远程库(Github)的账号与密码没有任何关系。

签名级别

如果两个级别同时存在,那么依照就近原则,仓库级别要大于系统级别。如果两个级别都不存在,则无法完成 Git 一些命令,会提示验证身份。

仓库级别:仅在当前项目库范围生效

  • git config user.name [user_name]
  • git config user.email [user_email]

用户签名信息保存位置:./.git/config

系统级别:登录当前操作系统的用户范围生效

  • git config - -global user.name [user_name]
  • git config - -global user.email [user_email]

用户签名信息保存位置:~/.gitconfig

查看签名信息

  • git config - -list:显示当前的Git配置
  • git config -e [- -global]:编辑Git配置文件

Git命令

常用的Git命令

Command Desc Command Desc
add 添加文件内容至索引 merge 合并两个或更多开发历史
bisect 通过二分查找定位引入 bug 的变更 init 创建一个空的 Git 版本库或重新初始化一个已存在的版本库
branch 列出、创建或删除分支 mv 移动或重命名一个文件、目录或符号链接
checkout 检出一个分支或路径到工作区 pull 获取并合并另外的版本库或一个本地分支
clone 克隆一个版本库到一个新目录 push 更新远程引用和相关的对象
commit 记录/提交变更到版本库 rebase 本地提交转移至更新后的上游分支中
config 本地配置信息查看 reset 重置当前HEAD到指定状态
diff 显示提交之间、提交和工作区之间等的差异 tag 创建、列出、删除或校验一个GPG签名的 tag 对象
fetch 从另外一个版本库下载对象和引用 show 显示各种类型的对象
grep 输出和模式匹配的行 status 显示工作区状态
log 显示提交日志 rm 从工作区和索引中删除文件

命令解析

初始化:

  • git init:初始化本地Git仓库,产生.git目录,对该目录不要进行操作

增加和删除文件:

  • git add [file1] [file2]…:添加指定文件到暂存区
  • git add [dir]:添加指定目录到暂存区,包括子目录
  • git add . / git add -A:添加当前目录的所有文件到暂存区
  • git add -p:添加每个变化前,都会要求确认,对于同一个文件的多处变化,可以实现分次提交
  • git rm [file1] [file2]…:删除工作区文件,并且将这次删除放入暂存区
  • git rm - - cached [filename]:将文件从暂存区删除
  • git mv [file-original] [file-renamed]:改名文件,并且将这个改名放入暂存区

代码提交

  • git add . + git commit -m “xxx”:提交修改
  • git commit -m “message” [filename]:提交暂存区到仓库
  • git commit [file1] [file2] … -m “message”:提交暂存区的指定文件到仓库区
  • git commit -a:提交工作区自上次commit之后的变化,直接到仓库区
  • git commit -v:提交时显示所有diff信息
  • git commit - -amend [file1] [file2]:重做上一次commit,并包括指定文件的新变化

查看信息

  • git status:查看工作区是否还有文件未提交
  • git diff [file_name]:显示暂存区和工作区的差异
  • git diff head [file_name]:显示工作区和版本库差异
  • git diff - -cached:显示暂存区和版本库差异
  • git diff file.xxx:查看文件的修改内容
  • git log:查看版本历史(git log命令显示从最近到最远的显示日志)
  • git log - -pretty=oneline:查看简短历史
  • git log - -oneline:每条记录显示在一行,更加简洁,缩短hash值,只显示过去的记录
  • git reflog:查看所有产生的版本号
  • git log - -stat:显示commit历史,以及每次commit发生变更的文件
  • git log -S [keyword]:根据关键词搜索提交历史
  • git diff [first-branch]…[second-branch]:显示两次提交之间的差异

git_log

head的本质不是指向分支,而是指向commit提交。

head 通过先指向分支的头指针,再指向提交的意义就是表明当前所处的分支。

当 head 指针直接指向提交时,就会导致 detached head 状态。在这个状态下,如果创建了新提交,新提交不属于任何分支。相对应的,现存的所有分支也不会受 detached head 状态提交的影响。

进入detached head:git checkout <commit id> git checkout --detach

  • HEAD : 指向当前正在操作的commit
  • ORIG_HEAD : 记录危险操作之前的HEAD,方便HEAD的恢复,修改前的备份
  • FETCH_HEAD : 记录从远程仓库拉取的记录
  • MERGE_HEAD : git merge时,记录正在合并到你的分支中的提交
  • CHERRY_PICK_HEAD : 记录在运行git cherry-pick时要合并的提交

Cherry-pick

  • git cherry-pick : 选择将现有的一个或者多个提交的修改引入当前内容

  • git merge : 将两个提交历史合并,操作对象是commit history

  • git cherry-pick : 将提交对应的内容合并,操作对象是commi、commit代表的是修改

版本回退

git reset - -hard [hash_version]:回退到对应的版本号,推荐使用

git reset - -hard HEAD^:回退到上个版本,使用^

git reset - -hard HEAD^^:回退到上上个版本

git reset - -hard HEAD~n:回退到第n个版本,使用~

rest 三个参数:

  • –soft :仅在本地库移动HEAD指针
  • –mixed :在本地移动HEAD指针,重置暂存区
  • –hard:在本地移动HEAD指针,重置暂存区,重置工作区

撤销修改:git checkout [file],撤销修改回到添加暂存区后的状态

恢复某个commit : git checkout [commit] [file]

恢复暂存区的所有文件到工作区 : git checkout .

删除文件:rm file.xxx 彻底删除需要执行commit提交。未commit之前,使用 git checkout – file.xxx在版本库中恢复文件

删除尚未提交版本库:git reset --hard HEAD

永久删除文件找回:可使用版本回退方式找回文件。删除前,文件存在时的状态已经提交到本地库。

分支

主要分支

  • master : origin/master 分支上的最新代码永远是版本发布状态
  • dev : 最新的开发进度,当dev上的代码达到一个稳定的状态,可以发布版本的时候,会以某种特别方式被合并到master分支上,然后标记上对应的版本标签
  • Feature : 用来做分模块功能开发,模块完成之后,会合并到dev分支,然后删除自己
  • Release : 用来做版本发布的预发布分支,测试中出现的小问题,在release分支进行修改提交,测试完毕准备发布的时候,代码会合并到master和dev,然后删除自己
  • Hotfix : 用来做线上的紧急bug修复的分支,当线上某个版本出现了问题,将检出对应版本的代码,创建Hotfix分支,问题修复后,合并回master和dev,然后删除自己

分支操作命令

  • git branch [-v]:查看所有本地分支,-v 详细信息
  • git branch -r:列出所有远程分支 :
  • git branch -a:列出所有分支
  • git branch [branch_name]:新建一个本地分支,创建之后并不会自动切换到新创建的分支,依然停留在当前分支
  • git branch –track [branch] [re-branch]:新建一个分支,与指定的远程分支建立追踪关系
  • git checkout [branch_name]:切换分支
  • git checkout - :切换到上一个分支
  • git checkout –b [branch_name]:创建+切换分支
  • git merge [branch_name]:将指定的 branch 分支与当前分支合并
  • git branch –d [branch_name]:删除分支
  • git push origin –delete [branch_name] / git branch -dr [remote/branch] :删除远程分支

冲突解决

Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容。

  • 其中<<<<<< HEAD是指主分支修改的内容;
  • ======= 分割冲突信息;
  • >>>>>> branch_name 是当前提交的分支上修改的内容;

当解决完冲突后,使用git add file > > git commit -m “msg” 完成代码合并merge。

分支管理策略: 通常合并分支时,git一般使用”Fast forward”模式,在这种模式下,删除分支后,会丢掉分支信息,现在我们来使用带参数 –no-ff 来禁用”Fast forward”模式。

分支策略: 首先master主分支应该是非常稳定的,也就是用来发布新版本,一般情况下不允许在上面干活,干活一般情况下在新建的dev分支上干活,干完后,比如上要发布,或者说dev分支代码稳定后可以合并到主分支master上来

Bug分支: 在Git中,分支是很强大的,每个bug都可以通过一个临时分支来修复,修复完成后,合并分支,然后将临时的分支删除掉。

stash 功能

Git还提供了一个stash功能,可以把当前工作现场 ”隐藏起来”,等以后恢复现场后继续工作.比如我现在是在主分支master上来修复的,现在我要在master分支上创建一个临时分支issue-404.修复完成后,切换到master分支上,并完成合并,最后删除issue-404分支。工作区是干净的,那么我们工作现场去哪里呢?我们可以使用命令 git stash list来查看下. 需要恢复一下,可以使用如下2个方法:

  1. git stash apply恢复,恢复后,stash内容并不删除,你需要使用命令git stash drop来删除。

  2. 另一种方式是使用git stash pop,恢复的同时把stash内容也删除了。

标签

标签指令

  • git tag:列出所有tag

  • git tag [tag]:新建一个tag在当前commit

  • git tag [tag] [commit]:新建一个tag在指定commit

  • git tag -d [tag]:删除本地tag

  • git push origin :refs/tags/[tagname]:删除远程tag

  • git show [tag]:查看tag信息

  • git push [remote] [tag]:提交指定tag

  • git push [remote] –tags:提交所有tag

  • git checkout -b [branch] [tag]:新建一个分支,指向某个tag

远程分支同步

远程同步指令

  • git remote add [origin] [git_url]:加一个新的远程仓库,并命名origin
  • git remote –v:查看远程库的详细信息
  • git remote show [origin]:查看某个远程库的信息
  • git fetch [origin]:下载远程仓库的所有变动
  • git fetch [origin] [master(Remote)]:下载远程仓库指定分支的所有变动
  • git merge [origin/master]:将远程仓库对应的master 分支合并到本地分支
  • git pull [origin] [master(Remote)]:取回远程仓库的变化,并与本地分支合并
  • git push -u [origin] [master(Local)]:上传本地指定分支到远程仓库
  • git push [origin] - -all:推送所有分支到远程仓库
  • git push [origin] - -force:强行推送当前分支到远程仓库,即使有冲突
  • git clone [git_url] [loc_url]:克隆远程库到指定文件夹

若想把本地的某个分支 test 提交到远程仓库,并作为远程仓库的 master 分支;或者作为另外一个名叫 test 的分支,可以使用以下方法:

  • git push [origin] test:test :提交本地 test 分支作为远程的 test 分支
  • git push [origin] test:master :提交本地 test 分支作为远程的 master 分支

免密登录: 只允许配置一个用户

  • ssh-keygen -t rsa -C [email]:会在~目录下的.ssh文件生产两个文件,id_rsa、id_rsa.pub(公钥).

  • 将公钥添加到Github的SSH keys

GitFlow

Git工作流

多人协作工作模式

一般场景: 首先,可以试图用git push origin [branch-name]推送自己的修改.

  • 如果推送失败,则因为远程分支比你的本地更新早,需要先用git pull试图合并。
  • 如果合并有冲突,则需要解决冲突,并在本地提交。再用git push origin branch-name推送.

推送分支:

推送分支就是把该分支上所有本地提交到远程库中,推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:

使用命令 git push origin master,master分支是主分支,因此要时刻与远程同步。一些修复bug分支不需要推送到远程去,可以先合并到主分支上,然后把主分支master推送到远程去。

抓取分支: 多人协作时,大家都会往master分支上推送各自的修改。

使用命令创建本地dev分支:git checkout –b dev origin/dev,现在可以在dev分支上做开发了,开发完成后把dev分支推送到远程库时。 如果别人已经向origin/dev分支上推送了提交,而我在我的目录文件下也对同样的文件同个地方作了修改,也试图推送到远程库时,会推送失败,因为他人最新提交的和我试图推送的有冲突。解决的办法也很简单,上面已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后在本地合并,解决冲突,再推送。

  • 指定本地dev分支与远程origin/dev分支的链接:git branch --set-upstream-to=origin/dev.git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的 解决冲突完全一样。解决后,提交,再push到远程库里面去。

跨团队合作:

  • 远程团队Fork仓库内容到其仓库
  • 远程团队Clone仓库到其本地
  • 远程团队对其本地仓库进行修改操作
  • 远程团队将修改好的内容提交到其远程仓库
  • pull request到管理团队审核人,审核人对pull request进行审核
  • 审核通过,merge pull request,将代码合并到该团队的远程仓库
  • 本地团队可将远程仓库代码拉取到本地进行操作

Git Submodule

Submodule

git Submodule 是一个很好的多项目使用共同类库的工具,他允许类库项目做为 repository, 子项目做为一个单独的 git项目存在父项目中,子项目可以有自己的独立的 commitpushpull。而父项目以 Submodule 的形式包含子项目,父项目可以指定子项目 header,父项目中会的提交信息包含 Submodule 的信息,在 clone父项目的时候可以把 Submodule 初始化。

  • 在当前文件夹添加Submodule

    • git submodule add [子项目git地址]
    • 会在文件夹下生成 .gitmodules文件,包含Submodule主要消息,指定repository
  • 修改Submodule,需要有Commit权限

    • 打开需要操作的文件夹,git status查看状态
    • git commit -a -m ‘modify submodule’:提交更改的内容
    • git push:push到子项目远程服务器
  • 在父目录中查看状态会带有(new commits)标识,将变动信息同步到父项目的远程服务器

    • git commit -m ‘modify submodule’
    • git push:push到父项目远程服务器
  • 更新submodule

    • (1) 打开相应的submodule目录:git pull
    • (2) 在父目录下直接运行:git submodule foreach git pull
  • clone submodule

    • (1) 递归clone整个项目:git clone [项目git地址] –recursive

    • (2) 先clone父项目,再初始化Submodule: git clone [项目git地址] 》 cd [submodule_目录] 》 git submodule init > git submodule update

  • 删除submodule

    • git rm [submodule name]

不同开发平台管理

保持专注性

  • 对语言进行包的忽略

  • 对平台配置文件进行忽略

  • 对编译成的文件进行忽略