标签 Github 下的文章

编者按:本文不代表本站观点,而且微软收购 GitHub 的后果目前尚未显现出来,本文只是提供了一个选择。

正如你们之前听到的那样,微软收购了 GitHub。这对 GitHub 的未来意味着什么尚不清楚,但 Gitlab 的人认为微软的最终目标是将 GitHub 整合到他们的 Azure 帝国。对我来说,这很有道理。

尽管我仍然不情愿地将 GitHub 用于某些项目,但我前一段时间将所有个人仓库迁移到了 Gitlab 中。现在是时候让你做同样的事情,并抛弃 GitHub。

有些人可能认为微软的收购没有问题,但对我来说,这是压垮骆驼的最后一根稻草。几年来,微软一直在开展一场关于他们如何热爱 Linux 的大型营销活动,并突然决定用它所有的形式拥抱自由软件。更像 MS BS 给我​​的。

让我们花点时间提醒自己:

  • Windows 仍然是一个巨大的专有怪物,数十亿人每天都在丧失他们的隐私和权利。
  • 微软公司(曾经)以传播自由软件的“危害”闻名,以防止政府和学校放弃 Windows,转而支持 FOSS。
  • 为了确保他们的垄断地位,微软通过向全世界的小学颁发“免费”许可证来吸引孩子使用 Windows。毒品经销商使用相同的策略并提供免费样品来获取新客户。
  • 微软的 Azure 平台 - 即使它可以运行 Linux 虚拟机 - 它仍然是一个巨大的专有管理程序。

我知道移动 git 仓库看起来像是一件痛苦的事情,但是 Gitlab 的员工正在趁着人们离开 GitHub 的浪潮,通过提供 GitHub 导入器使迁移变得容易。

如果你不想使用 Gitlab 的主实例(gitlab.org),下面是另外两个你可以用于自由软件项目的备选实例:

朋友不要再让人使用 Github 了。


via: https://veronneau.org/lets-migrate-away-from-github.html

作者:Louis-Philippe Véronneau 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

编程语言对软件质量的影响是什么?这个问题在很长一段时间内成为一个引起了大量辩论的主题。在这项研究中,我们从 GitHub 上收集了大量的数据(728 个项目,6300 万行源代码,29000 位作者,150 万个提交,17 种编程语言),尝试在这个问题上提供一些实证。这个还算比较大的样本数量允许我们去使用一个混合的方法,结合多种可视化的回归模型和文本分析,去研究语言特性的影响,比如,在软件质量上,静态与动态类型和允许混淆与不允许混淆的类型。通过从不同的方法作三角测量研究(LCTT 译注:一种测量研究的方法),并且去控制引起混淆的因素,比如,团队大小、项目大小和项目历史,我们的报告显示,语言设计确实(对很多方面)有很大的影响,但是,在软件质量方面,语言的影响是非常有限的。最明显的似乎是,不允许混淆的类型比允许混淆的类型要稍微好一些,并且,在函数式语言中,静态类型也比动态类型好一些。值得注意的是,这些由语言设计所引起的轻微影响,绝大多数是由过程因素所主导的,比如,项目大小、团队大小和提交数量。但是,我们需要提示读者,即便是这些不起眼的轻微影响,也是由其它的无形的过程因素所造成的,例如,对某些函数类型、以及不允许类型混淆的静态语言的偏爱。

1 序言

在给定的编程语言是否是“适合这个工作的正确工具”的讨论期间,紧接着又发生了多种辩论。虽然一些辩论出现了带有宗教般狂热的色彩,但是大部分人都一致认为,编程语言的选择能够对编码过程和由此生成的结果都有影响。

主张强静态类型的人,倾向于认为静态方法能够在早期捕获到缺陷;他们认为,一点点的预防胜过大量的矫正。动态类型拥护者主张,保守的静态类型检查无论怎样都是非常浪费开发者资源的,并且,最好是依赖强动态类型检查来捕获错误类型。然而,这些辩论,大多数都是“纸上谈兵”,只靠“传说中”的证据去支持。

这些“传说”也许并不是没有道理的;考虑到影响软件工程结果的大量其它因素,获取这种经验性的证据支持是一项极具挑战性的任务,比如,代码质量、语言特征,以及应用领域。比如软件质量,考虑到它有大量的众所周知的影响因素,比如,代码数量, 6 团队大小, 2 和年龄/熟练程度。 9

受控实验是检验语言选择在面对如此令人气馁的混淆影响时的一种方法,然而,由于成本的原因,这种研究通常会引入一种它们自己的混淆,也就是说,限制了范围。在这种研究中,完整的任务是必须要受限制的,并且不能去模拟 真实的世界 中的开发。这里有几个最近的这种大学本科生使用的研究,或者,通过一个实验因素去比较静态或动态类型的语言。 7 12 15

幸运的是,现在我们可以基于大量的真实世界中的软件项目去研究这些问题。GitHub 包含了多种语言的大量的项目,并且在大小、年龄、和开发者数量上有很大的差别。每个项目的仓库都提供一个详细的记录,包含贡献历史、项目大小、作者身份以及缺陷修复。然后,我们使用多种工具去研究语言特性对缺陷发生的影响。对我们的研究方法的最佳描述应该是“混合方法”,或者是三角测量法; 5 我们使用文本分析、聚簇和可视化去证实和支持量化回归研究的结果。这个以经验为根据的方法,帮助我们去了解编程语言对软件质量的具体影响,因为,他们是被开发者非正式使用的。

2 方法

我们的方法是软件工程中典型的大范围观察研究法。我们首先大量的使用自动化方法,从几种数据源采集数据。然后使用预构建的统计分析模型对数据进行过滤和清洗。过滤器的选择是由一系列的因素共同驱动的,这些因素包括我们研究的问题的本质、数据质量和认为最适合这项统计分析研究的数据。尤其是,GitHub 包含了由大量的编程语言所写的非常多的项目。对于这项研究,我们花费大量的精力专注于收集那些用大多数的主流编程语言写的流行项目的数据。我们选择合适的方法来评估计数数据上的影响因素。

2.1 数据收集

我们选择了 GitHub 上的排名前 19 的编程语言。剔除了 CSS、Shell 脚本、和 Vim 脚本,因为它们不是通用的编程语言。我们包含了 Typescript,它是 JavaScript 的超集。然后,对每个被研究的编程语言,我们检索出以它为主要编程语言的前 50 个项目。我们总共分析了 17 种不同的语言,共计 850 个项目。

我们的编程语言和项目的数据是从 GitHub Archive 中提取的,这是一个记录所有活跃的公共 GitHub 项目的数据库。它记录了 18 种不同的 GitHub 事件,包括新提交、fork 事件、PR(拉取请求)、开发者信息和以每小时为基础的所有开源 GitHub 项目的问题跟踪。打包后的数据上传到 Google BigQuery 提供的交互式数据分析接口上。

识别编程语言排名榜单

我们基于它们的主要编程语言分类合计项目。然后,我们选择大多数的项目进行进一步分析,如 表 1 所示。一个项目可能使用多种编程语言;将它确定成单一的编程语言是很困难的。Github Archive 保存的信息是从 GitHub Linguist 上采集的,它使用项目仓库中源文件的扩展名来确定项目的发布语言是什么。源文件中使用数量最多的编程语言被确定为这个项目的 主要编程语言

t1.jpg

表 1 每个编程语言排名前三的项目

检索流行的项目

对于每个选定的编程语言,我们先根据项目所使用的主要编程语言来选出项目,然后根据每个项目的相关 的数量排出项目的流行度。 的数量表示了有多少人主动表达对这个项目感兴趣,并且它是流行度的一个合适的代表指标。因此,在 C 语言中排名前三的项目是 linux、git、php-src;而对于 C++,它们则是 node-webkit、phantomjs、mongo ;对于 Java,它们则是 storm、elasticsearch、ActionBarSherlock 。每个编程语言,我们各选了 50 个项目。

为确保每个项目有足够长的开发历史,我们剔除了少于 28 个提交的项目(28 是候选项目的第一个四分位值数)。这样我们还剩下 728 个项目。表 1 展示了每个编程语言的前三个项目。

检索项目演进历史

对于 728 个项目中的每一个项目,我们下载了它们的非合并提交、提交记录、作者数据、作者使用 git 的名字。我们从每个文件的添加和删除的行数中计算代码改动和每个提交的修改文件数量。我们以每个提交中修改的文件的扩展名所代表的编程语言,来检索出所使用的编程语言(一个提交可能有多个编程语言标签)。对于每个提交,我们通过它的提交日期减去这个项目的第一个提交的日期,来计算它的 提交年龄 。我们也计算其它的项目相关的统计数据,包括项目的最大提交年龄和开发者总数,用于我们的回归分析模型的控制变量,它在第三节中会讨论到。我们通过在提交记录中搜索与错误相关的关键字,比如,errorbugfixissuemistakeincorrectfaultdefectflaw,来识别 bug 修复提交。这一点与以前的研究类似。 18

表 2 汇总了我们的数据集。因为一个项目可能使用多个编程语言,表的第二列展示了使用某种编程语言的项目的总数量。我们进一步排除了项目中该编程语言少于 20 个提交的那些编程语言。因为 20 是每个编程语言的每个项目的提交总数的第一个四分位值。例如,我们在 C 语言中共找到 220 项目的提交数量多于 20 个。这确保了每个“编程语言 – 项目”对有足够的活跃度。

t2.jpg

表 2 研究主题

总而言之,我们研究了最近 18 年以来,用了 17 种编程语言开发的,总共 728 个项目。总共包括了 29,000 个不同的开发者,157 万个提交,和 564,625 个 bug 修复提交。

