Git 常见问题

Git 设置代理来访问服务器

全局代理

1
2
3
4
5
6
7
# 设置代理
$ git config --global http.proxy http://127.0.0.1:1087
$ git config --global https.proxy https://127.0.0.1:1087

# 取消代理
$ git config --global --unset http.proxy
$ git config --global --unset https.proxy

设置项目代理

1
2
3
4
5
6
7
# 设置代理
$ git config --local http.proxy 127.0.0.1:1086
$ git config --local https.proxy 127.0.0.1:1086

# 取消代理
$ git config --local --unset http.proxy
$ git config --local --unset https.proxy

给指定的 URL 设置代理

1
2
3
4
5
6
[http "https://github.com/"]
proxy = http://127.0.0.1:1086
[https "https://github.com/"]
proxy = http://127.0.0.1:1086
[http "https://my.comapnyserver.com/"]
proxy = ""

Git 保留最新提交记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Checkout
git checkout --orphan latest_branch

# Add all the files
git add -A

# Commit the changes
git commit -am "commit message"

# Delete the branch

git branch -D master

# Rename the current branch to master

git branch -m master

# Finally, force update your repository

git push -f origin master

Git 保存请求的账号密码

https 方式每次都要输入密码,按照如下设置即可输入一次就可以很长时间不用再手输入密码。

第一步:设置邮箱和密码

1
2
$ git config --global user.email "your email"
$ git config --global user.name  "your username"

根据自己的需求执行下面的任意一条命令

第二步:

1
2
3
4
5
6
7
8
9
10
11
# 设置记住密码(默认15分钟):
$ git config --global credential.helper cache

# 如果想自己设置时间,可以这样做, 这样就设置一个小时之后失效
$ git config credential.helper 'cache --timeout=3600'

# 长期存储密码:
$ git config --global credential.helper store

# 增加远程地址的时候带上密码也是可以的。(推荐)
$ http://yourname:password@git.oschina.net/name/project.git

运行相关命令, 输入账号密码, 如果正确则下次不必重新输入, 在 git 目录中存在两个文件 .gitconfig, .git-credentials, 里边放置的是凭证的信息

Git 推送 github 代码出现 443: Operation timed out 问题

推送到 https://github.com/{username}/{project}.git
fatal: 无法访问 ‘https://github.com/...':Failed to connect to github.com port 443: Operation timed out
Completed with errors, see above

如果出现 raw.githubusercontent.com 之类的问题都可以采用此类方法解决

解决: hosts 中加入 ip 映射

查询真实 IP

通过 IPAddress.com, 输入 github.com 查询到真实 IP 地址, 然后修改 hosts 映射, 这里需要在本地 ping 下指定的查询出来的 ip , 否则也无法访问

删除本地所有的 Tag 并获取服务端所有

1
2
# 删除本地所有tag 并获取服务端所有
$ git tag -l | xargs git tag -d && git fetch --tags

git pull 冲突终极解决方案

服务端 git pull 或 git checkout 报以下错误

Your local changes to the following files would be overwritten by merge
error: Your local changes to the following files would be overwritten by merge:

解决方案:

1
2
$ git reset --hard HEAD
$ git clean -f -d

参考

Git-Flow 工作规范流程

当在团队开发中使用版本控制系统时,确定一个统一的工作流程是至关重要的。如果在团队中还没有能形成一个特定有效的工作流程,那么混乱就将是不可避免的。

什么是 git-flow?

一旦安装安装 git-flow,你将会拥有一些扩展命令。这些命令会在一个预定义的顺序下自动执行多个操作。是的,这就是我们的工作流程!

git-flow 并不是要替代 Git,它仅仅是非常聪明有效地把标准的 Git 命令用脚本组合了起来。

严格来讲,你并不需要安装什么特别的东西就可以使用 git-flow 工作流程。你只需要了解,哪些工作流程是由哪些单独的任务所组成的,并且附带上正确的参数,以及在一个正确的顺序下简单执行那些对应的 Git 命令就可以了。当然,如果你使用 git-flow 脚本就会更加方便了,你就不需要把这些命令和顺序都记在脑子里。

