Git 配置

查询配置信息

  1. 列出当前配置:git config --list;
  2. 列出repository配置:git config --local --list;
  3. 列出全局配置:git config --global --list;
  4. 列出系统配置:git config --system --list;

其他配置

  1. 配置解决冲突时使用哪种差异分析工具,比如要使用vimdiff:git config --global merge.tool vimdiff;
  2. 配置git命令输出为彩色的:git config --global color.ui auto;
  3. 配置git使用的文本编辑器:git config --global core.editor vi;

Git相关的配置文件:

  1. Git\etc\gitconfig

  2. C:\Users\ [用户名称] \ .gitconfig

设置用户名与邮箱(全局配置)

git config --global user.name "[name]"  
git config --global user.email [mail]

Git 基本理论

四个工作区域

  1. 工作区(Workspace):本地电脑存放项目文件的地方。
  2. 暂存区(Index/Stage):用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息。在使用git管理项目文件的时候,其本地的项目文件会多出一个.git的文件夹,将这个.git文件夹称之为版本库。其中.git文件夹中包含了两个部分,一个是暂存区(Index或者Stage),顾名思义就是暂时存放文件的地方,通常使用add命令将工作区的文件添加到暂存区里;
  3. 本地仓库(Repository):安全存放数据的位置,这里面有你提交到所有版本的数据。其中HEAD指向最新放入仓库的版本。即.git文件夹里还包括git自动创建的master分支,并且将HEAD指针指向master分支。使用commit命令可以将暂存区中的文件添加到本地仓库中;
  4. 远程仓库(Remote):将项目代码托管在远程git服务器上。

本地的三个区域确切的说应该是git仓库中HEAD指向的版本。

(stash为简化图)

Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git的管理空间。

WorkSpace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间。

.git:存放Git管理信息的目录,初始化仓库的时候自动创建。

Index/Stage:暂存区,或者叫待提交更新区,在提交进入repo之前,我们可以把所有的更新放在暂存区。

Local Repo:本地仓库,一个存放在本地的版本库;HEAD会只是当前的开发分支(branch)。

Stash:隐藏,是一个工作状态保存栈,用于保存/恢复WorkSpace中的临时状态。

工作流程

Git的工作流程一般是这样的:

1. 在工作目录中添加、修改文件;

2. 将需要进行版本管理的文件放入暂存区域;

3. 将暂存区域的文件提交到git仓库。

因此,git管理的文件有三种状态:已修改(modified),已暂存(staged),已提交(committed)
JeesWn.png

日常开发时代码实际上放置在工作区中,通过add等这些命令将代码文教提交给暂存区(Index/Stage),也就意味着代码全权交给了git进行管理,之后通过commit等命令将暂存区提交给master分支上,也就是意味打了一个版本,也可以说代码提交到了本地仓库中。另外,团队协作过程中自然而然还涉及到与远程仓库的交互。

Git 项目搭建

创建工作目录

工作目录(WorkSpace)一般就是你希望Git帮助你管理的文件夹,可以是你项目的目录,也可以是一个空目录,建议不要有中文。

本地仓库搭建

(创建本地仓库的方法有两种:一种是创建全新的仓库,另一种是克隆远程仓库。)

1.创建全新的仓库,需要用GIT管理的项目的根目录执行:

在当前目录新建一个Git代码库
git init

执行后可以看到,仅仅在项目目录多出了一个.git目录,关于版本等的所有信息都在这个目录里面。

2.克隆远程仓库

克隆远程目录,是将远程服务器上的仓库完全镜像一份至本地。

克隆一个项目和它的整个代码历史(版本信息)
git clone [url]

查看文件状态

查看指定文件状态
git status [filename]

查看所有文件状态
git status

版本控制就是对文件的版本控制,要对文件进行修改、提交等操作,首先要知道文件当前在什么状态,不然可能会提交了现在还不想提交的文件,或者要提交的文件没提交上。

  1. Untracked: 未跟踪, 此文件在文件夹中, 但并没有加入到git库, 不参与版本控制. 通过git add 状态变为Staged.
  2. Unmodify: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种类型的文件有两种去处, 如果它被修改, 而变为Modified. 如果使用git rm移出版本库, 则成为Untracked文件
  3. Modified: 文件已修改, 仅仅是修改, 并没有进行其他的操作. 这个文件也有两个去处, 通过git add可进入暂存staged状态, 使用git checkout 则丢弃修改过, 返回到unmodify状态, 这个git checkout即从库中取出文件, 覆盖当前修改 !
  4. Staged: 暂存状态. 执行git commit则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodify状态. 执行git reset HEAD filename取消暂存, 文件状态为Modified