2.2 语言分类

我们基于影响语言质量的几种编程语言特性定义了语言类别, 7 8 12 ,如 表 3 所示。

编程范式 Programming Paradigm 表示项目是以命令方式、脚本方式、还是函数语言所写的。在本文的下面部分,我们分别使用 命令 procedural 脚本 scripting 这两个术语去代表命令方式和脚本方式。

t3.jpg

表 3. 语言分类的不同类型

类型检查 Type Checking 代表静态或者动态类型。在静态类型语言中,在编译时进行类型检查,并且变量名是绑定到一个值和一个类型的。另外,(包含变量的)表达式是根据运行时,它们可能产生的值所符合的类型来分类的。在动态类型语言中,类型检查发生在运行时。因此,在动态类型语言中,它可能出现在同一个程序中,一个变量名可能会绑定到不同类型的对象上的情形。

隐式类型转换 Implicit Type Conversion 允许一个类型为 T1 的操作数,作为另一个不同的类型 T2 来访问,而无需进行显式的类型转换。这样的隐式类型转换在一些情况下可能会带来类型混淆,尤其是当它表示一个明确的 T1 类型的操作数时,把它再作为另一个不同的 T2 类型的情况下。因为,并不是所有的隐式类型转换都会立即出现问题,通过我们识别出的允许进行隐式类型转换的所有编程语言中,可能发生隐式类型转换混淆的例子来展示我们的定义。例如,在像 Perl、 JavaScript、CoffeeScript 这样的编程语言中,一个字符和一个数字相加是允许的(比如,"5" + 2 结果是 "52")。但是在 Php 中,相同的操作,结果是 7。像这种操作在一些编程语言中是不允许的,比如 Java 和 Python,因为,它们不允许隐式转换。在强数据类型的 C 和 C++ 中,这种操作的结果是不可预料的,例如,int x; float y; y=3.5; x=y;是合法的 C 代码,并且对于 xy 其结果是不同的值,具体是哪一个值,取决于含义,这可能在后面会产生问题。 a Objective-C 中,数据类型 id 是一个通用对象指针,它可以被用于任何数据类型的对象,而不管分类是什么。 b 像这种通用数据类型提供了很好的灵活性,它可能导致隐式的类型转换,并且也会出现不可预料的结果。 c 因此,我们根据它的编译器是否 允许 或者 不允许 如上所述的隐式类型转换,对编程语言进行分类;而不允许隐式类型转换的编程语言,会显式检测类型混淆,并报告类型不匹配的错误。

不允许隐式类型转换的编程语言,使用一个类型判断算法,比如,Hindley 10 和 Milner, 17 或者,在运行时上使用一个动态类型检查器,可以在一个编译器(比如,使用 Java)中判断静态类型的结果。相比之下,一个类型混淆可能会悄无声息地发生,因为,它可能因为没有检测到,也可能是没有报告出来。无论是哪种方式,允许隐式类型转换在提供了灵活性的同时,最终也可能会出现很难确定原因的错误。为了简单起见,我们将用 隐含 implicit 代表允许隐式类型转换的编程语言,而不允许隐式类型转换的语言,我们用 明确 explicit 代表。

内存分类 Memory Class 表示是否要求开发者去管理内存。尽管 Objective-C 遵循了一个混合模式,我们仍将它放在非管理的分类中来对待,因为,我们在它的代码库中观察到很多的内存错误,在第 3 节的 RQ4 中会讨论到。

请注意,我们之所以使用这种方式对编程语言来分类和研究,是因为,这种方式在一个“真实的世界”中被大量的开发人员非正式使用。例如,TypeScript 被有意地分到静态编程语言的分类中,它不允许隐式类型转换。然而,在实践中,我们注意到,开发者经常(有 50% 的变量,并且跨 TypeScript —— 在我们的数据集中使用的项目)使用 any 类型,这是一个笼统的联合类型,并且,因此在实践中,TypeScript 允许动态地、隐式类型转换。为减少混淆,我们从我们的编程语言分类和相关的模型中排除了 TypeScript(查看 表 37)。

2.3 识别项目领域

我们基于编程语言的特性和功能,使用一个自动加手动的混合技术,将研究的项目分类到不同的领域。在 GitHub 上,项目使用 project descriptionsREADME 文件来描述它们的特性。我们使用一种文档主题生成模型(Latent Dirichlet Allocation,缩写为:LDA) 3 去分析这些文本。提供一组文档给它,LDA 将生成不同的关键字,然后来识别可能的主题。对于每个文档,LDA 也估算每个主题分配的文档的概率。

我们检测到 30 个不同的领域(换句话说,就是主题),并且评估了每个项目从属于每个领域的概率。因为,这些自动检测的领域包含了几个具体项目的关键字,例如,facebook,很难去界定它的底层的常用功能。为了给每个领域分配一个有意义的名字,我们手动检查了 30 个与项目名字无关的用于识别领域的领域识别关键字。我们手动重命名了所有的 30 个自动检测的领域,并且找出了以下六个领域的大多数的项目:应用程序、数据库、代码分析、中间件、库,和框架。我们也找出了不符合以上任何一个领域的一些项目,因此,我们把这个领域笼统地标记为 其它 。随后,我们研究组的另一名成员检查和确认了这种项目领域分类的方式。表 4 汇总了这个过程识别到的领域结果。

t4.jpg

表 4 领域特征

2.4 bug 分类

尽管修复软件 bug 时,开发者经常会在提交日志中留下关于这个 bug 的原始的重要信息;例如,为什么会产生 bug,以及怎么去修复 bug。我们利用很多信息去分类 bug,与 Tan 的 et al 类似。 13 24

首先,我们基于 bug 的 原因 Cause 影响 Impact 进行分类。\_ 原因 \_ 进一步分解为不相关的错误子类:算法方面的、并发方面的、内存方面的、普通编程错误,和未知的。bug 的 影响 也分成四个不相关的子类:安全、性能、失败、和其它的未知类。因此,每个 bug 修复提交也包含原因和影响的类型。表 5 展示了描述的每个 bug 分类。这个类别分别在两个阶段中被执行:

t5.jpg

表 5 bug 分类和在整个数据集中的描述

(1) 关键字搜索 我们随机选择了 10% 的 bug 修复信息,并且使用一个基于关键字的搜索技术去对它们进行自动化分类,作为可能的 bug 类型。我们对这两种类型(原因和影响)分别使用这个注释。我们选择了一个限定的关键字和习惯用语集,如 表 5 所展示的。像这种限定的关键字和习惯用语集可以帮我们降低误报。

(2) 监督分类 我们使用前面步骤中的有注释的 bug 修复日志作为训练数据,为监督学习分类技术,通过测试数据来矫正,去对剩余的 bug 修复信息进行分类。我们首先转换每个 bug 修复信息为一个词袋(LCTT 译注:bag-of-words,一种信息检索模型)。然后,删除在所有的 bug 修复信息中仅出现过一次的词。这样减少了具体项目的关键字。我们也使用标准的自然语言处理技术来解决这个问题。最终,我们使用支持向量机(LCTT 译注:Support Vector Machine,缩写为 SVM,在机器学习领域中,一种有监督的学习算法)去对测试数据进行分类。

为精确评估 bug 分类器,我们手动注释了 180 个随机选择的 bug 修复,平均分布在所有的分类中。然后,我们比较手动注释的数据集在自动分类器中的结果。最终处理后的,表现出的精确度是可接受的,性能方面的精确度最低,是 70%,并发错误方面的精确度最高,是 100%,平均是 84%。再次运行,精确度从低到高是 69% 到 91%,平均精确度还是 84%。

我们的 bug 分类的结果展示在 表 5 中。大多数缺陷的原因都与普通编程错误相关。这个结果并不意外,因为,在这个分类中涉及了大量的编程错误,比如,类型错误、输入错误、编写错误、等等。我们的技术并不能将在任何(原因或影响)分类中占比为 1.4% 的 bug 修复信息再次进行分类;我们将它归类为未知。

2.5 统计方法

我们使用回归模型对软件项目相关的其它因素中的有缺陷的提交数量进行了建模。所有的模型使用 负二项回归 negative binomial regression (缩写为 NBR)(LCTT 译注:一种回归分析模型) 去对项目属性计数进行建模,比如,提交数量。NBR 是一个广义的线性模型,用于对非负整数进行响应建模。 4

在我们的模型中,我们对每个项目的编程语言,控制几个可能影响最终结果的因素。因此,在我们的回归分析中,每个(语言/项目)对是一个行,并且可以视为来自流行的开源项目中的样本。我们依据变量计数进行对象转换,以使变量保持稳定,并且提升了模型的适用度。 4 我们通过使用 AIC 和 Vuong 对非嵌套模型的测试比较来验证它们。

去检查那些过度的多重共线性(LCTT 译注:多重共线性是指,在线性回归模型中解释变量之间由于存在精确相关关系或高度相关关系而使模型估计失真或难以估计准确。)并不是一个问题,我们在所有的模型中使用一个保守的最大值 5,去计算每个依赖的变量的膨胀因子的方差。 4 我们通过对每个模型的残差和杠杆图进行视觉检查来移除高杠杆点,找出库克距离(LCTT 译注:一个统计学术语,用于诊断回归分析中是否存在异常数据)的分离值和最大值。

我们利用 效果 ,或者 差异 ,编码到我们的研究中,以提高编程语言回归系数的表现。 4 加权的效果代码允许我们将每种编程语言与所有编程语言的效果进行比较,同时弥补了跨项目使用编程语言的不均匀性。 23 去测试两种变量因素之间的联系,我们使用一个独立的卡方检验(LCTT 译注:Chi-square,一种统计学上的假设检验方法)测试。 14 在证实一个依赖之后,我们使用 Cramer 的 V,它是与一个 r × c 等价的正常数据的 phi(φ) 系数,去建立一个效果数据。