安装 git-flow

近些年来出现了很多不同的安装方法。在本章节中我们会使用当前最流行的一种: AVH Edition

要了解安装 git-flow 细节,请阅读下面这个文档  official documentation

在项目中设置 git-flow

当你想把你的项目 “切换” 到 git-flow 上后,Git 还是可以像往常一样工作的。这完全是取决于你在仓库上使用特殊的 git-flow 命令或是普通的 Git 命令。换句话说,git-flow 它不会以任何一种戏剧性的方式来改变你的仓库。

话虽如此,git-flow 却存在一些限制。让我们开始在一个新的项目上初始化它:

1
2
3
4
5
6
7
8
9
$ git flow init
Initialized empty Git repository in /Users/tobi/acme-website/.git/
Branch name for production releases: [master]
Branch name for "next release" development: [develop]

How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]

当在项目的根目录执行 “git flow init” 命令时(它是否已经包括了一个 Git 仓库并不重要),一个交互式安装助手将引导您完成这个初始化操作。听起来是不是有点炫,但实际上它只是在你的分支上配置了一些命名规则。
尽管如此,这个安装助手还是允许你使用自己喜欢的名字。我强烈建议你使用默认的命名机制,并且一步一步地确定下去。

分支的模式

git-flow 模式会预设两个主分支在仓库中:

  • master  只能用来包括产品代码。你不能直接工作在这个 master 分支上,而是在其他指定的,独立的特性分支中(这方面我们会马上谈到)。不直接提交改动到  master  分支上也是很多工作流程的一个共同的规则。
  • develop  是你进行任何新的开发的基础分支。当你开始一个新的功能分支时,它将是开发的基础。另外,该分支也汇集所有已经完成的功能,并等待被整合到  master  分支中。

这两个分支被称作为  长期分支。它们会存活在项目的整个生命周期中。而其他的分支,例如针对功能的分支,针对发行的分支,仅仅只是临时存在的。它们是根据需要来创建的,当它们完成了自己的任务之后就会被删除掉。

让我们开始探索一些在现实应用中可能遇到的案例吧!

功能开发 feature (功能分支)

对于一个开发人员来说,最平常的工作可能就是功能的开发。这就是为什么 git-flow 定义了很多对于功能开发的工作流程,从而来帮助你有组织地完成它。

开始新功能

让我们开始开发一个新功能 “rss-feed”:

1
2
3
4
5
6
$ git flow feature start rss-feed
Switched to a new branch 'feature/rss-feed'

Summary of actions:
- A new branch 'feature/rss-feed' was created, based on 'develop'
- You are now on branch 'feature/rss-feed'
概念

在这些命令的输出文本中,git-flow 会对刚刚完成的操作打印出一个很有帮助的概述

当你需要帮助的时候,你可以随时请求帮助。例如:

1
$ git flow feature help

正如上面这个新功能一样,git-flow 会创建一个名为 “feature/rss-feed” 的分支(这个 “feature/” 前缀 是一个可配置的选项设置)。你已经知道了,在你做新功能开发时使用一个独立的分支是版本控制中最重要的规则之一。
git-flow 也会直接签出这个新的分支,这样你就可以直接进行工作了。

完成一个功能

经过一段时间艰苦地工作和一系列的聪明提交,我们的新功能终于完成了:

1
2
3
4
5
6
7
8
$ git flow feature finish rss-feed
Switched to branch 'develop'
Updating 6bcf266..41748ad
Fast-forward
feed.xml | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 feed.xml
Deleted branch feature/rss-feed (was 41748ad).

最重要的是,这个 “feature finish” 命令会把我们的工作整合到主 “develop” 分支中去。在这里它需要等待:

  1. 一个在更广泛的 “开发” 背景下的全面测试。
  2. 稍后和所有积攒在 “develop” 分支中的其它功能一起进行发布。

之后,git-flow 也会进行清理操作。它会删除这个当下已经完成的功能分支,并且换到 “develop” 分支。