比较工作区中当前文件和暂存区之间的差异,也就是修改之后还没有暂存的内容:git diff;指定文件在工作区和暂存区上差异比较:git diff ;

忽略文件

有些时候我们不想把某些文件纳入版本控制中,比如数据库文件,临时文件,设计文件等

在主目录下建立”.gitignore”文件,此文件有如下规则:

  1. 忽略文件中的空行或以井号(#)开始的行将会被忽略。
  2. 可以使用Linux通配符。例如:星号( * )代表任意多个字符,问号(?)代表一个字符,方括号([abc])代表可选字符范围,大括号({string1,string2,…})代表可选的字符串等。
  3. 如果名称的最前面有一个感叹号(!),表示例外规则,将不被忽略。
  4. 如果名称的最前面是一个路径分隔符(/),表示要忽略的文件在此目录下,而子目录中的文件不忽略。
  5. 如果名称的最后面是一个路径分隔符(/),表示要忽略的是此目录下该名称的子目录,而非文件(默认文件或目录都忽略)。
    #为注释

    *.txt #忽略所有 .txt结尾的文件,这样的话上传就不会被选中!

    !lib.txt #但lib.txt除外

    /temp #仅忽略项目根目录下的TODO文件,不包括其它目录temp

    build/ #忽略build/目录下的所有文件

    doc/*.txt #会忽略 doc/notes.txt 但不包括 doc/server/arch.txt

传输操作

git add . 提交到暂存区

提交工作区所有文件到暂存区:git add .
提交工作区中指定文件到暂存区:git add ...
提交工作区中某个文件夹中所有文件到暂存区:git add [dir]

git commit -m "消息内容" 向仓库提交代码

将暂存区中的文件提交到本地仓库中,即打上新版本:git commit -m "commit_info"
将所有已经使用git管理过的文件暂存后一并提交,跳过add到暂存区的过程:git commit -a -m "commit_info"
提交文件时,发现漏掉几个文件,或者注释写错了,可以撤销上一次提交:git commit --amend

git log 查看提交记录

比较暂存区与上一版本的差异:git diff --cached
指定文件在暂存区和本地仓库的不同:git diff --cached
查看提交历史:git log;参数-p展开每次提交的内容差异,用-2显示最近的两次更新,如git log -p -2

查看远程库信息,使用git remote -v

撤销

用暂存区中的文件覆盖工作目录中的文件:git checkout -- [文件名] 不加 -- [文件名]则覆盖全部文件。不加 --则变成了 切换到另一个分支 的命令

将文件从暂存区中删除:git rm --cached [文件名]

将git仓库中指定的更新记录恢复出来,并且覆盖暂存区和工作目录: git reset --hard [commitID]

撤销

  1. 删除工作区文件,并且也从暂存区删除对应文件的记录:git rm ;
  2. 从暂存区中删除文件,但是工作区依然还有该文件:git rm --cached ;
  3. 取消暂存区已经暂存的文件:git reset HEAD ...;
  4. 撤销上一次对文件的操作:git checkout --。要确定上一次对文件的修改不再需要,如果想保留上一次的修改以备以后继续工作,可以使用stashing和分支来处理;
  5. 隐藏当前变更,以便能够切换分支:git stash
  6. 查看当前所有的储藏:git stash list
  7. 应用最新的储藏:git stash apply,如果想应用更早的储藏:git stash apply stash@{2};重新应用被暂存的变更,需要加上--index参数:git stash apply --index;
  8. 使用apply命令只是应用储藏,而内容仍然还在栈上,需要移除指定的储藏:git stash drop stash{0};如果使用pop命令不仅可以重新应用储藏,还可以立刻从堆栈中清除:git stash pop;
  9. 在某些情况下,你可能想应用储藏的修改,在进行了一些其他的修改后,又要取消之前所应用储藏的修改。Git没有提供类似于 stash unapply 的命令,但是可以通过取消该储藏的补丁达到同样的效果:git stash show -p stash@{0} | git apply -R;同样的,如果你沒有指定具体的某个储藏,Git 会选择最近的储藏:git stash show -p | git apply -R

更新文件

  1. 重命名文件,并将已改名文件提交到暂存区:git mv [file-original] [file-renamed];

Git 远程仓库

在托管平台创建仓库

得到指令:git remote add origin git@github.com:[用户名]/[仓库名].git

远程库的名字就是origin,这是Git默认的叫法,也可以改成别的。仓库名与本地仓库名相同。

git push -u origin master
将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。

从现在起,只要本地作了提交,就可以通过git push origin master把本地master分支的最新修改推送至远程仓库。

git push origin将当前分支推送到origin主机的对应分支。
如果当前分支只有一个追踪分支,那么主机名都可以省略。

$ git push 上传本地所有分支代码到远容程对应的分支上。
如果当前分支与多个主机存在追踪关系,那么这个时候-u选项会指定一个默认主机,这样后面就可以不加任何参数使用git push。
不带任何参数的git push,默认只推送当前分支,这叫做simple方式。此外,还有一种matching方式,会推送所有有对应的远程分支的本地分支。Git 2.0版本之前,默认采用matching方法,现在改为默认采用simple方式。

git push 远程仓库地址 分支名称

git push 远程仓库地址别名 分支名称

git push -u 远程仓库地址别名 分支名称

-u 记住推送地址和分支,下次只需要输入git push

git remote add 远程仓库地址别名 远程仓库地址

删除别名: git remote remove 远程仓库地址别名

第一次提交需要用户名和密码,电脑会记住密码在凭据管理器,第二次就不用了。

我们可以为一个本地管理库添加多个远程仓库。用来做数据备份,或者上线仓库管理等操作。
比如,我们可以为项目 market 项目,创建多个版本的仓库。

  • develop 开发仓库
  • test 测试仓库
  • release 发布仓库
    对于不同的仓库,就可以进行不同的权限控制。
  • develop 开发仓库,可以是所有的操作人员都可以有修改的权限,可以操作
  • test 测试仓库,就只能是测试人员才能够查看到的,以及对其中的内容进行修改。
  • release 发布仓库,这个仓库就只能是运维,或者一些管理者才具有权限。这个仓库本身会存在一些配置上的不同。一般人是看不到的,达到了线上数据的安全控制,避免泄露。

当我们要进行版本发布的时候。就可以选择要将代码推送到不同的仓库。然后,针对不同的仓库,各自又能够在不同的地方做不同的事。

$ git remote add develop master
$ git remote add test master
$ git remote add release master

这样本地代码,就会关联上3 个不同的仓库。那么当我们提交代码的时候,具体要提交到哪个远程分支呢。这个时候,我们就需要在push 的时候明确远程分支。

$ git push develop master
$ git push test master
$ git push release master

大多数情况下,我们都是在一个远程分支上进行代码提交,只是偶尔有用到提交到不同的远程仓库 。所以有一个更简单的方式提交。那就是关联远程仓库分支跟本地的分支。使用git branch --set-upstream-to 命令。

$ git branch --set-upstream-to develop master 或者 git branch -u develop master

这样,后续只需要简单的使用 git push 或者 git pull 命令就可以了。

Git 分支

分支的主要作用是生成副本,避免影响开发主线。

  1. 主分支(master):第一次向git仓库提交更新记录时自动产生的一个分支。
  2. 开发分支(develop):作为开发的分支,基于master分支创建。
  3. 功能分支(feature):作为开发具体功能的分支基于开发分支创建。

分支命令

  • git branch 查看分支
  • git branch <name> 创建分支
  • git checkout <name> 切换分支
  • git checkout -b <name>或者git switch -c <name> 创建+切换分支
  • git merge <name> 合并指定分支到当前分支
  • git branch -d <name> 删除分支(分支合并后才允许被删除)(-D 大写强制删除)
    git push origin :branch-name : 远程仓库同步删除掉的分支

注意:开发分支文件后要 commit 后再切换主分支,否则分支文件会出现在主分支里面

最新版本的Git提供了新的git switch命令来切换分支:

创建并切换到新的dev分支,可以使用: git switch -c dev

直接切换到已有的master分支,可以使用: git switch master

分支管理

  1. 创建分支:git branch
  2. 从当前所处的分支切换到其他分支:git checkout
  3. 新建并切换到新建分支上:git checkout -b ;
  4. 删除分支:git branch -d
  5. 将当前分支与指定分支进行合并:git merge ;
  6. 显示本地仓库的所有分支:git branch;
  7. 查看各个分支最后一个提交对象的信息:git branch -v;
  8. 查看哪些分支已经合并到当前分支:git branch --merged;
  9. 查看当前哪些分支还没有合并到当前分支:git branch --no-merged;
  10. 把远程分支合并到当前分支:git merge如果是单线的历史分支不存在任何需要解决的分歧,只是简单的将HEAD指针前移,所以这种合并过程可以称为快进(Fast forward),而如果是历史分支是分叉的,会以当前分叉的两个分支作为两个祖先,创建新的提交对象;如果在合并分支时,遇到合并冲突需要人工解决后,再才能提交;
  11. 在远程分支的基础上创建新的本地分支:git checkout -b /,如git checkout -b (分支名) (远程仓库名)/(分支名);
  12. 从远程分支checkout出来的本地分支,称之为跟踪分支。在跟踪分支上向远程分支上推送内容:git push。该命令会自动判断应该向远程仓库中的哪个分支推送数据;在跟踪分支上合并远程分支:git pull
  13. 将一个分支里提交的改变移到基底分支上重放一遍:git rebase ,如git rebase master server,将特性分支server提交的改变在基底分支master上重演一遍;使用rebase操作最大的好处是像在单个分支上操作的,提交的修改历史也是一根线;如果想把基于一个特性分支上的另一个特性分支变基到其他分支上,可以使用--onto操作:git rebase --onto ,如git rebase --onto master server client;使用rebase操作应该遵循的原则是:一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行rebase操作

分支管理策略

通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。

如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

git merge --no-ff -m " " [name]强制禁用Fast forward模式。

以dev和master分支为例,不使用Fast forward模式,merge后就像这样:

实际开发分支管理基本原则:

  1. master用于发布新版本。
  2. dev用于开发。

团队合作就是每个人都在dev分支上干活,每个人都有自己的分支,并且时不时地往dev分支上合并。

暂时保存更改

git中可以不提交更改,只提取分支上所有改动并储存,让开发人员得到一个干净的副本,临时转向其它工作。复制到“剪切板”,可以“粘贴“到其它分支。

场景:

  • 储存临时改动:git stash
  • 恢复临时改动:git stash pop

分支BUG解决方案(点击查看)

分支合并冲突处理

两个分支各自都分别有新的提交时,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。

冲突产生时必须手动解决冲突后再提交,git status可以查看冲突的文件。

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

修改冲突内容再次提交,删除多余分支。

用带参数的git log可以查看分支的合并情况

Bug分支处理

每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。

如果在开发过程中发现之前的项目出现一个Bug,可以使用git stash临时存储目前正在开发的内容优先解决Bug。

解决bug后,使用用git stash list命令查看储存的工作内容。

可以用git stash apply恢复,但是恢复后,stash内容并没删除,需要额外使用用git stash drop来删除

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

如果当前的开发内容存在同样的BUG,可以用cherry-pick命令,复制一个特定的提交到当前分支。


git cherry-pick [commit id]

Git 标签

Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。

在Git中打标签非常简单,首先,切换到需要打标签的分支上,然后,敲命令git tag 就可以打一个新标签。

列出现在所有的标签可以用 git tag

默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,可以用git log --pretty=oneline --abbrev-commit找到历史提交的commit id,然后打上就可以了。
git tag [tagname] [commit id]

注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show [tagname]查看标签信息

删除标签用git tag -d [tagname]

推送标签到远程仓库git push [远程仓库名] [tagname]

如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除,然后,从远程删除。删除命令也是push,但是格式是这样的:git push origin :refs/tags/[tagname]

使用特定的搜索模式列出符合条件的标签,例如只对1.4.2系列的版本感兴趣:git tag -l "v1.4.2.*";
创建一个含附注类型的标签,需要加-a参数,如git tag -a v1.4 -m "my version 1.4";

如果有自己的私钥,可以使用GPG来签署标签,只需要在命令中使用-s参数:git tag -s v1.5 -m "my signed 1.5 tag";
验证已签署的标签:git tag -v ,如git tag -v v1.5;
创建一个轻量级标签的话,就直接使用git tag命令即可,连-a,-s以及-m选项都不需要,直接给出标签名字即可,如git tag v1.5;
将标签推送到远程仓库中:git push origin ,如git push origin v1.5
将本地所有的标签全部推送到远程仓库中:git push origin --tags;

相关知识

免密登录

设置本机绑定SSH公钥,可以实现免密码登录。

进入 C:\Users\ [用户名] \ .ssh 目录

生成公钥ssh-keygen

将公钥信息public key 添加到托管平台账户中即可!

远程分支

远程分支是对远程仓库中的分支的索引。它们是一些无法移动的本地分支;只有在GIT进行网络交互时才会更新。远程分支就是书签,提醒着你上次连接远程仓库是上面各分支的位置。

我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支。我们来举一个例子,假如你从GITHUB上克隆了一个项目到本地,GIT会自动为你将次远程仓库命名为origin,并且下载其中的所有数据,建立一个指向它的master分支的指针,在本地命名为origin/master,但是你无法在本地更改其数据,所以Git建立了一个属于你自己的本地的master分支,始于origin上master分支相同的位置,你可以就此开始工作。

注:一次 Git 克隆会建立你自己的本地分支 master 和远程分支 origin/master,并且将它们都指向 origin 上的 master 分支。

你可以沿着本地master分支做一些改动,但是只要你不和服务器通讯,你的 origin/master 指针仍然保持原位不会移动。