3 结果

我们从简单明了的问题开始,它非常直接地解决了人们坚信的一些核心问题,即:

问题 1:一些编程语言相比其它语言来说更易于出现缺陷吗?

我们使用了回归分析模型,去比较每个编程语言对所有编程语言缺陷数量平均值的影响,以及对缺陷修复提交的影响(查看 表 6)。

t6.jpg

表 6. 一些语言的缺陷要少于其它语言

我们包括了一些变量,作为对明确影响反应的控制因子。项目 年龄 age 也包括在内,因为,越老的项目生成的缺陷修复数量越大。 提交 commits 数量也会对项目反应有轻微的影响。另外,从事该项目的 开发人员 dev 的数量和项目的原始 大小 size ,都会随着项目的活跃而增长。

上述模型中估算系数的大小和符号(LCTT 译注:指 “+”或者“-”)与结果的预测因子有关。初始的四种变量是控制变量,并且,我们对这些变量对最终结果的影响不感兴趣,只是说它们都是积极的和有意义的。语言变量是指示变量,是每个项目的变化因子,该因子将每种编程语言与所有项目的编程语言的加权平均值进行比较。编程语言系数可以大体上分为三类。第一类是,那些在统计学上无关紧要的系数,并且在建模过程中这些系数不能从 0 中区分出来。这些编程语言的表现与平均值相似,或者它们也可能有更大的方差。剩余的系数是非常明显的,要么是正的,要么是负的。对于那些正的系数,我们猜测可能与这个编程语言有大量的缺陷修复相关。这些语言包括 C、C++、Objective-C、Php,以及 Python。所有的有一个负的系数的编程语言,比如 Clojure、Haskell、Ruby,和 Scala,暗示这些语言的缺陷修复提交可能小于平均值。

应该注意的是,虽然,从统计学的角度观察到编程语言与缺陷之间有明显的联系,但是,大家不要过高估计编程语言对于缺陷的影响,因为,这种影响效应是非常小的。异常分析的结果显示,这种影响小于总异常的 1%。

ut1.jpg

我们可以这样去理解模型的系数,它代表一个预测因子在所有其它预测因子保持不变的情况下,这个预测因子一个 单位 unit 的变化,所反应出的预期的响应的对数变化;换句话说,对于一个系数 β i ,在 β i 中一个单位的变化,产生一个预期的 e β i 响应的变化。对于可变因子,这个预期的变化是与所有编程语言的平均值进行比较。因此,如果对于一定数量的提交,用一个处于平均值的编程语言开发的特定项目有四个缺陷提交,那么,如果选择使用 C++ 来开发,意味着我们预计应该有一个额外的(LCTT 译注:相对于平均值 4,多 1 个)缺陷提交,因为 e 0.18 × 4 = 4.79。对于相同的项目,如果选择使用 Haskell 来开发,意味着我们预计应该少一个(LCTT 译注:同上,相对于平均值 4)缺陷提交。因为, e −0.26 × 4 = 3.08。预测的精确度取决于剩余的其它因子都保持不变,除了那些微不足道的项目之外,所有的这些都是一个极具挑战性的命题。所有观察性研究都面临类似的局限性;我们将在第 5 节中详细解决这些事情。

结论 1:一些编程语言相比其它编程语言有更高的缺陷相关度,不过,影响非常小。

在这篇文章的剩余部分,我们会在基本结论的基础上详细阐述,通过考虑不同种类的应用程序、缺陷、和编程语言,可以进一步深入了解编程语言和缺陷倾向之间的关系。

软件 bug 通常落进两种宽泛的分类中:

  1. 特定领域的 bug :特定于项目功能,并且不依赖于底层编程语言。
  2. 普通 bug :大多数的普通 bug 是天生的,并且与项目功能无关,比如,输入错误,并发错误、等等。

因此,在一个项目中,应用程序领域和编程语言相互作用可能会影响缺陷的数量,这一结论被认为是合理的。因为一些编程语言被认为在一些任务上相比其它语言表现更突出,例如,C 对于低级别的(底层)工作,或者,Java 对于用户应用程序,对于编程语言的一个不合适的选择,可能会带来更多的缺陷。为研究这种情况,我们将理想化地忽略领域特定的 bug,因为,普通 bug 更依赖于编程语言的特性。但是,因为一个领域特定的 bug 也可能出现在一个普通的编程错误中,这两者是很难区分的。一个可能的变通办法是在控制领域的同时去研究编程语言。从统计的角度来看,虽然,使用 17 种编程语言跨 7 个领域,在给定的样本数量中,理解大量的术语将是一个极大的挑战。

鉴于这种情况,我们首先考虑在一个项目中测试领域和编程语言使用之间的依赖关系,独立使用一个 卡方检验 Chi-square 测试。在 119 个单元中,是 46 个,也就是说是 39%,它在我们设定的保守值 5 以上,它太高了。这个数字不能超过 20%,应该低于 5。 14 我们在这里包含了完整有值; d 但是,通过 Cramer 的 V 测试的值是 0.191,是低相关度的,表明任何编程语言和领域之间的相关度是非常小的,并且,在回归模型中包含领域并不会产生有意义的结果。

去解决这种情况的一个选择是,去移除编程语言,或者混合领域,但是,我们现有的数据没有进行完全挑选。或者,我们混合编程语言;这个选择导致一个相关但略有不同的问题。

问题 2: 哪些编程语言特性与缺陷相关?

我们按编程语言类别聚合它们,而不是考虑单独的编程语言,正如在第 2.2 节所描述的那样,然后去分析与缺陷的关系。总体上说,这些属性中的每一个都将编程语言按照在上下文中经常讨论的错误、用户辩论驱动、或者按以前工作主题来划分的。因此,单独的属性是高度相关的,我们创建了六个模型因子,将所有的单独因子综合到我们的研究中。然后,我们对六个不同的因子对缺陷数量的影响进行建模,同时控制我们在 问题 1 节中使用的模型中的相同的基本协变量(LCTT 译注:协变量是指在实验中不能被人为操纵的独立变量)。

关于使用的编程语言(在前面的 表 6中),我们使用跨所有语言类的平均反应来比较编程语言 。这个模型在 表 7 中表达了出来。很明显,Script-Dynamic-Explicit-Managed 类有最小的量级系数。这个系数是微不足道的,换句话说,对这个系数的 Z 校验 z-test (LCTT 译注:统计学上的一种平均值差异校验的方法) 并不能把它从 0 中区分出来。鉴于标准错误的量级,我们可以假设,在这个类别中的编程语言的行为是非常接近于所有编程语言行为的平均值。我们可以通过使用 Proc-Static-Implicit-Unmanaged 作为基本级并用于处理,或者使用基本级来虚假编码比较每个语言类,来证明这一点。在这种情况下,Script-Dynamic-Explicit-Managed 是明显不同于 p = 0.00044 的。注意,虽然我们在这是选择了不同的编码方法,影响了系数和 Z 值,这个方法和所有其它的方面都是一样的。当我们改变了编码,我们调整系数去反应我们希望生成的对比。 4 将其它类的编程语言与总体平均数进行比较,Proc-Static-Implicit-Unmanaged 类编程语言更容易引起缺陷。这意味着与其它过程类编程语言相比,隐式类型转换或者管理内存会导致更多的缺陷倾向。

t7.jpg

表 7. 函数式语言与缺陷的关联度和其它类语言相比要低,而过程类语言则大于或接近于平均值。

在脚本类编程语言中,我们观察到类似于允许与不允许隐式类型转换的编程语言之间的关系,它们提供的一些证据表明,隐式类型转换(与显式类型转换相比)才是导致这种差异的原因,而不是内存管理。鉴于各种因素之间的相关性,我们并不能得出这个结论。但是,当它们与平均值进行比较时,作为一个组,那些不允许隐式类型转换的编程语言出现错误的倾向更低一些,而那些出现错误倾向更高的编程语言,出现错误的机率则相对更高。在函数式编程语言中静态和动态类型之间的差异也很明显。

函数式语言作为一个组展示了与平均值的很明显的差异。静态类型语言的系数要小很多,但是函数式语言类都有同样的标准错误。函数式静态编程语言出现错误的倾向要小于函数式动态编程语言,这是一个强有力的证据,尽管如此,Z 校验仅仅是检验系数是否能从 0 中区分出来。为了强化这个推论,我们使用处理编码,重新编码了上面的模型,并且观察到,Functional-Static-Explicit-Managed 编程语言类的错误倾向是明显小于 Functional-Dynamic-Explicit-Managed 编程语言类的 p = 0.034。

ut2.jpg

与编程语言和缺陷一样,编程语言类与缺陷之间关系的影响是非常小的。解释类编程语言的这种差异也是相似的,虽然很小,解释类编程语言的这种差异小于 1%。

我们现在重新回到应用领域这个问题。应用领域是否与语言类相互影响?怎么选择?例如,一个函数化编程语言,对特定的领域有一定的优势?与上面一样,对于这些因素和项目领域之间的关系做一个卡方检验,它的值是 99.05, df = 30, p = 2.622e–09,我们拒绝无意义假设,Cramer 的 V 产生的值是 0.133,表示一个弱关联。因此,虽然领域和编程语言之间有一些关联,但在这里应用领域和编程语言类之间仅仅是一个非常弱的关联。

结论 2:在编程语言类与缺陷之间有一个很小但是很明显的关系。函数式语言与过程式或者脚本式语言相比,缺陷要少。