管理 releases (预发分支)

Release 管理是版本控制处理中的另外一个非常重要的话题。让我们来看看如何利用 git-flow 创建和发布 release。

创建 release

当你认为现在在 “develop” 分支的代码已经是一个成熟的 release 版本时,这意味着:第一,它包括所有新的功能和必要的修复;第二,它已经被彻底的测试过了。如果上述两点都满足,那就是时候开始生成一个新的 release 了:

1
2
$ git flow release start 1.1.5
Switched to a new branch 'release/1.1.5'

请注意,release 分支是使用版本号命名的。这是一个明智的选择,这个命名方案还有一个很好的附带功能,那就是当我们完成了 release 后,git-flow 会适当地自动去标记那些 release 提交。

有了一个 release 分支,再完成针对 release 版本号的最后准备工作(如果项目里的某些文件需要记录版本号),并且进行最后的编辑。

完成 release

现在是时候按下那个危险的红色按钮来完成我们的 release 了:

1
git flow release finish 1.1.5

这个命令会完成如下一系列的操作:

  1. 首先,git-flow 会拉取远程仓库,以确保目前是最新的版本。
  2. 然后,release 的内容会被合并到 “master” 和 “develop” 两个分支中去,这样不仅产品代码为最新的版本,而且新的功能分支也将基于最新代码。
  3. 为便于识别和做历史参考,release 提交会被标记上这个 release 的名字(在我们的例子里是 “1.1.5”)。
  4. 清理操作,版本分支会被删除,并且回到 “develop”。

从 Git 的角度来看,release 版本现在已经完成。依据你的设置,对 “master” 的提交可能已经触发了你所定义的部署流程,或者你可以通过手动部署,来让你的软件产品进入你的用户手中。

hotfix (补丁分支)

很多时候,仅仅在几个小时或几天之后,当对 release 版本作做全面测试时,可能就会发现一些小错误。
在这种情况下,git-flow 提供一个特定的 “hotfix” 工作流程(因为在这里不管使用 “功能” 分支流程,还是 “release” 分支流程都是不恰当的)。

创建 Hotfixes

1
$ git flow hotfix start missing-link

这个命令会创建一个名为 “hotfix/missing-link” 的分支。因为这是对产品代码进行修复,所以这个 hotfix 分支是基于 “master” 分支。
这也是和 release 分支最明显的区别,release 分支都是基于 “develop” 分支的。因为你不应该在一个还不完全稳定的开发分支上对产品代码进行地修复。

就像 release 一样,修复这个错误当然也会直接影响到项目的版本号!

完成 Hotfixes

在把我们的修复提交到 hotfix 分支之后,就该去完成它了:

1
$ git flow hotfix finish missing-link

这个过程非常类似于发布一个 release 版本:

  • 完成的改动会被合并到 “master” 中,同样也会合并到 “develop” 分支中,这样就可以确保这个错误不会再次出现在下一个 release 中。
  • 这个 hotfix 程序将被标记起来以便于参考。
  • 这个 hotfix 分支将被删除,然后切换到 “develop” 分支上去。

还是和产生 release 的流程一样,现在需要编译和部署你的产品(如果这些操作不是自动被触发的话)。

回顾

最后,在结束前,要再次强调几个重点
首先,git-flow 并不会为 Git 扩展任何新的功能,它仅仅使用了脚本来捆绑了一系列 Git 命令来完成一些特定的工作流程。
其次,定义一个固定的工作流程会使得团队协作更加简单容易。无论是一个 “版本控制的新手” 还是 “Git 专家”,每一个人都知道如何来正确地完成某个任务。

一个成功的 Git 分支模型

原文地址: 介绍一个成功的 Git 分支模型
英文原文地址: A successful Git branching model

在这篇文章中,我提出一个开发模型。我已经将这个开发模型引入到我所有的项目里(无论在工作还是私人)已经一年有余,并且它被证明是非常成功的。我打算写这些已经很久了,但我一直找不到时间来做,现在终于有时间了。我不会讲任何项目的具体细节,仅是关于分支策略和释放管理相关内容。