这个结论有些不太令人满意的地方,因为,我们并没有一个强有力的证据去证明,在一个项目中编程语言或者语言类和应用领域之间的关联性。一个替代方法是,基于全部的编程语言和应用领域,忽略项目和缺陷总数,而去查看相同的数据。因为,这样不再产生不相关的样本,我们没有从统计学的角度去尝试分析它,而是使用一个描述式的、基于可视化的方法。

我们定义了 缺陷倾向 Defect Proneness 作为 bug 修复提交与每语言每领域总提交的比率。图 1 使用了一个热力图展示了应用领域与编程语言之间的相互作用,从亮到暗表示缺陷倾向在增加。我们研究了哪些编程语言因素影响了跨多种语言写的项目的缺陷修复提交。它引出了下面的研究问题:

f1.jpg

图 1. 编程语言的缺陷倾向与应用领域之间的相互作用。对于一个给定的领域(列底部),热力图中的每个格子表示了一个编程语言的缺陷倾向(行头部)。“整体”列表示一个编程语言基于所有领域的缺陷倾向。用白色十字线标记的格子代表一个 null 值,换句话说,就是在那个格子里没有符合的提交。

问题 3: 编程语言的错误倾向是否取决于应用领域?

为了回答这个问题,我们首先在我们的回归模型中,以高杠杆点过滤掉认为是异常的项目,这种方法在这里是必要的,尽管这是一个非统计学的方法,一些关系可能影响可视化。例如,我们找到一个简单的项目,Google 的 v8,一个 JavaScript 项目,负责中间件中的所有错误。这对我们来说是一个惊喜,因为 JavaScript 通常不用于中间件。这个模式一直在其它应用领域中不停地重复着,因此,我们过滤出的项目的缺陷度都低于 10% 和高于 90%。这个结果在 图 1 中。

我们看到在这个热力图中仅有一个很小的差异,正如在问题 1 节中看到的那样,这个结果仅表示编程语言固有的错误倾向。为验证这个推论,我们测量了编程语言对每个应用领域和对全部应用领域的缺陷倾向。对于除了数据库以外的全部领域,关联性都是正向的,并且 p 值是有意义的(<0.01)。因此,关于缺陷倾向,在每个领域的语言排序与全部领域的语言排序是基本相同的。

ut3.jpg

结论 3: 应用领域和编程语言缺陷倾向之间总体上没有联系。

我们证明了不同的语言产生了大量的缺陷,并且,这个关系不仅与特定的语言相关,也适用于一般的语言类;然而,我们发现,项目类型并不能在一定程度上协调这种关系。现在,我们转变我们的注意力到反应分类上,我想去了解,编程语言与特定种类的缺陷之间有什么联系,以及这种关系怎么与我们观察到的更普通的关系去比较。我们将缺陷分为不同的类别,如 表 5 所描述的那样,然后提出以下的问题:

问题 4:编程语言与 bug 分类之间有什么关系?

我们使用了一个类似于问题 3 中所用的方法,去了解编程语言与 bug 分类之间的关系。首先,我们研究了 bug 分类和编程语言类之间的关系。一个热力图(图 2)展示了在编程语言类和 bug 类型之上的总缺陷数。去理解 bug 分类和语言之间的相互作用,我们对每个类别使用一个 NBR 回归模型。对于每个模型,我们使用了与问题 1 中相同的控制因素,以及使用加权效应编码后的语言,去预测缺陷修复提交。

f2.jpg

图 2. bug 类别与编程语言类之间的关系。每个格子表示每语言类(行头部)每 bug 类别(列底部)的 bug 修复提交占全部 bug 修复提交的百分比。这个值是按列规范化的。

结果和编程语言的方差分析值展示在 表 8 中。每个模型的整体异常是非常小的,并且对于特定的缺陷类型,通过语言所展示的比例在大多数类别中的量级是类似的。我们解释这种关系为,编程语言对于特定的 bug 类别的影响要大于总体的影响。尽管我们结论概括了全部的类别,但是,在接下来的一节中,我们对 表 5 中反应出来的 bug 数较多的 bug 类别做进一步研究。

t8.jpg

表 8. 虽然编程语言对缺陷的影响因缺陷类别而不同,但是,编程语言对特定的类别的影响要大于一般的类别。

编程错误 普通的编程错误占所有 bug 修复提交的 88.53% 左右,并且在所有的编程语言类中都有。因此,回归分析给出了一个与问题 1 中类似的结论(查看 表 6)。所有的编程语言都会导致这种编程错误,比如,处理错误、定义错误、输入错误、等等。

内存错误 内存错误占所有 bug 修复提交的 5.44%。热力图 图 2 证明了在 Proc-Static-Implicit-Unmanaged 类和内存错误之间存在着非常紧密的联系。非管理内存的编程语言出现内存 bug,这是预料之中的。表 8 也证明了这一点,例如,C、C++、和 Objective-C 引发了很多的内存错误。在管理内存的语言中 Java 引发了更多的内存错误,尽管它少于非管理内存的编程语言。虽然 Java 自己做内存回收,但是,它出现内存泄露一点也不奇怪,因为对象引用经常阻止内存回收。 11 在我们的数据中,Java 的所有内存错误中,28.89% 是内存泄漏造成的。就数量而言,编程语言中内存缺陷相比其它类别的 原因 造成的影响要大很多。

并发错误 在总的 bug 修复提交中,并发错误相关的修复提交占 1.99%。热力图显示,Proc-Static-Implicit-Unmanaged 是主要的错误类型。在这种错误中,C 和 C++ 分别占 19.15% 和 7.89%,并且它们分布在各个项目中。

ut4.jpg

属于 Static-Strong-Managed 语言类的编程语言都被证实处于热力图中的暗区中,普通的静态语言相比其它语言产生更多的并发错误。在动态语言中,仅仅有 Erlang 有更多的并发错误倾向,或许与使用这种语言开发的并发应用程序非常多有关系。同样地,在 表 8 中的负的系数表明,用诸如 Ruby 和 `Php 这样的动态编程语言写的项目,并发错误要少一些。请注意,某些语言,如 JavaScript、CoffeeScript 和 TypeScript 是不支持并发的,在传统的惯例中,虽然 Php 具有有限的并发支持(取决于它的实现)。这些语言在我们的数据中引入了虚假的 0,因此,在 表 8 中这些语言的并发模型的系数,不能像其它的语言那样去解释。因为存在这些虚假的 0,所以在这个模型中所有语言的平均数非常小,它可能影响系数的大小,因此,她们给 w.r.t. 一个平均数,但是,这并不影响他们之间的相对关系,因为我们只关注它们的相对关系。

基于 bug 修复消息中高频词的文本分析表明,大多数的并发错误发生在一个条件争用、死锁、或者不正确的同步上,正如上面表中所示。遍历所有语言,条件争用是并发错误出现最多的原因,例如,在 Go 中占 92%。在 Go 中条件争用错误的改进,或许是因为使用了一个争用检测工具帮助开发者去定位争用。同步错误主要与消息传递接口(MPI)或者共享内存操作(SHM)相关。Erlang 和 Go 对线程间通讯使用 MPI e ,这就是为什么这两种语言没有发生任何 SHM 相关的错误的原因,比如共享锁、互斥锁等等。相比之下,为线程间通讯使用早期的 SHM 技术的语言写的项目,就可能存在锁相关的错误。

安全和其它冲突错误 在所有的 bug 修复提交中,与 冲突 Impact 错误相关的提交占了 7.33% 左右。其中,Erlang、C++、Python 与安全相关的错误要高于平均值(表 8)。Clojure 项目相关的安全错误较少(图 2)。从热力图上我们也可以看出来,静态语言一般更易于发生失败和性能错误,紧随其后的是 Functional-Dynamic-Explicit-Managed 语言,比如 Erlang。对异常结果的分析表明,编程语言与冲突失败密切相关。虽然安全错误在这个类别中是弱相关的,与残差相比,解释类语言的差异仍然比较大。

结论 4: 缺陷类型与编程语言强相关;一些缺陷类型比如内存错误和并发错误也取决于早期的语言(所使用的技术)。对于特定类别,编程语言所引起的错误比整体更多。

4. 相关工作

在编程语言比较之前做的工作分为以下三类:

(1) 受控实验

对于一个给定的任务,开发者使用不同的语言进行编程时受到监视。研究然后比较结果,比如,开发成果和代码质量。Hanenberg 7 通过开发一个解析程序,对 48 位程序员监视了 27 小时,去比较了静态与动态类型。他发现这两者在代码质量方面没有显著的差异,但是,基于动态类型的语言花费了更短的开发时间。他们的研究是在一个实验室中,使用本科学生,设置了定制的语言和 IDE 来进行的。我们的研究正好相反,是一个实际的流行软件应用的研究。虽然我们只能够通过使用回归模型间接(和 事后 )控制混杂因素,我们的优势是样本数量大,并且更真实、使用更广泛的软件。我们发现在相同的条件下,静态化类型的语言比动态化类型的语言更少出现错误倾向,并且不允许隐式类型转换的语言要好于允许隐式类型转换的语言,其对结果的影响是非常小的。这是合理的,因为样本量非常大,所以这种非常小的影响在这个研究中可以看得到。

Harrison et al. 8 比较了 C++ 与 SML,一个是过程化编程语言,一个是函数化编程语言,在总的错误数量上没有找到显著的差异,不过 SML 相比 C++ 有更高的缺陷密集度。SML 在我们的数据中并没有体现出来,不过,认为函数式编程语言相比过程化编程语言更少出现缺陷。另一个重点工作是比较跨不同语言的开发工作。 12 20 不过,他们并不分析编程语言的缺陷倾向。

(2) 调查

Meyerovich 和 Rabkin 16 调查了开发者对编程语言的观点,去研究为什么一些编程语言比其它的语言更流行。他们的报告指出,非编程语言的因素影响非常大:先前的编程语言技能、可用的开源工具、以及现有的老式系统。我们的研究也证明,可利用的外部工具也影响软件质量;例如,在 Go 中的并发 bug(请查看问题 4 节内容)。

(3) 对软件仓库的挖掘

Bhattacharya 和 Neamtiu 1 研究了用 C 和 C++ 开发的四个项目,并且发现在 C++ 中开发的组件一般比在 C 中开发的组件更可靠。我们发现 C 和 C++ 的错误倾向要高于全部编程语言的平均值。但是,对于某些 bug 类型,像并发错误,C 的缺陷倾向要高于 C++(请查看第 3 节中的问题 4)。

5. 有效的风险

我们认为,我们的报告的结论几乎没有风险。首先,在识别 bug 修复提交方面,我们依赖的关键字是开发者经常用于表示 bug 修复的关键字。我们的选择是经过认真考虑的。在一个持续的开发过程中,我们去捕获那些开发者一直面对的问题,而不是他们报告的 bug。不过,这种选择存在过高估计的风险。我们对领域分类是为了去解释缺陷的倾向,而且,我们研究组中另外的成员验证过分类。此外,我们花费精力去对 bug 修复提交进行分类,也可能有被最初选择的关键字所污染的风险。每个项目在提交日志的描述上也不相同。为了缓解这些风险,我们像 2.4 节中描述的那样,利用手工注释评估了我们的类别。

我们判断文件所属的编程语言是基于文件的扩展名。如果使用不同的编程语言写的文件使用了我们研究的通用的编程语言文件的扩展名,这种情况下也容易出现错误倾向。为减少这种错误,我们使用一个随机样本文件集手工验证了我们的语言分类。

根据我们的数据集所显示的情形,2.2 节中的解释类编程语言,我们依据编程语言属性的主要用途作了一些假设。例如,我们将 Objective-C 分到非管理内存类型中,而不是混合类型。同样,我们将 Scala 注释为函数式编程语言,将 C# 作为过程化的编程语言,虽然,它们在设计的选择上两者都支持。 19 21 在这项研究工作中,我们没有从过程化编程语言中分离出面向对象的编程语言(OOP),因为,它们没有清晰的区别,主要差异在于编程类型。我们将 C++ 分到允许隐式类型转换的类别中是因为,某些类型的内存区域可以通过使用指针操作被进行不同的处理, 22 并且我们注意到大多数 C++ 编译器可以在编译时检测类型错误。

最后,我们将缺陷修复提交关联到编程语言属性上,它们可以反应出报告的风格或者其它开发者的属性。可用的外部工具或者 library 也可以影响一个相关的编程语言的 bug 数量。

6. 总结

我们对编程语言和使用进行了大规模的研究,因为它涉及到软件质量。我们使用的 Github 上的数据,具有很高的复杂性和多个维度上的差异的特性。我们的样本数量允许我们对编程语言效果以及在控制一些混杂因素的情况下,对编程语言、应用领域和缺陷类型之间的相互作用,进行一个混合方法的研究。研究数据显示,函数式语言是好于过程化语言的;不允许隐式类型转换的语言是好于允许隐式类型转换的语言的;静态类型的语言是好于动态类型的语言的;管理内存的语言是好于非管理的语言的。进一步讲,编程语言的缺陷倾向与软件应用领域并没有关联。另外,每个编程语言更多是与特定的 bug 类别有关联,而不是与全部 bug。

另一方面,即便是很大规模的数据集,它们被多种方法同时进行分割后,也变得很小且不全面。因此,随着依赖的变量越来越多,很难去回答某个变量所产生的影响有多大这种问题,尤其是在变量之间相互作用的情况下。因此,我们无法去量化编程语言在使用中的特定的效果。其它的方法,比如调查,可能对此有帮助。我们将在以后的工作中来解决这些挑战。

致谢

这个材料是在美国国家科学基金会(NSF)以及美国空军科学研究办公室(AFOSR)的授权和支持下完成的。授权号 1445079, 1247280, 1414172,1446683,FA955-11-1-0246。

参考资料

  1. Bhattacharya, P., Neamtiu, I. Assessing programming language impact on development and maintenance: A study on C and C++. In Proceedings of the 33rd International Conference on Software Engineering, ICSE'11 (New York, NY USA, 2011). ACM, 171–180.
  2. Bird, C., Nagappan, N., Murphy, B., Gall, H., Devanbu, P. Don't touch my code! Examining the effects of ownership on software quality. In Proceedings of the 19th ACM SIGSOFT Symposium and the 13th European Conference on Foundations of Software Engineering (2011). ACM, 4–14.
  3. Blei, D.M. Probabilistic topic models. Commun. ACM 55 , 4 (2012), 77–84.
  4. Cohen, J. Applied Multiple Regression/Correlation Analysis for the Behavioral Sciences. Lawrence Erlbaum, 2003.
  5. Easterbrook, S., Singer, J., Storey, M.-A., Damian, D. Selecting empirical methods for software engineering research. In Guide to Advanced Empirical Software Engineering (2008). Springer, 285–311.
  6. El Emam, K., Benlarbi, S., Goel, N., Rai, S.N. The confounding effect of class size on the validity of object-oriented metrics. IEEE Trans. Softw. Eng. 27 , 7 (2001), 630–650.
  7. Hanenberg, S. An experiment about static and dynamic type systems: Doubts about the positive impact of static type systems on development time. In Proceedings of the ACM International Conference on Object Oriented Programming Systems Languages and Applications, OOPSLA'10 (New York, NY, USA, 2010). ACM, 22–35.
  8. Harrison, R., Smaraweera, L., Dobie, M., Lewis, P. Comparing programming paradigms: An evaluation of functional and object-oriented programs. Softw. Eng. J. 11 , 4 (1996), 247–254.
  9. Harter, D.E., Krishnan, M.S., Slaughter, S.A. Effects of process maturity on quality, cycle time, and effort in software product development. Manage. Sci. 46 4 (2000), 451–466.
  10. Hindley, R. The principal type-scheme of an object in combinatory logic. Trans. Am. Math. Soc. (1969), 29–60.
  11. Jump, M., McKinley, K.S. Cork: Dynamic memory leak detection for garbage-collected languages. In ACM SIGPLAN Notices , Volume 42 (2007). ACM, 31–38.
  12. Kleinschmager, S., Hanenberg, S., Robbes, R., Tanter, É., Stefik, A. Do static type systems improve the maintainability of software systems? An empirical study. In 2012 IEEE 20th International Conference on Program Comprehension (ICPC) (2012). IEEE, 153–162.
  13. Li, Z., Tan, L., Wang, X., Lu, S., Zhou, Y., Zhai, C. Have things changed now? An empirical study of bug characteristics in modern open source software. In ASID'06: Proceedings of the 1st Workshop on Architectural and System Support for Improving Software Dependability (October 2006).
  14. Marques De Sá, J.P. Applied Statistics Using SPSS, Statistica and Matlab , 2003.
  15. Mayer, C., Hanenberg, S., Robbes, R., Tanter, É., Stefik, A. An empirical study of the influence of static type systems on the usability of undocumented software. In ACM SIGPLAN Notices , Volume 47 (2012). ACM, 683–702.
  16. Meyerovich, L.A., Rabkin, A.S. Empirical analysis of programming language adoption. In Proceedings of the 2013 ACM SIGPLAN International Conference on Object Oriented Programming Systems Languages & Applications (2013). ACM, 1–18.
  17. Milner, R. A theory of type polymorphism in programming. J. Comput. Syst. Sci. 17 , 3 (1978), 348–375.
  18. Mockus, A., Votta, L.G. Identifying reasons for software changes using historic databases. In ICSM'00. Proceedings of the International Conference on Software Maintenance (2000). IEEE Computer Society, 120.
  19. Odersky, M., Spoon, L., Venners, B. Programming in Scala. Artima Inc, 2008.
  20. Pankratius, V., Schmidt, F., Garretón, G. Combining functional and imperative programming for multicore software: An empirical study evaluating scala and java. In Proceedings of the 2012 International Conference on Software Engineering (2012). IEEE Press, 123–133.
  21. Petricek, T., Skeet, J. Real World Functional Programming: With Examples in F# and C#. Manning Publications Co., 2009.
  22. Pierce, B.C. Types and Programming Languages. MIT Press, 2002.
  23. Posnett, D., Bird, C., Dévanbu, P. An empirical study on the influence of pattern roles on change-proneness. Emp. Softw. Eng. 16 , 3 (2011), 396–423.
  24. Tan, L., Liu, C., Li, Z., Wang, X., Zhou, Y., Zhai, C. Bug characteristics in open source software. Emp. Softw. Eng. (2013).

作者

Baishakhi Ray ([email protected]), Department of Computer Science, University of Virginia, Charlottesville, VA.

Daryl Posnett ([email protected]), Department of Computer Science, University of California, Davis, CA.

Premkumar Devanbu ([email protected]), Department of Computer Science, University of California, Davis, CA.

Vladimir Filkov ([email protected]), Department of Computer Science, University of California, Davis, CA.

脚注


via: https://cacm.acm.org/magazines/2017/10/221326-a-large-scale-study-of-programming-languages-and-code-quality-in-github/fulltext?imm_mid=0f7103&cmp=em-prog-na-na-newsltr_20171007

作者:Baishakhi Ray, Daryl Posnett, Premkumar Devanbu, Vladimir Filkov 译者:qhwdw 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

借助 GitHub 的 网络钩子 webhook ,开发者可以创建很多有用的服务。从触发一个 Jenkins 实例上的 CI(持续集成) 任务到配置云中的机器,几乎有着无限的可能性。这篇教程将展示如何使用 Python 和 Flask 框架来搭建一个简单的持续部署(CD)服务。

在这个例子中的持续部署服务是一个简单的 Flask 应用,其带有接受 GitHub 的 网络钩子 webhook 请求的 REST 端点 endpoint 。在验证每个请求都来自正确的 GitHub 仓库后,服务器将 拉取 pull 更改到仓库的本地副本。这样每次一个新的 提交 commit 推送到远程 GitHub 仓库,本地仓库就会自动更新。

Flask web 服务

用 Flask 搭建一个小的 web 服务非常简单。这里可以先看看项目的结构。

├── app
│   ├── __init__.py
│   └── webhooks.py
├── requirements.txt
└── wsgi.py

首先,创建应用。应用代码在 app 目录下。

两个文件(__init__.pywebhooks.py)构成了 Flask 应用。前者包含有创建 Flask 应用并为其添加配置的代码。后者有 端点 endpoint 逻辑。这是该应用接收 GitHub 请求数据的地方。

这里是 app/__init__.py 的内容:

import os
from flask import Flask

from .webhooks import webhook

def create_app():
 """ Create, configure and return the Flask application """

  app = Flask(__name__)
  app.config['GITHUB_SECRET'] = os.environ.get('GITHUB_SECRET')
  app.config['REPO_PATH'] = os.environ.get('REPO_PATH')
  app.register_blueprint(webhook)

  return(app)

该函数创建了两个配置变量:

  • GITHUB_SECRET 保存一个密码,用来认证 GitHub 请求。
  • REPO_PATH 保存了自动更新的仓库路径。

这份代码使用 Flask 蓝图 Flask Blueprints 来组织应用的 端点 endpoint 。使用蓝图可以对 API 进行逻辑分组,使应用程序更易于维护。通常认为这是一种好的做法。

这里是 app/webhooks.py 的内容:

import hmac
from flask import request, Blueprint, jsonify, current_app 
from git import Repo

webhook = Blueprint('webhook', __name__, url_prefix='')

@webhook.route('/github', methods=['POST']) 
def handle_github_hook(): 
 """ Entry point for github webhook """

  signature = request.headers.get('X-Hub-Signature') 
  sha, signature = signature.split('=')

  secret = str.encode(current_app.config.get('GITHUB_SECRET'))

  hashhex = hmac.new(secret, request.data, digestmod='sha1').hexdigest()
  if hmac.compare_digest(hashhex, signature): 
    repo = Repo(current_app.config.get('REPO_PATH')) 
    origin = repo.remotes.origin 
    origin.pull('--rebase')

    commit = request.json['after'][0:6]
    print('Repository updated with commit {}'.format(commit))
  return jsonify({}), 200

首先代码创建了一个新的蓝图 webhook。然后它使用 Flask route 为蓝图添加了一个端点。任何请求 /GitHub URL 端点的 POST 请求都将调用这个路由。

验证请求

当服务在该端点上接到请求时,首先它必须验证该请求是否来自 GitHub 以及来自正确的仓库。GitHub 在请求头的 X-Hub-Signature 中提供了一个签名。该签名由一个密码(GITHUB_SECRET),请求体的 HMAC 十六进制摘要,并使用 sha1 哈希生成。

为了验证请求,服务需要在本地计算签名并与请求头中收到的签名做比较。这可以由 hmac.compare_digest 函数完成。

自定义钩子逻辑

在验证请求后,现在就可以处理了。这篇教程使用 GitPython 模块来与 git 仓库进行交互。GitPython 模块中的 Repo 对象用于访问远程仓库 origin。该服务在本地拉取 origin 仓库的最新更改,还用 --rebase 选项来避免合并的问题。

调试打印语句显示了从请求体收到的短提交哈希。这个例子展示了如何使用请求体。更多关于请求体的可用数据的信息,请查询 GitHub 文档

最后该服务返回了一个空的 JSON 字符串和 200 的状态码。这用于告诉 GitHub 的网络钩子服务已经收到了请求。

部署服务

为了运行该服务,这个例子使用 gunicorn web 服务器。首先安装服务依赖。在支持的 Fedora 服务器上,以 sudo 运行这条命令:

sudo dnf install python3-gunicorn python3-flask python3-GitPython

现在编辑 gunicorn 使用的 wsgi.py 文件来运行该服务:

from app import create_app
application = create_app()

为了部署服务,使用以下命令克隆这个 git 仓库或者使用你自己的 git 仓库:

git clone https://github.com/cverna/github_hook_deployment.git /opt/

下一步是配置服务所需的环境变量。运行这些命令:

export GITHUB_SECRET=asecretpassphraseusebygithubwebhook
export REPO_PATH=/opt/github_hook_deployment/

这篇教程使用网络钩子服务的 GitHub 仓库,但你可以使用你想要的不同仓库。最后,使用这些命令开启该 web 服务:

cd /opt/github_hook_deployment/
gunicorn --bind 0.0.0.0 wsgi:application --reload

这些选项中绑定了 web 服务的 IP 地址为 0.0.0.0,意味着它将接收来自任何的主机的请求。选项 --reload 确保了当代码更改时重启 web 服务。这就是持续部署的魔力所在。每次接收到 GitHub 请求时将拉取仓库的最近更新,同时 gunicore 检测这些更改并且自动重启服务。

*注意: *为了能接收到 GitHub 请求,web 服务必须部署到具有公有 IP 地址的服务器上。做到这点的简单方法就是使用你最喜欢的云提供商比如 DigitalOcean,AWS,Linode等。

配置 GitHub

这篇教程的最后一部分是配置 GitHub 来发送网络钩子请求到 web 服务上。这是持续部署的关键。

从你的 GitHub 仓库的设置中,选择 Webhook 菜单,并且点击“Add Webhook”。输入以下信息:

  • “Payload URL”: 服务的 URL,比如 <http://public_ip_address:8000/github>
  • “Content type”: 选择 “application/json”
  • “Secret”: 前面定义的 GITHUB_SECRET 环境变量

然后点击“Add Webhook” 按钮。

现在每当该仓库发生推送事件时,GitHub 将向服务发送请求。

总结

这篇教程向你展示了如何写一个基于 Flask 的用于接收 GitHub 的网络钩子请求,并实现持续集成的 web 服务。现在你应该能以本教程作为起点来搭建对自己有用的服务。


via: https://fedoramagazine.org/continuous-deployment-github-python/

作者:Clément Verna 选题:lujun9972 译者:kimii 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

今天我将谈一下我这个博客是如何搭建的。在我们开始之前,我希望你熟悉使用 Github 并且可以搭建一个 Python 虚拟环境来进行开发。如果你不能做到这些,我推荐你去学习一下 Django Girls 教程,它包含以上和更多的内容。

这是一篇帮助你发布由 Github 托管的个人博客的教程。为此,你需要一个正常的 Github 用户账户 (而不是一个工程账户)。

你要做的第一件事是创建一个放置代码的 Github 仓库。如果你想要你的博客仅仅指向你的用户名 (比如 rsip22.github.io) 而不是一个子文件夹 (比如 rsip22.github.io/blog),你必须创建一个带有全名的仓库。

Github 截图,打开了创建新仓库的菜单,正在以 'rsip22.github.io' 名字创建一个新的仓库

我推荐你使用 README、用于 Python 的 .gitignore一个自由软件许可证 初始化你的仓库。如果你使用自由软件许可证,你仍然拥有这些代码,但是你使得其他人能从中受益,允许他们学习和复用它,并且更重要的是允许他们享有这些代码。

既然仓库已经创建好了,那我们就克隆到本机中将用来保存代码的文件夹下:

$ git clone https://github.com/YOUR_USERNAME/YOUR_USERNAME.github.io.git

并且切换到新的目录:

 $ cd YOUR_USERNAME.github.io

因为 Github Pages 偏好运行的方式是从 master 分支提供文件,你必须将你的源代码放到新的分支,防止 Pelican 产生的静态文件输出到 master 分支。为此,你必须创建一个名为 source 的分支。

$ git checkout -b source

用你的系统所安装的 Pyhton 3 创建该虚拟环境(virtualenv)。

在 GNU/Linux 系统中,命令可能如下:

$ python3 -m venv venv

或者像这样:

$ virtualenv --python=python3.5 venv

并且激活它:

$ source venv/bin/activate

在虚拟环境里,你需要安装 pelican 和它的依赖包。你也应该安装 ghp-import (来帮助我们发布到 Github 上)和 Markdown (为了使用 markdown 语法来写文章)。运行如下命令:

(venv)$ pip install pelican markdown ghp-import

一旦完成,你就可以使用 pelican-quickstart 开始创建你的博客了:

(venv)$ pelican-quickstart

这将会提示我们一系列的问题。在回答它们之前,请看一下如下我的答案:

> Where do you want to create your new web site? [.] ./
> What will be the title of this web site? Renata's blog
> Who will be the author of this web site? Renata
> What will be the default language of this web site? [pt] en
> Do you want to specify a URL prefix? e.g., http://example.com (Y/n) n
> Do you want to enable article pagination? (Y/n) y
> How many articles per page do you want? [10] 10
> What is your time zone? [Europe/Paris] America/Sao_Paulo
> Do you want to generate a Fabfile/Makefile to automate generation and publishing? (Y/n) Y **# PAY ATTENTION TO THIS!**
> Do you want an auto-reload & simpleHTTP script to assist with theme and site development? (Y/n) n
> Do you want to upload your website using FTP? (y/N) n
> Do you want to upload your website using SSH? (y/N) n
> Do you want to upload your website using Dropbox? (y/N) n
> Do you want to upload your website using S3? (y/N) n
> Do you want to upload your website using Rackspace Cloud Files? (y/N) n
> Do you want to upload your website using GitHub Pages? (y/N) y
> Is this your personal page (username.github.io)? (y/N) y
 Done. Your new project is available at /home/username/YOUR_USERNAME.github.io

关于时区,应该指定为 TZ 时区(这里是全部列表: tz 数据库时区列表)。

现在,继续往下走并开始创建你的第一篇博文!你可能想在你喜爱的代码编辑器里打开工程目录并且找到里面的 content 文件夹。然后创建一个新文件,它可以被命名为 my-first-post.md (别担心,这只是为了测试,以后你可以改变它)。在文章内容之前,应该以元数据开始,这些元数据标识标题、日期、目录及更多,像下面这样:

.lang="markdown" # DON'T COPY this line, it exists just for highlighting purposes

Title: My first post
Date: 2017-11-26 10:01
Modified: 2017-11-27 12:30
Category: misc
Tags: first, misc
Slug: My-first-post
Authors: Your name
Summary: What does your post talk about? Write here.

This is the *first post* from my Pelican blog. **YAY!**

让我们看看它长什么样?

进入终端,产生静态文件并且启动服务器。要这么做,使用下面命令:

(venv)$ make html && make serve

当这条命令正在运行,你应该可以在你喜爱的 web 浏览器地址栏中键入 localhost:8000 来访问它。

博客主页的截图。它有一个带有 Renata's blog 标题的头部,第一篇博文在左边,文章的信息在右边,链接和社交在底部

相当简洁,对吧?

现在,如果你想在文章中放一张图片,该怎么做呢?好,首先你在放置文章的内容目录里创建一个目录。为了引用简单,我们将这个目录命名为 image。现在你必须让 Pelican 使用它。找到 pelicanconf.py 文件,这个文件是你配置系统的地方,并且添加一个包含你的图片目录的变量:

.lang="python" # DON'T COPY this line, it exists just for highlighting purposes

STATIC_PATHS = ['images']

保存它。打开文章并且以如下方式添加图片:

.lang="markdown" # DON'T COPY this line, it exists just for highlighting purposes

![Write here a good description for people who can't see the image]({filename}/images/IMAGE_NAME.jpg)

你可以在终端中随时按下 CTRL+C 来中断服务器。但是你应该再次启动它并检查图片是否正确。你能记住怎么样做吗?

(venv)$ make html && make serve

在你代码完工之前的最后一步:你应该确保任何人都可以使用 ATOM 或 RSS 流来读你的文章。找到 pelicanconf.py 文件,这个文件是你配置系统的地方,并且编辑关于 RSS 流产生的部分:

.lang="python" # DON'T COPY this line, it exists just for highlighting purposes

FEED_ALL_ATOM = 'feeds/all.atom.xml'
FEED_ALL_RSS = 'feeds/all.rss.xml'
AUTHOR_FEED_RSS = 'feeds/%s.rss.xml'
RSS_FEED_SUMMARY_ONLY = False

保存所有,这样你才可以将代码上传到 Github 上。你可以通过添加所有文件,使用一个信息(“first commit”)来提交它,并且使用 git push。你将会被问起你的 Github 登录名和密码。

$ git add -A && git commit -a -m 'first commit' && git push --all

还有...记住在最开始的时候,我给你说的怎样防止 Pelican 产生的静态文件输出 master 分支吗。现在对你来说是时候产生它们了:

$ make github

你将会被再次问及 Github 登录名和密码。好了!你的新博客应该创建在 https://YOUR_USERNAME.github.io

如果你在过程中任何一步遇到一个错误,请重新读一下这篇手册,尝试并看看你是否能发现错误发生的部分,因为这是调试的第一步。有时甚至一些简单的东西比如一个错字或者 Python 中错误的缩进都可以给我们带来麻烦。说出来并向网上或你的社区求助。

对于如何使用 Markdown 来写文章,你可以读一下 Daring Fireball Markdown 指南

为了获取其它主题,我建议你访问 Pelican 主题

这篇文章改编自 Adrien Leger 的使用一个 Bottstrap3 主题来搭建由 Github 托管的 Pelican 博客


via: https://rsip22.github.io/blog/create-a-blog-with-pelican-and-github-pages.html

作者:rsip22 译者:liuxinyu123 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

仓库被封禁

在 2018 年 2 月 20 日,我们的开源项目放在 GitHub 上的仓库由于收到了 DMCA Takedown 投诉被封禁,仓库处于不可访问状态。此时在 GitHub 上访问该仓库时,会显示一个公开消息,表明该仓库被封禁的原因。

按照 GitHub DMCA 的规则,GitHub 在确认投诉有效后,会给该仓库的管理员发送一封邮件,提示该仓库需要在 24 小时内清理被投诉的内容,并回复 GitHub 才行——否则,该仓库会被封禁,禁止任何访问和数据导出。

我们在收到该 Takedown 投诉后,会有 24 小时的时间来响应,但由于过年期间,仓库拥有者没有及时看到邮件,未能及时发现这么严重的通知。因此,在过了 24 小时后, Github 按照 DMCA 的规则,进行了仓库的封禁。

仓库被封禁后,我们发现无法访问。根据封禁消息的提示,发现原来之前仓库内的某个文件出了问题,侵犯了原作者的版权。原作者向 Github 发送了 DMCA 投诉。而由于我们的未及时处理,导致了仓库的最终被封。当我们发现被封时,已经是深夜了。

紧急商讨方案

在被封禁后,由于已经超过了 24 小时时限,在这个阶段下, GitHub 的文档中给出的解决方案仅有请求 GitHub 来删除该仓库并根据自己手里的仓库数据重建的方案。但对我们来说,这种方案是不可接受的,因为这种方案会导致丢失所有的 issue、PR、Wiki,以及你本地的仓库和远程的仓库之间的版本差异。

我们在群内先找了更新最全的 fork,找到了一个群友提供的,和上游只差 2 个提交的版本,并将其保存下来,作为最后的自救手段。

此外,在查询 DMCA 的过程中,我了解到 DMCA 除了有 DMCA Takedown 以外,还有一个 DMCA Counter Notice,用于反向解除 DMCA 封禁。

DMCA Counter Notice

DMCA Counter Notice 用于向服务商发起申诉,说明 DMCA Takedown 投诉为恶意投诉且并无版权问题。

延展阅读

https://www.plagiarismtoday.com/2010/06/03/7-common-questions-about-dmca-counter-notices/

https://help.github.com/articles/guide-to-submitting-a-dmca-counter-notice/

当时考虑到我们已经错过了窗口期,没办法删除 GitHub 上仓库中的特定文件,所以想通过 DMCA Counter Notice 来解除封禁。

为此,我通过 Github 发给我们的邮件,找到了那份侵权文件,并在他的网站中找到了版权拥有者的邮件,发送邮件说明情况,看看能否通过付费获得授权。但其是挪威人,存在时差,所以我们只能边等待,边想办法。

山重水复疑无路,柳暗花明又一村

在准备 DMCA Counter Notice 时,我们又向 Github 发送了邮件,说明了中国春节的特殊情况,导致我们没有来得及处理文件,请求给我们一个机会让我们处理这些文件。但是迟迟没有回应,无奈之下,多位成员又以成员身份向 GitHub 发送邮件,请求给予帮助。

令人惊奇的是,经过大约 9 个小时的等待,仓库拥有者的请求邮件似乎开小差了,而各位成员的请求邮件得到了响应。Github 回信给大家说,根据其规则,给出了额外的 24 小时窗口期,让我们处理这些文件(后来经过仔细查阅 GitHub 的 DMCA Takedown 规则,对这种错过了第一次窗口期的情况,可以给予第二个,也是最后一个窗口期)。但是这个开启额外的窗口期,需要仓库的拥有者向 GitHub 发送邮件请求。

然后,我们就以仓库拥有者的身份再次向 GitHub 发送了请求,可能是由于时差的原因,又是几个小时没有回应。

与此同时,我们也收到了版权拥有者的回复。很遗憾,原作者不愿意授权,也不打算收费。好在 Github 给的额外窗口期,让我们有了改正错误的机会。

还好,在焦急的等待之中,我们终于收到了 GitHub 的回复,并同时恢复了仓库的访问——宝贵的 24 小时窗口期。

使用 BFG 处理文件

得到了窗口期后,我们开始处理仓库内的文件。

首先,你得清除了现在还在仓库里面的文件,然后再使用下面的方面来清除提交历史中的数据。

推荐阅读

以下文章建议按顺序阅读

https://help.github.com/articles/removing-sensitive-data-from-a-repository/

https://rtyley.github.io/bfg-repo-cleaner/

删除 Git 仓库的历史数据有多种方法,一种是使用 git filter-branch来处理,但是速度极慢。另外一种就是使用 BFG 来处理,我们采用的是 BFG 来处理(BFG 是git filter-branch 首字母的逆转)。

BFG 需要 Java 的运行环境,如果无法运行,请检查你的本地 Java 环境是否安装,或高于 Java 7 。

Java 6 需要使用 bfg v1.12.3版本

BFG 的处理过程比较简单,首先,你需要下载 BFG。

wget http://repo1.maven.org/maven2/com/madgag/bfg/1.13.0/bfg-1.13.0.jar

然后克隆你的仓库到本地,比如 bestony/test

git clone --mirror  git://github.com/bestony/test.git

克隆时用不用 --mirror 模式都可以,但是后续命令上会有所差距,所以我还是推荐大家使用镜像模式,毕竟按照官方的文档走,出现了问题也好搜索。(镜像模式克隆的仓库和远程仓库完全一样,但是不能直接看到仓库里面的文件,而且也不能允许 git 的各种命令)

克隆到本地后,执行 BFG 命令来处理文件。

cd test.git
java -jar bfg.jar --delete-files "filename"

这里需要注意的是,filename 不支持目录路径,只能是文件名,而不能是 dir/filename 这样的形式,所以添加参数时你要注意这个。对于有同样名字的文件却想只删除某个目录的情况,可能就没有办法了。

此外,默认情况下, BFG 不会处理最新的提交,它认为你的最新提交应该是干净的(不包含需要删除的敏感数据),如果你要删除的文件是最新的提交(比如你最新的一个提交是删除那些敏感数据),可以加入--no-blob-protection参数来强制清除,也可以再添加一个提交,使包含了要被删除文件的提交不是最新的提交。

java -jar bfg.jar --delete-files "filename" --no-blob-protection

BFG 处理完成后,会提示你使用 git 命令进行垃圾回收,你需要执行如下命令来操作:

cd test.git # 进入目标目录
git reflog expire --expire=now --all && git gc --prune=now --aggressive # 垃圾回收
这里需要注意的是,如果你删除多个文件,每次删除后执行和多个文件都删除后效果一样,所以建议你删除多个文件后再进行垃圾回收,会更省时一些。

处理完成后,将数据推送到远端即可(需要关闭 GitHub 上仓库设置里面对强制推送的防护):

git push

执行完成后,就可以到远端上去看了,你的文件会被删除,相关的提交不会被删除,但是提交里面不包含该文件了。

在推送时,可能会提示你有些更改被拒绝了,这些更改如果是和 Pull Request 有关的,你可以不需要在意,这是 GitHub 自身的问题。Github 设定 Pull Request 是只读不可改的。所以我们无法修改这些信息。

具体可以参考 https://github.com/rtyley/bfg-repo-cleaner/issues/36

至此,我们将文件进行了删除处理,并清除了相关的数据。

后续处理

在完成文件及历史数据的删除后,我们将我们的删除结果回复了 Github ,等待 Github 的确认。GitHub 会在 24 小时收到该回复后,会通知投诉方进行确认。如果投诉方无异议,此事就此结束,不会再有下一步动作。如果有异议,则会重新进行此流程。

此外,由于 GitHub 存在垃圾缓存回收的时间差,所以你推送到 GitHub 上的数据虽然并无需要被删除的文件,但是依旧在一定时间内可以看到。这种缓存只能请 GitHub 自行操作删除。此外,与要删除的文件相关的 Pull Request 也需要 GitHub 来删除——因为用户是没有权限删除 Pull Request 的。这些请求也可以一并发给 GitHub 来操作(但似乎 GitHub 并不热衷执行这些请求,只要被投诉的文件访问不到即可,也就是说,如果没有被投诉历史数据,其实或许并不用大动干戈清理历史……)。

这种清除操作还有一个副作用就是,所有之前 fork 的仓库,由于主仓库被封禁而导致各个 fork 仓库的 remote 意外地变为另外一个仓库(该仓库是最早的一个 fork 仓库)。而主仓库恢复之后,我们并没有找到好的办法将 remote 恢复回原来的主仓库。因此,需要所有成员重新 fork 主仓库并从缓慢的 GitHub 克隆到本地。

余思

这个惊魂事件当中,我们首先要反思的是,我们对版权问题的认识不足,这是一切问题的根源。因此,这之后,我们对既有数据进行了排查。

其次,GitHub 在这种事件的处置上,我们认为也并不够好。这么严重的处置(整个库封禁),仅仅通过一份普通的邮件通知,而且仅仅给出 24 小时的时间窗口。而 GitHub 其实掌握了仓库拥有者的更可靠、更及时的联系方式,比如说手机短信,也完全可以在 GitHub 的网页界面上以显目的方式提醒。另外,虽然 DMCA 规则中提到了可以容情第二个时间窗口,但是似乎这个附加窗口期是后来才改变的政策,在前面的流程说明中并未提及,很容易忽视。

其三,由于封禁会导致对该仓库的所有访问均不可进行,这不仅包括了提交数据,也包括了并没有存在于 Git 仓库中的 issue、PR 和 Wiki 等数据,而 GitHub 不会让你在封禁的情况下有机会导出这些数据。所以,有机会的话,各种数据还是有个备份的好。

最后,感谢在这个事件中,所有不离不弃支持我们的成员,感谢小白进行的仓库清理工作。

相关阅读

如果您从未使用过 Git,甚至可能从未听说过它。莫慌张,只需要一步步地跟着这篇入门教程,很快您就会在 GitHub 上拥有一个全新的 Git 仓库。

在开始之前,让我们先理清一个常见的误解:Git 并不是 GitHub。Git 是一套版本控制系统(或者说是一款软件),能够协助您跟踪计算机程序和文件在任何时间的更改。它同样允许您在程序、代码和文件操作上与同事协作。GitHub 以及类似服务(包括 GitLab 和 BitBucket)都属于部署了 Git 程序的网站,能够托管您的代码。

步骤 1:申请一个 GitHub 账户

GitHub.com 网站上(免费)创建一个账户是最简单的方式。

选择一个用户名(比如说,octocat123),输入您的邮箱地址和密码,然后点击 Sign up for GitHub。进入之后,您将看到下方插图的界面:

步骤 2:创建一个新的仓库

一个仓库( repository),类似于能储存物品的场所或是容器;在这里,我们创建仓库存储代码。在 + 符号(在插图的右上角,我已经选中它了) 的下拉菜单中选择 New Repository

给您的仓库命名(比如说,Demo)然后点击 Create Repository。无需考虑本页面的其他选项。

恭喜!您已经在 GitHub.com 中建立了您的第一个仓库。

步骤 3: 创建文件

当仓库创建完毕后,界面将和下方一致:

不必惊慌,它比看上去简单。跟紧步骤。忽略其他内容,注意截图上的 “...or create a new repository on the command line,”。

在您的计算机中打开终端。

键入 git 然后回车。如果命令行显示 bash: git: command not found,在您的操作系统或发行版 安装 Git 命令。键入 git 并回车检查是否成功安装;如果安装成功,您将看见大量关于使用该命令的说明信息。

在终端内输入:

mkdir Demo

这个命令将会创建一个名为 Demo 的目录(文件夹)。

如下命令将会切换终端目录,跳转到 Demo 目录:

cd Demo

然后输入:

echo "#Demo" >> README.md

创建一个名为 README.md 的文件,并写入 #Demo。检查文件是否创建成功,请输入:

cat README.md

这将会为您显示 README.md 文件的内容,如果文件创建成功,您的终端会有如下显示:

使用 Git 程序告诉您的电脑,Demo 是一个被 Git 管理的目录,请输入:

git init

然后,告诉 Git 程序您关心的文件并且想在此刻起跟踪它的任何改变,请输入:

git add README.md

步骤 4:创建一次提交

目前为止,您已经创建了一个文件,并且已经通知了 Git,现在,是时候创建一次 提交 commit 了。提交可以看作是一个里程碑。每当完成一些工作之时,您都可以创建一次提交,保存文件当前版本,这样一来,您可以返回之前的版本,并且查看那时候的文件内容。无论何时您修改了文件,都可以对文件创建一个上一次的不一样的新版本。

创建一次提交,请输入:

git commit -m "first commit"

就是这样!刚才您创建了包含一条注释为 “first commit” 的 Git 提交。每次提交,您都必须编辑注释信息;它不仅能协助您识别提交,而且能让您理解此时您对文件做了什么修改。这样到了明天,如果您在文件中添加新的代码,您可以写一句提交信息:“添加了新的代码”,然后当您一个月后回来查看提交记录或者 Git 日志(即提交列表),您还能知道当时的您在文件夹里做了什么。

步骤 5: 将您的计算机与 GitHub 仓库相连接

现在,是时候用如下命令将您的计算机连接到 GitHub 仓库了:

git remote add origin https://github.com/<your_username>/Demo.git

让我们一步步的分析这行命令。我们通知 Git 去添加一个叫做 origin (起源)的,拥有地址为 https://github.com/<your_username>/Demo.git(它也是您的仓库的 GitHub 地址) 的 remote (远程仓库)。当您提交代码时,这允许您在 GitHub.com 和 Git 仓库交互时使用 origin 这个名称而不是完整的 Git 地址。为什么叫做 origin?当然,您可以叫点别的,只要您喜欢(惯例而已)。

现在,我们已经将本地 Demo 仓库副本连接到了其在 GitHub.com 远程副本上。您的终端看起来如下:

此刻我们已经连接到远程仓库,可以推送我们的代码 到 GitHub.com(例如上传 README.md 文件)。

执行完毕后,您的终端会显示如下信息:

然后,如果您访问 https://github.com/<your_username>/Demo,您会看到截图内显示的情况:

就是这么回事!您已经创建了您的第一个 GitHub 仓库,连接到了您的电脑,并且从你的计算机推送(或者称:上传)一个文件到 GitHub.com 名叫 Demo 的远程仓库上了。下一次,我将编写关于 Git 复制(从 GitHub 上下载文件到你的计算机上)、添加新文件、修改现存文件、推送(上传)文件到 GitHub。


via: https://opensource.com/article/18/1/step-step-guide-git

作者:Kedar Vijay Kulkarni 译者:CYLeft 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出