它主要体现了 Git 对我们源代码版本的管理。

为何是 Git?

对于 Git 与其他集中式代码管理工具相比的优缺点的全面讨论,请参见这里。这样的争论总是喋喋不休。作为一个开发者,与现今的其他开发工具相比较,我更喜欢 Git。Git 真得改变了开发者对于合并和分支的思考。我曾经使用经典的 CVS/Subversion,然而每次的合并/分支和其他行为总让人担惊受怕(“小心合并里的冲突,简直要命!”)。

但是对于 Git 来说,这些行为非常简单和搞笑,它们被认为是日常工作中的核心部分。例如,在很多 CVS/Subversion里,分支与合并总是在后面的章节中被讨论(对于高级用户使用),然而在每个 Git中,在第 3 章就已经完全涵盖了(作为基础)。

简单和重复的特性带来的结果是:分支与合并不再是什么可以害怕的东西。分支/合并被认为对于版本管理工具比其他功能更重要。

关于工具,不再多说,让我们直接看开发模型吧。这个模型并不是如下模型:在管理软件开发进度方面,面对每个开发过程,每个队员必须按一定次序开发。

分布式而非集中式

对于这种分支模型,我们设置了一个版本库,它运转良好,这是一个”事实上” 版本库。不过请注意,这个版本库只是被认为是中心版本库(因为 Git 是一个分布式版本管理系统,从技术上来讲,并没有一个中心版本库)。我们将把这个版本库称为原始库,这个名字对所有的 Git 用户来说都很容易理解。

每个开发者都对 origin 库拉代码和提交代码。但是除了集中式的存取代码关系,每个开发者也可以从子团队的其他队友那里获得代码版本变更。例如,对于 2 个或多个开发者一起完成的大版本变更,为了防止过早地向 origin 库提交工作内容,这种机制就变得非常有用。在上述途中,有如下子团队:Alice 和 Bob,Alice 和 David,Clair 和 David。

从技术上将,这意味着,Alice 创建了一个 Git 的远程节点,而对于 Bob,该节点指向了 Bob 的版本库,反之亦然。

主分支

在核心部分,研发模型很大程度上靠其他现有模型支撑的。中心库有 2 个可一直延续的分支:

  • master 分支
  • develop 分支

每个 Git 用户都要熟悉原始的 master 分支。与 master 分支并行的另一个分支,我们称之为 develop 分支。

我们把原始库/master 库认作为主分支,HEAD 的源代码存在于此版本中,并且随时都是一个 预备 生产 状态。

我们把 origin/develop 库认为是主分支,该分支 HEAD 源码始终体现下个发布版的最新软件变更。有人称这个为“集成分支”,而这是每晚自动构建得来的。

当 develop 分支的源码到达了一个稳定状态待发布,所有的代码变更需要以某种方式合并到 master 分支,然后标记一个版本号。如何操作将在稍后详细介绍。

所以,每次变更都合并到了 master,这就是新产品的定义。在这一点,我们倾向于严格执行这一点,从而,理论上,每当对 master 有一个提交操作,我们就可以使用 Git 钩子脚本来自动构建并且发布软件到生产服务器。

辅助性分支

我们的开发模型使用了各种辅助性分支,这些分支与关键分支(master 和 develop)一起,用来支持团队成员们并行开发,使得易于追踪功能,协助生产发布环境准备,以及快速修复实时在线问题。与关键分支不同,这些分支总是有一个有限的生命期,因为他们最终会被移除。

我们用到的分支类型包括:

  • 功能分支
  • 发布分支
  • bug 修复(hotfix)分支

每一种分支有一个特定目的,并且受限于严格到规则,比如:可以用哪些分支作为源分支,哪些分支能作为合并目标。我们马上将进行演练。

从技术角度来看,这些分支绝不是特殊分支。分支的类型基于我们使用的方法来进行分类。它们理所当然是普通的 Git 分支。

功能分支

可能是 develop 分支的分支版本,最终必须合并到 develop 分支中。

分支命名规则:除了masterdeveloprelease-*hotfix-*之外,其他命名均可。

功能分支(有时被称为 topic 分支)通常为即将发布或者未来发布版开发新的功能。当新功能开始研发,包含该功能的发布版本在这个还是无法确定发布时间的。功能版本的实质是只要这个功能处于开发状态它就会存在,但是最终会或合并到 develop 分支(确定将新功能添加到不久的发布版中)或取消(譬如一次令人失望的测试)。

功能分支通常存在于开发者的软件库,而不是在源代码库中。

创建一个功能分支

开始一项功能的开发工作时,基于 develop 创建分支。

1
2
$ git checkout -b myfeature develop
Switched to a new branch` `"myfeature"

合并一个功能到 develop 分支

完成的功能可以合并进 develop 分支,以明确加入到未来的发布:

1
2
3
4
5
6
7
8
9
10
11
$ git checkout develop
Switched to branch` `'develop'

$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)

$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).

$ git push origin develop

–no-ff 标志导致合并操作创建一个新 commit 对象,即使该合并操作可以 fast-forward。这避免了丢失这个功能分支存在的历史信息,将该功能的所有提交组合在一起。 比较:

后一种情况,不可能从 Git 历史中看到哪些提交一起实现了一个功能——你必须手工阅读全部的日志信息。如果对整个功能进行回退  (比如一组提交),后一种方式会是一种真正头痛的问题,而使用–no-ffflag 的情况则很容易.

是的,它会创建一个新的(空)提交对象,但是收益远大于开销。

不幸的是,我还没找到一种方法,让–no-ff 时作为合并操作的默认选项,但它应该是可行的。

Release 分支

Release 分支可能从 develop 分支分离而来,但是一定要合并到 develop 和 master 分支上,它的习惯命名方式为:release-*。

Release 分支是为新产品的发布做准备的。它允许我们在最后时刻做一些细小的修改。他们允许小 bugs 的修改和准备发布元数据(版本号,开发时间等等)。当在 Release 分支完成这些所有工作以后,对于下一次打的发布,develop 分支接收 features 会更加明确。

从 develop 分支创建新的 Release 分支的关键时刻是 develop 分支达到了发布的理想状态。至少所有这次要发布的 features 必须在这个点及时合并到 develop 分支。对于所有未来准备发布的 features 必须等到 Release 分支创建以后再合并。

在 Release 分支创建的时候要为即将发行版本分配一个版本号,一点都不早。直到那时,develop 分支反映的变化都是为了下一个发行版,但是在 Release 分支创建之前,下一个发行版到底叫 0.3 还是 1.0 是不明确的。这个决定是在 Release 分支创建时根据项目在版本号上的规则制定的。

创建一个 release 分支

Release 分支是从 develop 分支创建的。例如,当前产品的发行版本号为 1.1.5,同时我们有一个大的版本即将发行。develop 分支已经为下次发行做好了准备,我们得决定下一个版本是 1.2(而不是 1.1.6 或者 2.0)。所以我们将 Release 分支分离出来,给一个能够反映新版本号的分支名。

1
2
3
4
5
6
7
8
9
$ git checkout -b release-1.2 develop
Switched to a new branch` `"release-1.2"

$ .``/bump-version``.sh 1.2
Files modified successfully, version bumped to 1.2.

$ git commit -a -m` `"Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2
1 files changed, 1 insertions(+), 1 deletions(-)

创建新分支以后,切换到该分支,添加版本号。这里,bump-version.sh 是一个虚构的 shell 脚本,它可以复制一些文件来反映新的版本(这当然可以手动改变–目的就是修改一些文件)。然后版本号被提交。

这个新分支可能会存在一段时间,直到该发行版到达它的预定目标。在此期间,bug 的修复可能被提交到该分支上(而不是提交到 develop 分支上)。在这里严格禁止增加大的新 features。他们必须合并到 develop 分支上,然后等待下一次大的发行版。

完成一个 release 分支

当一个 release 分支准备好成为一个真正的发行版的时候,有一些工作必须完成。首先,release 分支要合并到 master 上(因为每一次提交到 master 上的都是一个新定义的发行版,记住)。然后,提交到 master 上必须打一个标签,以便以后更加方便的引用这个历史版本。最后,在 release 分支上的修改必须合并到 develop 分支上,以便未来发行版也包含这些 bugs 的修复。

在 Git 中的前两步是:

1
2
3
4
5
6
7
8
$ git checkout master
Switched to branch` `'master'

$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)

$ git tag -a 1.2

发行版现在已经完成,为以后引用打上标签。

编辑:你可能也想使用 the-sor-u flags 来标记你的标签。

为了是修改保持在 release 分支上,我们需要合并这些到 develop 分支上去,在 Git 上:

1
2
3
4
5
6
$ git checkout develop
Switched to branch` `'develop'

$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)

这个步骤可能会导致合并冲突(可能由于改变版本号更是如此)。如果是这样,修复它然后提交。

现在我们真正的完成了,这个 release 分支将被删除,因为我们不再需要它了。

1
2
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).

bug 修复分支

可以基于 master 分支,必须合并回 develop 和 master 分支。
分支名约定:hotfix-*

热修复分支与发布分支很相似,他们都为新的生成环境发布做准备,尽管这是未经计划的。他们来自生产环境的处于异常状态压力。当生成环境验证缺陷必须马上修复是,热修复分支可以基于 master 分支上对应与线上版本的 tag 创建。

其本质是团队成员(在 develop 分支上)的工作可以继续,而另一个人准备生产环境的快速修复。

创建修补 bug 分支

hotfix branch(修补 bug 分支)是从 Master 分支上面分出来的。例如,1.2 版本是当前生产环境的版本并且有 bug。但是开发分支(develop)变化还不稳定。我们需要分出来一个修补 bug 分支(hotfix branch)来解决这种情况。

1
2
3
4
5
6
7
8
9
10
$ git checkout -b hotfix-1.2.1 master
Switched to a new branch` `"hotfix-1.2.1"

$ .``/bump-version``.sh 1.2.1
Files modified successfully, version bumped to 1.2.1.

$ git commit -a -m` `"Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1

1 files changed, 1 insertions(+), 1 deletions(-)

分支关闭的时侯不要忘了更新版本号(bump the version)

然后,修复 bug,一次提交或者多次分开提交。

1
2
3
4
$ git commit -m` `"Fixed severe production problem"

[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)

完成一个 hotfix 分支

完成一个 bugfix 之后,需要把 butfix 合并到 master 和 develop 分支去,这样就可以保证修复的这个 bug 也包含到下一个发行版中。这一点和完成 release 分支很相似。

首先,更新 master 并对 release 打上 tag:

1
2
3
4
5
6
7
8
$ git checkout master
Switched to branch 'master'

$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)

$ git tag -a 1.2.1

编辑:你可能也会想使用 -sor-u 参数来对你的 tag 进行加密

下一步,把 bugfix 添加到 develop 分支中:

1
2
3
4
5
6
$ git checkout develop
Switched to branch 'develop'

$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)

规则的一个例外是: 如果一个 release 分支已经存在,那么应该把 hotfix 合并到这个 release 分支,而不是合并到 develop 分支。当 release 分支完成后, 将 bugfix 分支合并回 release 分支也会使得 bugfix 被合并到 develop 分支。(如果在 develop 分支的工作急需这个 bugfix,等不到 release 分支的完成,那你也可以把 bugfix 合并到 develop 分支)

最后,删除临时分支:

1
2
$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).

摘要

尽管这个分支模型没有任何震撼的新东西, 文章开头的图表在我们的项目中表现出惊人的实用性。它形成了一个优雅的思维模型,易于领悟并使团队成员发展出对分支和发布过程的共同理解。

这里提供一份高质量 PDF 格式图表。去吧,把它挂载墙上以便能随时快速参考。