<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ GitHub - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ GitHub - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 31 May 2026 04:47:36 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/github/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 什么是语义化版本和约定式提交 ]]>
                </title>
                <description>
                    <![CDATA[ 最近写了不少新奇的小东西开源到了 GitHub 上，比如浏览器扩展或者一些运行起来的网页应用。在写浏览器扩展的过程中，我在生成 release 这一步时用 Github Action 创建了一条流水线，用于监听 manifest.json 中 version 属性值的变化，每次变化就根据 version 的值生成一个新版本的 release。也就在这时，我开始思考一个问题：我应该如何规范我的版本号？ 因为我此前并未接触过相关的规范，此前参与过的项目也没有明确的提交守则与版本号命名规范，对于相关的规范我只在一些开源项目中看到过，以及偶然听人提起过，并没有一个较为完整的认识，所以我决定查询一些资料去学习一下如何规范一个项目的版本号。 所幸我在让 AI 帮我规范提交信息时，它提到了“约定式提交”这个概念，而“约定式提交”与“语义化版本”有着密切的关系，所以我先分别了解了两者的概念，然后了解了它们之间是如何相互对应的。 语义化版本 软件的版本号是一个软件的重要标识，而许多软件的版本号命名都遵循着一套规范，“语义化版本”就是其中的一种。 什么是语义化版本 首先来看语义化版本的概念： > ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/semantic-versioning-and-conventional-commits/</link>
                <guid isPermaLink="false">67f3c36e11498004ee937050</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tsukistar ]]>
                </dc:creator>
                <pubDate>Wed, 09 Apr 2025 01:07:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2025/04/----_20250421114125.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>最近写了不少新奇的小东西开源到了 GitHub 上，比如浏览器扩展或者一些运行起来的网页应用。在写浏览器扩展的过程中，我在生成 release 这一步时用 Github Action 创建了一条流水线，用于监听 <code>manifest.json</code> 中 <strong>version</strong> 属性值的变化，每次变化就根据 version 的值生成一个新版本的 release。也就在这时，我开始思考一个问题：我应该如何规范我的版本号？</p>
<p>因为我此前并未接触过相关的规范，此前参与过的项目也没有明确的提交守则与版本号命名规范，对于相关的规范我只在一些开源项目中看到过，以及偶然听人提起过，并没有一个较为完整的认识，所以我决定查询一些资料去学习一下如何规范一个项目的版本号。</p>
<p>所幸我在让 AI 帮我规范提交信息时，它提到了“约定式提交”这个概念，而“约定式提交”与“语义化版本”有着密切的关系，所以我先分别了解了两者的概念，然后了解了它们之间是如何相互对应的。</p>
<h2 id="">语义化版本</h2>
<p>软件的版本号是一个软件的重要标识，而许多软件的版本号命名都遵循着一套规范，“语义化版本”就是其中的一种。</p>
<h3 id="">什么是语义化版本</h3>
<p>首先来看语义化版本的概念：</p>
<blockquote>
<p>语义化版本是一种用于描述软件版本的方式，它使用一组规则来描述版本的变化。语义化版本的规则如下：</p>
<ul>
<li>版本号由三个部分组成：主版本号、次版本号、修订号，格式为：<code>主版本号.次版本号.修订号</code></li>
<li>版本号的递增规则如下：
<ul>
<li>主版本号：当软件发生了不兼容的 API 更改时，主版本号递增，次版本号和修订号重置为 0</li>
<li>次版本号：当软件添加了新功能，但向后兼容时，次版本号递增，修订号重置为 0</li>
<li>修订号：当软件进行了 bug 修复，向后兼容时，修订号递增</li>
</ul>
</li>
<li>先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面，作为延伸。</li>
</ul>
</blockquote>
<p>（PS：向后兼容指的是，用户 import 你的软件包，用了里面的函数，这个软件包更新时，用户无需做任何修改，预期结果也不受影响）</p>
<h3 id="">举个例子</h3>
<p>让我们以一个例子来理解这种规则：</p>
<p>假设我们有一个版本号为 <code>1.0.0</code> 的软件包供外部软件调用，这个软件包中有一个函数<code>example(a: string, b: number): string {}</code>，以下三种情况分别对应了修订号、次版本号、主版本号的修改：</p>
<ul>
<li>example 函数体存在bug，我们在确保修复了这个 bug 的情况下未在软件包中引入新的 API，该函数依旧能够实现预期的功能，返回预期的结果。这时仅进行了 bug 修复，未添加新功能，未发生不兼容的API修改，版本号变为 <code>1.0.1</code></li>
<li>软件包中新增了一种 example 函数的函数重载，它的实现函数变成<code>example(a: string, b: number, c?: boolean): string {}</code>，用户原有的函数调用依旧返回预期结果，但是当用户调用 example 函数传入 <code>c</code>参数时，返回值会发生变化，这时我们新增了一个函数重载，未发生不兼容的API修改，版本号变为 <code>1.1.0</code></li>
<li>example 函数新增了一个参数，变成<code>example(a: string, b: number, c: string): string {}</code>，用户原有的函数调用会报错提示缺少参数。这时我们在 example 函数中新增了一个参数，发生了不兼容的API修改，版本号变为 <code>2.0.0</code></li>
</ul>
<p>可以看到，语义化版本的规则是非常清晰的，它规定了版本号的递增规则，以及当软件发生不兼容的 API 修改时，版本号的变化。这也是语义化版本的最主要的作用，它可以确保用户在使用某一主版本的软件包时，已编写的代码在该软件包仅发生次版本号会修订版本号更新时，不会出现错误。</p>
<h3 id="">开发初始阶段的版本控制</h3>
<p>软件的主版本号为零（0.y.z）是一种特殊状态，这意味着该软件处于开发初始阶段，一切都可能随时被改变。因此在初始开发阶段，进行版本控制最简单的做法是以 0.1.0 作为初始化开发版本，并在后续的每次发行时递增次版本号。</p>
<h3 id="100">如何判断发布 1.0.0 版本的时机</h3>
<p>当软件被部署到生产环境中开始被正式使用，它应该已经达到了 1.0.0 版。此时的软件已经有了一系列稳定的API在被使用者依赖，并开始需要考虑向后兼容的问题。</p>
<h3 id="">版本的优先级</h3>
<p>对于不同版本号的软件，如何排序决定它们的优先级呢？在判断优先级时，我们必须把版本依序拆分为主版本号、次版本号、修订号及先行版本号后进行比较（版本编译信息不在这份比较的列表中）。随后，按照这三条规则进行判断：</p>
<ul>
<li>对于两个正式版本，需要依据它们的主版本号、次版本号、修订号依次进行比较，直到找到第一个不同的数字，该数字较大的版本号优先级较高（例如1.0.0 &lt; 1.0.1 &lt; 1.1.0 &lt; 1.1.1 &lt; 2.0.0）</li>
<li>对于具有相同主版本号、次版本号、修订号的正式版本和先行版本，正式版本的优先级总是高于先行版本（例如1.0.0-alpha &lt; 1.0.0）</li>
<li>对于两个先行版本，如果它们的主版本号、次版本号、修订号完全相同，其优先级必须通过由左到右的每个被句点分隔的标识符来比较，直到找到一个差异值后，按照如下规则决定：
<ul>
<li>只有数字的标识符以数值高低比较。（例如1.0.0-alpha.1&lt;1.0.0-alpha.5）</li>
<li>有字母或连接号时则逐字以 ASCII 的排序来比较。（例如1.0.0-alpha.beta&lt;1.0.0-alpha.rc）</li>
<li>数字的标识符比非数字的标识符优先级低。（例如1.0.0-0&lt;1.0.0-alpha）</li>
<li>若开头的标识符都相同时，栏位比较多的先行版本号优先级比较高。（例如1.0.0-alpha.1&lt;1.0.0-alpha.1.0）</li>
</ul>
</li>
</ul>
<p>再举一个最直观的例子：<br>
1.0.0-alpha &lt; 1.0.0-alpha.1 &lt; 1.0.0-alpha.beta &lt; 1.0.0-beta &lt; 1.0.0-beta.2 &lt; 1.0.0-beta.11 &lt; 1.0.0-rc.1 &lt; 1.0.0 &lt; 1.0.1 &lt; 1.1.0 &lt; 1.1.1 &lt; 2.0.0<br>
你可以按照上述的三条规则，一一它们的优先级排列，相信你会对版本的优先级有更加深刻的认识。</p>
<h2 id="">约定式提交</h2>
<p>最开始时我提到过，“约定式提交”与“语义化版本”有着密切的关系，这是因为在进行源代码管理时，我们通常会使用版本控制工具，如 Git，在使用<code>git commit</code>提交代码时，如果我们按照“约定式提交”的规范填写提交信息，我们可以方便地将一组特定的提交发布为一个新版本，而这个新版本可以按照对应的提交类型确定版本号如何更新。</p>
<p>在许多开源项目的 contribute 指南中，都有约定式提交的规范，比如在 <a href="https://github.com/arco-design/arco-design/blob/main/CONTRIBUTING.zh-CN.md" target="_blank" rel="noopener">arco-design的CONTRIBUTING.zh-CN.md</a> 的 Commit 指南章节，就要求 Commit messages 需要遵循约定式提交的标准，并列出了 commit 的类型列表。</p>
<p>因此，让我们首先了解什么是约定式提交。</p>
<h3 id="">什么是约定式提交</h3>
<p>首先来看约定式提交的概念：</p>
<blockquote>
<p>约定式提交是一种用于描述提交信息的方式，它使用一组规则来描述提交的内容。约定式提交的规则如下：</p>
<ul>
<li>提交信息必须以一个描述性的标题开头，标题的第一个单词必须是提交类型，提交类型必须是以下之一：
<ul>
<li>feat：新功能（feature），类型 为 feat 的提交表示在代码库中新增了一个功能（这和语义化版本中的 次版本号 相对应）。</li>
<li>fix：bug 修复，类型 为 fix 的提交表示在代码库中修复了一个 bug（这和语义化版本中的 修订号 相对应）。</li>
<li>BREAKING CHANGE：破坏性变更，在脚注中包含 BREAKING CHANGE: 或 &lt;类型&gt;(范围) 后面有一个 ! 的提交，表示引入了破坏性 API 变更（这和语义化版本中的 MAJOR 相对应）。</li>
<li>其他提交类型：例如 docs、chore、style、refactor、perf、test、build、ci、revert 等</li>
</ul>
</li>
<li>提交信息的结构如下所示：<pre><code>&lt;类型&gt;[可选 范围]: &lt;描述&gt;
[可选 正文]
[可选 脚注]
</code></pre>
</li>
</ul>
</blockquote>
<p>让我们通过一些例子了解如何完成一个约定式提交：</p>
<ul>
<li>包含了描述、范围并且脚注中有破坏性变更的提交说明：<pre><code>feat(api): 新增了一个新功能
这是一个新功能的详细描述。
BREAKING CHANGE: 这个提交引入了一个破坏性变更。
</code></pre>
</li>
<li>包含了 ! 字符以提醒注意破坏性变更的提交说明：<pre><code>feat(api)!: 新增了一个新功能
这是一个新功能的详细描述。
</code></pre>
</li>
<li>不包含正文的提交说明：<pre><code>docs: correct spelling of CHANGELOG
</code></pre>
</li>
</ul>
<h3 id="">约定式提交与语义化版本的对应关系</h3>
<table>
<thead>
<tr>
<th>提交类型</th>
<th>语义化版本</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>feat</td>
<td>次版本号</td>
<td>新功能</td>
</tr>
<tr>
<td>fix</td>
<td>修订号</td>
<td>bug 修复</td>
</tr>
<tr>
<td>BREAKING CHANGE</td>
<td>主版本号</td>
<td>破坏性变更</td>
</tr>
<tr>
<td>其他提交类型</td>
<td>无</td>
<td>其他类型的提交</td>
</tr>
</tbody>
</table>
<h4 id="">举个例子</h4>
<p>让我们以一个例子来理解这种规则：</p>
<p>对于一个当前版本为 1.0.0 的项目，我们有以下几个提交：</p>
<ul>
<li>feat(api): 新增了一个新功能</li>
<li>fix(api): 修复了一个 bug</li>
<li>docs: 修正了文档中的错误</li>
</ul>
<p>我们现在想要发布一个新版本，由于新的提交包括了新功能的增加，但是没有引入破坏性的变更，所以我们可以按照语义化版本的规则，将次版本号递增，版本号变为 1.1.0。</p>
<p>但是如果我们有以下几个提交：</p>
<ul>
<li>feat(api)!: 新增了一个新功能, 引入了破坏性变更</li>
<li>fix(api): 修复了一个 bug</li>
<li>docs: 修正了文档中的错误</li>
</ul>
<p>由于引入了破坏性变更，如果我们此时想要发布新版本，我们需要将主版本号递增，版本号变为 2.0.0。</p>
<h3 id="">使用约定式提交的好处</h3>
<p>使用约定式提交有以下几个好处：</p>
<ul>
<li>便于建立流水线自动化生成 CHANGELOG 、自动化触发对应的构建与部署流程，从而方便地进行版本发布。</li>
<li>便于基于提交的类型，自动决定语义化的版本变更，从而方便地进行版本管理。</li>
<li>便于形成结构化的提交历史，向同事、公众与其他利益关系者传达变化的性质，降低贡献者了解项目发展历程的难度。</li>
</ul>
<h2 id="">后记</h2>
<p>语义化版本和约定式提交是两个相互关联的概念，它们都可以帮助我们更好地管理软件版本。语义化版本规定了版本号的递增规则，以及当软件发生不兼容的 API 修改时版本号的变化。约定式提交规定了提交信息的结构，以及如何根据提交信息自动决定版本号的变化。</p>
<p>在实际的项目开发与项目管理过程中，建立和遵守这些规范可以提高软件的质量和可维护性，同时也可以方便地进行版本管理和发布，提高开发效率与协作效率。</p>
<h2 id="">参考文章</h2>
<ul>
<li><a href="https://semver.org/lang/zh-CN/">语义化版本 2.0.0 | Semantic Versioning</a></li>
<li><a href="https://www.conventionalcommits.org/en/v1.0.0/">约定式提交</a></li>
</ul>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 .github 存储库 ]]>
                </title>
                <description>
                    <![CDATA[ GitHub 拥有许多特殊的存储库。比如，你可以创建一个与你用户名相匹配的存储库，添加 README 文件，其包含的所有信息都将在你的 GitHub 个人资料中可见。 你可能已经对许多仓库中的 .github 目录很熟悉了，而 .github 目录中包含了工作流（Workflow）、议题模板（Issue Template）、拉取请求模板（Pull Request Template）、资助信息（Funding Information）及那个项目所特有的文件。 但另一个你能创建的特殊仓库却是 .github 仓库。它的作用是为你那些实质上没有 .github 目录的库提供默认的议题模板及其他社区健康文件[^1]。 举个例子，假如说我有一个名为 .github 的存储库，其中包含通用的故障报告（Bug Report）和功能请求的议题模板（Feature Request Issue Template）。然后再建另一个名为 new-project 的库，但我没有在其中添加包含议题模板的 .github 目录。 那么当有人看到这个 new-project 库并打开一个议题时，他将看到一个选 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-use-the-dot-github-repository/</link>
                <guid isPermaLink="false">6575ca6b5c9dcf03f9984d8b</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jiaming Huang ]]>
                </dc:creator>
                <pubDate>Fri, 08 Dec 2023 10:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/12/Thumbnail.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-use-the-dot-github-repository/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use the .github Repository</a>
      </p><!--kg-card-begin: markdown--><p>GitHub 拥有许多特殊的存储库。比如，你可以创建一个与你用户名相匹配的存储库，添加 README 文件，其包含的所有信息都将在你的 GitHub 个人资料中可见。</p>
<p>你可能已经对许多仓库中的 <code>.github</code> 目录很熟悉了，而 <code>.github</code> 目录中包含了工作流（Workflow）、议题模板（Issue Template）、拉取请求模板（Pull Request Template）、资助信息（Funding Information）及那个项目所特有的文件。</p>
<p>但另一个你能创建的特殊仓库却是 <code>.github</code> 仓库。它的作用是为你那些实质上没有 <code>.github</code> 目录的库提供默认的议题模板及其他社区健康文件[^1]。</p>
<p>举个例子，假如说我有一个名为 <code>.github</code> 的存储库，其中包含通用的故障报告（Bug Report）和功能请求的议题模板（Feature Request Issue Template）。然后再建另一个名为 <code>new-project</code> 的库，但我没有在其中添加包含议题模板的 <code>.github</code> 目录。</p>
<p>那么当有人看到这个 <code>new-project</code> 库并打开一个议题时，他将看到一个选项用来选择 <code>.github</code> 仓库的同名目录中[^2]已有的通用模板。</p>
<p>同样的，当我为我的 <code>.github</code> 库中添加行为准则（Code of Conduct）后，它将在我所有未明确规定过的库中展示出来。</p>
<p>请注意，相对于 <code>.github</code> 仓库中的文件，当前仓库会优先选择自己 <code>.github</code> 目录中的那份文件。比如说，当我 <code>new-project</code> 库的 <code>.github</code> 目录中有功能请求的议题模板时， <code>.github</code> 库中的那份就不会被展示出来。</p>
<p>让我们看看这些特殊的仓库是如何运作的吧。</p>
<h2 id="githubgithub">如何在私人 GitHub 账号中使用 .github</h2>
<p>在 GitHub 上，创建特殊的仓库同创建其他库一样简单。所以打开浏览器，登上 GitHub，像这样创建一个仓库：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/12/Xo__mfEdt.png" alt="在我的 GitHub 账号里创建 .github 仓库" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>在我的 GitHub 账号里创建 .github 仓库</figcaption>
</figure>
<p>完成后，你可以开始往里添加文件。我要加的第一个文件是故障报告表单（Bug Report Issue Form）。我并不准备详细地讲表格的详情，不过你可以浏览我<a href="https://blog.anishde.dev/creating-a-bug-report-form-in-github">先前有关 GitHub 议题表单的文章</a>。</p>
<p><code>.github/ISSUE_TEMPLATE/bug_report.yml</code></p>
<pre><code class="language-yml">name: 🐛Bug Report
description: File a bug report here
title: "[BUG]: "
labels: ["bug"]
assignees: ["AnishDe12020"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report 🤗
        Make sure there aren't any open/closed issues for this topic 😃
        
  - type: textarea
    id: bug-description
    attributes:
      label: Description of the bug
      description: Give us a brief description of what happened and what should have happened
    validations:
      required: true
      
  - type: textarea
    id: steps-to-reproduce
    attributes:
      label: Steps To Reproduce
      description: Steps to reproduce the behavior.
      placeholder: |
        1. Go to '...'
        2. Click on '...'
        3. Scroll down to '...'
        4. See error
    validations:
      required: true
  - type: textarea
    id: additional-information
    attributes:
      label: Additional Information
      description: |
        Provide any additional information such as logs, screenshots, likes, scenarios in which the bug occurs so that it facilitates resolving the issue.
</code></pre>
<p>故障报告表单代码</p>
<p>我还打算创建一份功能请求表单。</p>
<p><code>.github/ISSUE_TEMPLATE/feature_request.yml</code></p>
<pre><code class="language-yml">name: ✨Feature Request
description: Request a new feature or enhancement
labels: ["enhancement"]
title: "[FEAT]: "
body:
  - type: markdown
    attributes:
      value: |
        Please make sure this feature request hasn't been already submitted by someone by looking through other open/closed issues
  
  - type: textarea
    id: description
    attributes:
      label: Description
      description: Give us a brief description of the feature or enhancement you would like
    validations:
      required: true
      
  - type: textarea
    id: additional-information
    attributes:
      label: Additional Information
      description: Give us some additional information on the feature request like proposed solutions, links, screenshots, etc.
</code></pre>
<p>功能请求表单代码</p>
<p>我要再加一份拉取请求模板。</p>
<p><code>.github/pull_request_template.md</code></p>
<pre><code class="language-md">&lt;!-- 
感谢你的拉取请求 🤗

请确保拉取请求被限定在一个类中（文档或功能之类的），而且让它尽可能地小。你可以开多个 pr(Pull Request, 拉取请求) 而不是单开一个很大的。
--&gt;

&lt;!-- 如果这个拉取请求关闭了议题，请在下面提及 --&gt;
Closes # &lt;!-- 对应议题的序号 --&gt;

## 📑 描述
&lt;!-- 为 pr 添加一个简短的描述--&gt;

&lt;!-- 你也可以选择用 markdown 的 to-do list 格式列出一系列变更来标记是否完成- [ ] 未完成
- [x] 已完成
--&gt;

## ✅ 检查
&lt;!-- 确保你的 pr 通过了 CI(Continuous Integration, 持续集成) 检查,并且根据需要选择以下选项 - --&gt;
- [ ] 我的拉取请求符合本项目的代码风格
- [ ] 我的代码需要修改文档
- [ ] 我已按要求更新了文件
- [ ] 所有测试均已通过

## ℹ 补充信息
&lt;!-- 任何补充信息，像破坏性更新（Breaking Change）、依赖添加（Dependency Added）、截图（Screenshot）和新旧行为的对比等等 --&gt;
</code></pre>
<p>拉取请求模板代码</p>
<p>我要加的最后一个文件是行为准则————不过它要放在库的根目录下。尽管如此，它也能正常工作（行为准则通常都被保存在根目录下）。注意，我在使用的是 <a href="https://www.contributor-covenant.org/">Contributor Convent</a> 公约。</p>
<p><code>CODE_OF_CONDUCT.md</code></p>
<pre><code class="language-md">
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
  community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or advances of
  any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
  without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[INSERT CONTACT METHOD].
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series of
actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within the
community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].

Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].

For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
</code></pre>
<p>Markdown 格式的 Contributor Convent 行为准则 v2.1</p>
<p>我们还可以添加更多文件，像资助信息、贡献指南(Contributing Guide)等。你可以在 <a href="https://docs.github.com/zh/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file">GitHub 文档</a> 中获取更多信息。</p>
<h3 id="github"><code>.github</code> 存储库实践</h3>
<p>我的 <a href="https://github.com/AnishDe12020/blog">blog 库</a>并没有任何议题模板、行为准则或其他文件，除了记录了我博客的 markdown 文件和一篇 README。所以这是用来测试这些功能(feature)是否起作用的绝佳仓库。</p>
<p>我已经可以看到行为准则在这里出现了：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/4Dk1gl1ZS.png" alt="4Dk1gl1ZS" width="600" height="400" loading="lazy"></p>
<p>如果我尝试去创建议题，也会收到模板：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/5fqH-4IYX.png" alt="5fqH-4IYX" width="600" height="400" loading="lazy"></p>
<p>在拉取请求时也起作用了。</p>
<h2 id="github">如何将 .github 存储库用于组织或公共账号</h2>
<p>组织账号中的 .github 存储库和个人账号上的 .github 存储库工作原理相同——只有一处区别。</p>
<p>组织也可以在 GitHub 上拥有 README 资料，它将展示在组织页面上。这个 README 位于组织 <code>.github</code> 仓库的 <code>profile</code> 目录下。为了展示，我将快速地建一个演示组织。</p>
<p>当为组织创建 <code>.github</code> 仓库时，你应该会得到如下消息：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/s2QEAhtHG-1.png" alt="s2QEAhtHG-1" width="600" height="400" loading="lazy"></p>
<p>同样的，当你给 <code>profile/README.md</code> 添加内容时，你也应该会受到这样的消息：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/12/vf0IEmbTH-1.png" alt="为 GitHub 组织创建 README" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>为 GitHub 组织创建 README</figcaption>
</figure>
<p>现在，我准备向 README 里加一些内容并提交。当我访问组织首页时，我们应当看到下面内容：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2021/12/svqbJ3PfG.png" alt="在组织页面查看 GitHub 组织资料 README 内容" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>在组织页面查看 GitHub 组织资料 README 内容</figcaption>
</figure>
<h2 id="">总结</h2>
<p>我希望你现在知道了 <code>.github</code> 仓库到底是什么。你也应该明白如何为你的库创建默认社区健康文件还有如何为你的组织添加 README 资料。</p>
<p>请随意通过 <a href="https://twitter.com/AnishDe12020">Twitter</a> 联系我。祝你有美好的一天😃</p>
<h3 id="">资源</h3>
<ul>
<li><a href="https://docs.github.com/zh/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file">GitHub 文档中关于社区健康文件的内容</a></li>
<li><a href="https://github.com/AnishDe12020/.github">我的 <code>.github</code> 仓库</a></li>
<li><a href="https://github.com/AnishDe12020-test/.github">我测试组织的 <code>.github</code> 仓库</a></li>
<li><a href="https://www.contributor-covenant.org/">Contributor Convent</a></li>
<li><a href="https://blog.anishde.dev/creating-a-bug-report-form-in-github">有关如何使用 GitHub 议题表单的文章</a></li>
</ul>
<p>我最近在开发一个叫 DevKit 的项目，这是一个渐进式 Web 应用，它将开发者工具整合在一个应用中，且让你的工作更快地完成。一定要来看看 <a href="https://www.devkit.one/">https://www.devkit.one/</a> 。</p>
<p><strong>译者注</strong></p>
<p>^1 翻译来源于<a href="https://docs.github.com/zh/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file">这篇文章</a>。<br>
^2 原文为 already in the .github directory ，我认为直接放在文中不合理，故修改，请注意，后文同样预设你已知要将文件放在同名库的 <code>.github</code> 目录下。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 GitHub Actions 实现开源项目的自动化 ]]>
                </title>
                <description>
                    <![CDATA[ 如今，开发人员经常使用自动化工具来有效地管理任务，并简化他们的日常活动。GitHub Actions 就是这些流行的工具之一。 你可能会同意我的观点：软件（包括开源软件）对效率的要求很高。在这种自动化工具的帮助下，维护者可以将重复性的工作自动化，并专注于更重要的任务，如编写高质量的代码、审核贡献，以及围绕项目创建一个活跃的社区。 我曾经不得不手动完成一些本可以自动化的任务，所以我觉得我有资格分享如何使用 GitHub Actions 来节省时间。 我花了大半天的时间去研究我的一个已经吸引了贡献者的迷你 Python 项目 [https://github.com/larymak/Python-project-Scripts]。我给新的贡献者写热情洋溢的欢迎词，检查最近 pull requests，以确保他们遵守项目的规则比如 README 文件和说明、如果需要的话提供截图，等等。 但我不知道的是，我可以自动执行其中一些任务以及更多任务，以减少自己的工作量。我可以在 GitHub Actions 的帮助下做到这一点。 在本指南中，我将分享更多关于 GitHub Actions 的 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/automate-open-source-projects-with-github-actions/</link>
                <guid isPermaLink="false">64257c76e32a7606487d5e9f</guid>
                
                    <category>
                        <![CDATA[ 开源 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 自动化 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Thu, 30 Mar 2023 10:21:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/03/OOP--4-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/automate-open-source-projects-with-github-actions/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use GitHub Actions to  Automate Open-Source Projects</a>
      </p><p>如今，开发人员经常使用自动化工具来有效地管理任务，并简化他们的日常活动。GitHub Actions 就是这些流行的工具之一。</p><p>你可能会同意我的观点：软件（包括开源软件）对效率的要求很高。在这种自动化工具的帮助下，维护者可以将重复性的工作自动化，并专注于更重要的任务，如编写高质量的代码、审核贡献，以及围绕项目创建一个活跃的社区。</p><p>我曾经不得不手动完成一些本可以自动化的任务，所以我觉得我有资格分享如何使用 GitHub Actions 来节省时间。</p><p>我花了大半天的时间去研究我的一个已经吸引了贡献者的<a href="https://github.com/larymak/Python-project-Scripts">迷你 Python 项目</a>。我给新的贡献者写热情洋溢的欢迎词，检查最近 pull requests，以确保他们遵守项目的规则比如 README 文件和说明、如果需要的话提供截图，等等。</p><p>但我不知道的是，我可以自动执行其中一些任务以及更多任务，以减少自己的工作量。我可以在 GitHub Actions 的帮助下做到这一点。</p><p>在本指南中，我将分享更多关于 GitHub Actions 的信息，以及我如何使用它。我将向你展示如何利用它来自动化项目的各个方面，从欢迎新贡献者到分配任务、检查代码质量等等。我们一起来让你的项目更加高效、更具有交互性。</p><h2 id="github-actions-"><strong>GitHub Actions 是什么</strong></h2><p>GitHub Actions 是一种工具，可让你在 GitHub 仓库中执行不同的自动化操作。它允许你创建自定义工作流，你可以使用这些工作流来自动化开发过程，例如构建、测试和部署代码。</p><p>将此工具与其他 GitHub 功能集成后，你可以转变你的项目管理程序，让每个参与其中的人都更加愉快，更加有参与感。</p><h3 id="-github-actions"><strong>如何在你的仓库中设置 GitHub Actions</strong></h3><p>一切都从根文件夹开始。默认情况下，GitHub Actions 通常集成在你的 GitHub 仓库中，因此你无需在安装时注册单独的账户。但是你需要执行几个步骤才能访问它的功能。</p><ul><li>在你的 GitHub 仓库中，在顶部导航选项卡上，你将看到 <strong><strong>Actions</strong></strong> 选项卡。单击它，你可以访问推荐的工作流列表以及创建自己的工作流的选项。</li><li>现在，根据项目的性质，你可以选择从可用列表中选择一个已经创建的工作流，也可以选择自己创建一个。由于你了解项目的全部内容以及可能需要自动化的内容，因此我建议你自己设置一个新的工作流程。这将使你更好地了解正在发生的事情。</li><li>要设置新的工作流程，请单击 <strong><strong>Set up a workflow yourself</strong></strong>（自行设置工作流程）。这将带你进入一个工作流创建界面，其中包含一个名为 <code>main.yml</code> 的新 YAML 文件。既然你选择编写自己的工作流程，在这里，我应该提到了解 YAML 非常重要。</li></ul><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/03/actions-tab.png" class="kg-image" alt="actions-tab" width="600" height="400" loading="lazy"></figure><p>在幕后发生的事情是，一旦你单击此选项，就会在 <code>main.yml</code> 文件旁边创建两个文件夹。如果你之后访问你的仓库或查看文件名之前的路径，你将看到：<code>.github/workflows/&lt;filename&gt;</code>。</p><ul><li>在 <code>main.yml</code> 文件中是你定义工作流程的地方，在编写完所有内容后，你可以提交更改（commit the changes），就像对仓库进行更改时所做的那样。这样，你就设置了工作流，它将根据 YAML 文件中定义的触发器运行。</li></ul><p>或者，你仍然可以在你最喜欢的代码编辑器中完成这一切。你需要做的就是将仓库克隆到你的计算机上，在项目的根文件夹中创建一个 <code>.github</code> 文件夹，在其中创建另一个名为 <code>workflows</code> 的文件夹，最后添加一个扩展名为 <code>.yml</code> 的文件并将你的脚本写入这个文件。</p><p>在下面的示例中，我将引用我在项目中实现的代码来帮助你理解。</p><h2 id="github-actions--1">GitHub Actions 组件</h2><p>GitHub Actions 主要由三个主要组件组成，包括：</p><ul><li>工作流（Workflows） - 这些是定义自动化过程的规则集。它们在 YAML 文件中定义，该文件存储在 <code>.github/workflows</code> 目录中。</li><li>事件（Events） - 它们启动工作流。例如，你可以将事件设置为在创建 PR 或新开 issue 时运行工作流。要在工作流中定义事件，请使用关键字 <code>on</code> 后跟事件名称。</li></ul><p>例如：</p><pre><code class="language-yaml">on:
    issues:
        types: [opened]
    pull_request_target:
        types: [opened]
</code></pre><ul><li>任务（Jobs） - 这些构成了工作流程。默认情况下，任务是同时运行的。要在给定的工作流中定义你的任务，请使用关键字 <code>jobs</code>，后跟每个任务及其配置的唯一标识符。</li></ul><p>例如：</p><pre><code class="language-yaml">jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.10
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
</code></pre><p>所有这些组件确保一组特定的规则被成功执行。现在看看我们的项目。</p><h2 id="-issue-pull-request">如何自动化管理 issue 和 pull request</h2><p>管理 issue 和 pull request 可能是一项非常耗时的工作，尤其是对于大型开源项目。但有了 GitHub Actions，维护者就可以把这些流程自动化，把更多的时间花在编码和与社区互动上。</p><h3 id="-issue-pull-request-">如何创建 issue 和 pull request 模板</h3><p>如果你是一个活跃的开源贡献者，你有可能遇到过一个指南，告诉你在创建 issue 或提交 PR 时应该包括什么。这种模板的主要目的是提供指导，确保贡献者提供所有必要的信息。</p><p>现在，让我们看看你如何在你的项目中创建这个模板：</p><ul><li>第一步是确保在仓库的 root 中有一个 <code>.github</code> 目录，如果你还没有的话。</li><li>在 <code>.github</code> 文件夹内，创建两个文件夹 <code>ISSUE_TEMPLATE</code> 和 <code>PULL_REQUEST_TEMPLATE</code>。</li><li>在这两个文件夹中，添加代表你想自动化内容的 markdown 文件：例如，你可以将 <code>feature_request.md</code> 和 <code>issue_report.md</code> 作为 issue 模板，将 <code>pull_request_template.md</code> 作为 PR 模板。</li></ul><p>下面是我在 <code>pull_request_template.md</code> 文件中写的内容，作为参考。这是一个简单的指南，告诉贡献者在提交 pull request 之前应该包括什么。</p><pre><code class="language-markdown">**Related Issue(s):**
Please provide a title for this pull request.

**Description:**
Please provide a brief description of the changes you are proposing.

**Checklist:**

-   [ ] I have read and followed the [contributing guidelines](/CONTRIBUTING.md).
-   [ ] I have included a README file for my project.
-   [ ] I have updated the main README file where necessary.
-   [ ] I have included a requirements.txt file.
-   [ ] I have added tests that prove my changes are effective or that my feature works.
-   [ ] All new and existing tests pass.

**Screenshots**
If applicable, add screenshots to help explain behavior of your code.

**Additional Notes:**
Please provide any additional information about the changes you are proposing.
</code></pre><p>如需更详细的解释，请在<a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates">这里</a>查看 GitHub Action 关于 issue 和 pull request 模板的文档。</p><h3 id="-">欢迎新的贡献者并表彰社区的努力</h3><p>作为一个维护者，与社区打交道是很重要的，因为你有机会与你的朋辈直接互动并获得反馈。但如果你正在运行一个吸引众多贡献者的大型项目，你可能不会经常有机会直接与社区互动。</p><p>在 GitHub Actions 的帮助下，你可以完成其中的一些任务，比如欢迎新的贡献者，认可他们的努力，并为现有的社区成员创造一个积极的氛围。</p><p>如果你正在管理一个小项目，你可能可以直接与社区互动，但仍然利用自动化来完成一些工作。</p><p>例如，这里有一些示例代码，当新的贡献者创建 pull request 或在仓库上创建一个新的 issue 时，我通过这些代码来欢迎他们。在其中，你可以看到我有一条信息，感谢他们的努力，也向他们保证我将尽快审核他们提交的修改。尽管如此，如果需要额外的东西或提出新的想法，我还是会通过对话去跟进。</p><pre><code class="language-yaml">name: Welcome New Contributors

on:
    issues:
        types: [opened]
    pull_request_target:
        types: [opened]

jobs:
    welcome:
        runs-on: ubuntu-latest
        steps:
            - name: Welcome Issue
              if: github.event_name == 'issues'
              uses: actions/github-script@v5
              with:
                  script: |
                      const issue = context.issue;
                      const repo = context.repo;
                      const issueAuthor = context.payload.sender.login;
                      const welcomeMessage = `
                        Hi @${issueAuthor}! :wave:
                        Thank you for creating an issue in our repository! We appreciate your contribution and will get back to you as soon as possible.
                      `;
                      github.rest.issues.createComment({
                        ...repo,
                        issue_number: issue.number,
                        body: welcomeMessage
                      });
            - name: Welcome Pull Request
              if: github.event_name == 'pull_request_target'
              uses: actions/github-script@v5
              with:
                  script: |
                      const pr = context.issue;
                      const repo = context.repo;
                      const prAuthor = context.payload.sender.login;
                      const welcomeMessage = `
                        Hi @${prAuthor}! :wave:
                        Thank you for submitting a pull request! We appreciate your contribution and will review your changes as soon as possible.
                      `;
                      github.rest.issues.createComment({
                        ...repo,
                        issue_number: pr.number,
                        body: welcomeMessage
                      });
</code></pre><p>除了这个简单的工作流程外，如果你正在领导一个更复杂的项目，你可以考虑编写一个更详细的工作流程，能够自动为贡献者分配徽章、标签或自定义标题。</p><p>同样，你也可以选择添加一个工作流，在贡献者的 pull request 被合并或 issue 被关闭时对其表示感谢。你可以查看 <a href="https://docs.github.com/en/actions/managing-issues-and-pull-requests/using-github-actions-for-project-management">GitHub Action 文档</a>，了解详细的指南。</p><h2 id="-qa-">如何实现代码 QA 的自动化</h2><p>对于大多数开发人员来说，编写高质量的代码是非常重要的，特别是如果他们正在创建与消费者有关的应用。虽然一个项目的成功取决于精心编写和测试的代码，但有时审核修改可能会花些时间，甚至会使用户正需要的功能延迟上线。</p><p>在代码自动化工具的帮助下，你可以保持一致的编码风格，并快速和容易地识别潜在的错误，保持你的项目整洁。</p><p>那么，如何用 GitHub Actions 设置持续集成（CI），整合代码 formatting 和 linting 工具，并在项目中使用自动化代码审核服务？</p><p>持续集成（CI）帮助你实现流程自动化，如构建、测试和验证代码更改。就像其他自动化代码一样，CI 代码写在一个 <code>.yml</code> 文件中，存储在 <code>.github/workflows</code> 文件夹中。</p><p>下面是一个 Python 项目 CI 工作流程的例子，它在向仓库的 <code>main</code> 分支 pull request 时运行。它测试 Python 多个版本的代码，安装必要的依赖，并使用 <code>unittest</code> 模块运行测试。</p><pre><code class="language-yaml">name: Python CI

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.7, 3.8, 3.9, 3.10]

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run tests
      run: |
        python -m unittest discover
</code></pre><p>除了上面的代码，如果你想保持代码风格的一致性，你可以整合代码 formatting 和 linting 工具，如 <code>black</code>、<code>isort</code> 或 <code>flake8</code>。为此，你只需要在 <code>requirements.txt</code> 文件中加入这些工具，该文件已经包含在上述代码中。下面的代码块运行这些工具。</p><pre><code class="language-yaml">#...
    # ...
    - name: Run black for code formatting
      run: |
        black --check .
    - name: Run isort for import sorting
      run: |
        isort --check --diff .
    - name: Run flake8 for linting
      run: |
        flake8 .
</code></pre><p>如果它发现任何代码格式问题，CI 构建将失败。要解决这个问题，你必须手动检查日志。请查看这个关于<a href="https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python">构建和测试 Python</a> 的指南，了解更多的例子。</p><p>上面的想法只是你可以在项目中使用的一些自动程序。对于一个更复杂的应用，你可以考虑增加工作流程，用 Sphinx 或 MkDocs 等工具生成文档，自动化依赖性更新，自动化发布管理和项目跟踪，等等。</p><h2 id="-github-actions-">关于构建自定义 GitHub Actions 的提示</h2><p>在 <a href="https://github.com/marketplace?type=actions">GitHub Actions 市场</a>上已经有很多预制的 action。但有时你可能想要或需要定制工作流程以满足你的需求。</p><p>为此，你可以选择 JavaScript 或 Docker 容器，并与社区分享。</p><p>这里有一些可以遵循的最佳做法：</p><ul><li>了解问题 - 就像其他项目一样，在开始构建之前，确保你了解要解决的问题以及你将如何解决它。</li><li>选择正确的技术栈 - 如上所述，可以使用 JavaScript 或 Docker 编写 GitHub Actions。请确保选择最适合你的需求和理解的方式。</li><li>确保你遵守最佳的编码实践，以便别人能够轻松地理解和阅读你的代码。</li><li>利用已有的软件包，如 <code>@actions/core</code> 和 <code>@actions/github</code>，它们提供了与 GitHub Actions 环境和 GitHub API 的简单互动。</li><li>在成功创建自己的工作流程后，你有可能想要发布它。无论你是否发布，请确保测试你的 action 是否有潜在的问题或错误。</li></ul><p>通过这些简单的技巧，你可以创建一个自定义的 GitHub Action 来自动完成项目中的一些主要/基本任务。除了上面的提示，你可以在<a href="https://docs.github.com/en/actions/creating-actions">官方文档</a>中找到更多关于创建自定义 action 的详细信息。</p><h2 id="--1">总结</h2><p>在本指南中，我们看到了 GitHub Actions 能给我们的项目带来的好处。它不仅简化了生产流程，而且还允许我们自定义 action 以适应项目需要。</p><p>这仅仅是我们能实现的东西的一小部分。我鼓励你更好地理解和探索不同的方法，以进一步用 GitHub Actions 改进你的开源项目。让我们拥抱自动化，利用它来完成更多工作。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何用 SSH 密钥在一台机器上管理多个 GitHub 账户 ]]>
                </title>
                <description>
                    <![CDATA[ 对大多数开发者来说，有时候需要在同一台机器上管理多个 GitHub 账户。当我碰巧换了 Mac，或者需要用新的工作账户进行 Git 推送时，我经常都需要停下来去搜索如何实现。 我的懒惰使我没有把过程记录下来，也没有能力记住步骤，这使我花了相当多的时间从网上找零碎的东西，然后以某种方式使它发挥作用。 我相信你们当中有很多人都有过这样的经历，也有很多人只是在等待下一次发生同样的事情（包括我自己！）。本文介绍的方法是为了帮助我们所有人。 1. 生成 SSH 密钥 在生成 SSH 密钥之前，我们可以检查一下我们是否有任何现有的 SSH 密钥：ls -al ~/.ssh 这将列出所有现有的公钥和私钥对，如果存在的话。 如果 ~/.ssh/id_rsa 是可用的，我们可以重新使用它，否则我们可以先通过运行以下代码来生成一个默认 ~/.ssh/id_rsa 的密钥： ssh-keygen -t rsa 对于保存密钥的位置，按回车键接受默认位置。一个私钥和公钥 ~/.ssh/id_rsa.pub 将在默认的 SSH 位置 ~/.ssh/ 创建。 让我们为我们的个人账户使用这个默认的密钥对 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/manage-multiple-github-accounts-the-ssh-way/</link>
                <guid isPermaLink="false">638df211832e3f0781763d0b</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Miya Liu ]]>
                </dc:creator>
                <pubDate>Tue, 29 Nov 2022 12:38:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/12/1_OnUzsFnzyPzklZWjgRLr5g.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/manage-multiple-github-accounts-the-ssh-way-2dadc30ccaca/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to manage multiple GitHub accounts on a single machine with SSH keys</a>
      </p><p>对大多数开发者来说，有时候需要在同一台机器上管理多个 GitHub 账户。当我碰巧换了 Mac，或者需要用新的工作账户进行 Git 推送时，我经常都需要停下来去搜索如何实现。</p><p>我的懒惰使我没有把过程记录下来，也没有能力记住步骤，这使我花了相当多的时间从网上找零碎的东西，然后以某种方式使它发挥作用。</p><p>我相信你们当中有很多人都有过这样的经历，也有很多人只是在等待下一次发生同样的事情（包括我自己！）。本文介绍的方法是为了帮助我们所有人。</p><h4 id="1-ssh-"><strong>1. </strong>生成 SSH 密钥</h4><p>在生成 SSH 密钥之前，我们可以检查一下我们是否有任何现有的 SSH 密钥：<code>ls -al ~/.ssh</code> 这将列出所有现有的公钥和私钥对，如果存在的话。</p><p>如果 <code>~/.ssh/id_rsa</code> 是可用的，我们可以重新使用它，否则我们可以先通过运行以下代码来生成一个默认 <code>~/.ssh/id_rsa</code> 的密钥：</p><pre><code>ssh-keygen -t rsa</code></pre><p>对于保存密钥的位置，按回车键接受默认位置。一个私钥和公钥 <code>~/.ssh/id_rsa.pub</code> 将在默认的 SSH 位置 <code>~/.ssh/</code> 创建。</p><p>让我们为我们的个人账户使用这个默认的密钥对。</p><p>对于工作账户，我们将创建不同的 SSH 密钥。下面的代码将生成 SSH 密钥，并将标签为 “email@work_mail.com” 的公钥保存到 <code>~/.ssh/id_rsa_work_user1.pub</code> 中。</p><pre><code class="language-bash">$ ssh-keygen -t rsa -C "email@work_mail.com" -f "id_rsa_work_user1"
</code></pre><p>我们创建了两个不同的密钥：</p><pre><code class="language-bash">~/.ssh/id_rsa
~/.ssh/id_rsa_work_user1</code></pre><h4 id="2-ssh-github-"><strong>2. </strong>将新的 SSH 密钥添加到相应的 GitHub 账户中</h4><p>我们已经准备好了 SSH 公钥，我们将要求 GitHub 账户信任我们创建的密钥。这是为了避免每次进行 Git 推送时都要输入用户名和密码的麻烦。</p><p>复制公钥 <code>pbcopy &lt; ~/.ssh/id_rsa.pub</code>，然后登录你的个人 GitHub 账户：</p><ul><li>转到 <code>Settings</code></li><li>在左边的菜单中选择 <code>SSH and GPG keys</code></li><li>点击 <code>New SSH key</code>，提供一个合适的标题，并将密钥粘贴在下面的方框中</li><li>点击 <code>Add key</code> - 就完成了！</li></ul><p>对于工作账户，使用相应的公钥（<code>pbcopy &lt; ~/.ssh/id_rsa_work_user1.pub</code>），在 GitHub 工作账户中重复上述步骤。</p><h4 id="3-ssh-agent-ssh-"><strong>3 . </strong>在 ssh-agent 上注册新的 SSH 密钥</h4><p>为了使用这些密钥，我们必须在我们机器上的 <strong>ssh-agent</strong> 上注册它们。使用 <code>eval "$(ssh-agent -s)"</code> 命令确保 ssh-agent 运行。像这样把密钥添加到 ssh-agent 中：</p><pre><code class="language-bash">ssh-add ~/.ssh/id_rsa
ssh-add ~/.ssh/id_rsa_work_user1</code></pre><p>让 ssh-agent 为不同的 SSH 主机使用各自的 SSH 密钥。</p><p>这是最关键的部分，我们有两种不同的方法：</p><p>使用 SSH 配置文件（第 4 步），以及在 ssh-agent 中每次只有一个有效的 SSH 密钥（第 5 步）。</p><h3 id="4-ssh-"><strong><strong><strong>4. 创建 SSH 配置文件</strong></strong></strong></h3><p>在这里，我们实际上是为不同的主机添加 SSH 配置规则，说明在哪个域名使用哪个身份文件。</p><p>SSH 配置文件将在 <strong>~/.ssh/config</strong> 中。如果有的话，请编辑它，否则我们可以直接创建它。</p><pre><code class="language-bash">$ cd ~/.ssh/
$ touch config           // Creates the file if not exists
$ code config            // Opens the file in VS code, use any editor</code></pre><p>在 <code>~/.ssh/config</code> 文件中为相关的 GitHub 账号做类似于下面的配置项：</p><pre><code class="language-bash"># Personal account, - the default config
Host github.com
   HostName github.com
   User git
   IdentityFile ~/.ssh/id_rsa
   
# Work account-1
Host github.com-work_user1    
   HostName github.com
   User git
   IdentityFile ~/.ssh/id_rsa_work_user1</code></pre><p>“work_user1” 是工作账户的 GitHub 用户 ID。</p><p>“github.com-work_user1” 是用来区分多个 Git 账户的记号。你也可以使用 “work_user1.github.com” 记号。确保与你使用的主机名记号一致。当你克隆一个仓库或为本地仓库设置 remote origin 时，这一点很重要。</p><p>上面的配置要求 ssh-agent：</p><ul><li>使用 <strong>id_rsa</strong> 作为任何使用 <strong>@github.com</strong> 的 Git URL 的密钥</li><li>对任何使用 <strong>@github.com-work_user1</strong> 的 Git URL 使用 <strong>id_rsa_work_user1</strong> 密钥</li></ul><h3 id="5-ssh-agent-ssh-"><strong>5. </strong>在 ssh-agent 中每次有一个活跃的 SSH 密钥</h3><p>这种方法不需要 SSH 配置规则。相反，我们手动确保在进行任何 Git 操作时，ssh-agent 中只有相关的密钥。</p><p><code>ssh-add -l</code> 会列出所有连接到 ssh-agent 的 SSH 密钥。把它们全部删除，然后添加你要用的那个密钥。</p><p>如果是要推送到个人的 Git 账号：</p><pre><code class="language-bash">$ ssh-add -D            //removes all ssh entries from the ssh-agent
$ ssh-add ~/.ssh/id_rsa                 // Adds the relevant ssh key</code></pre><p>现在 ssh-agent 已经有了映射到个人 GitHub 账户的密钥，我们可以向个人仓库进行 Git 推送。</p><p>要推送到工作的 GitHub account-1，需要改变 SSH 密钥与 ssh-agent 的映射关系，删除现有的密钥，并添加与 GitHub 工作账号映射的 SSH 密钥。</p><pre><code class="language-bash">$ ssh-add -D
$ ssh-add ~/.ssh/id_rsa_work_user1</code></pre><p>目前，ssh-agent 已经将密钥映射到了工作的 GitHub 账户，你可以将 Git 推送到工作仓库。不过这需要一点手动操作。</p><h3 id="-git-remote-url">为本地仓库设置 git remote url</h3><p>一旦我们克隆/创建了本地的 Git 仓库，确保 Git 配置的用户名和电子邮件正是你想要的。GitHub 会根据提交（commit）描述所附的电子邮件 ID 来识别任何提交的作者。</p><p>要列出本地 Git 目录中的配置名称和电子邮件，请执行 <code>git config user.name</code> 和 <code>git config user.email</code>。如果没有找到，可以进行更新。</p><pre><code class="language-bash">git config user.name "User 1"   // Updates git config user name
git config user.email "user1@workMail.com"</code></pre><h3 id="6-"><strong>6. </strong>克隆仓库</h3><p>注意：如果我们在本地已经有了仓库，那么请查看第 7 步。</p><p>现在配置已经好了，我们可以继续克隆相应的仓库了。在克隆时，注意我们要使用在 SSH 配置中使用的主机名。</p><p>仓库可以使用 Git 提供的 clone 命令来克隆：</p><pre><code>git clone git@github.com:personal_account_name/repo_name.git</code></pre><p>工作仓库将需要用这个命令来进行修改：</p><pre><code class="language-bash">git clone git@github.com-work_user1:work_user1/repo_name.git</code></pre><p>这个变化取决于 SSH 配置中定义的主机名。@ 和 : 之间的字符串应该与我们在 SSH 配置文件中给出的内容相匹配。</p><h3 id="7-"><strong>7. </strong>对于本地存在的版本库</h3><p><strong>如果我们有已经克隆的仓库：</strong></p><p>列出该仓库的 Git remote，<code>git remote -v</code></p><p>检查该 URL 是否与我们要使用的 GitHub 主机相匹配，否则就更新 remote origin URL。</p><pre><code class="language-bash">git remote set-url origin git@github.com-worker_user1:worker_user1/repo_name.git</code></pre><p>确保 @ 和 : 之间的字符串与我们在 SSH 配置中给出的主机一致。</p><p><strong>如果你要在本地创建一个新的仓库：</strong></p><p>在项目文件夹中初始化 Git <code>git init</code>。</p><p>在 GitHub 账户中创建新的仓库，然后将其作为 Git remote 添加给本地仓库。</p><pre><code class="language-bash">git remote add origin git@github.com-work_user1:work_user1/repo_name.git </code></pre><p>确保 @ 和 : 之间的字符串与我们在 SSH 配置中给出的主机相匹配。</p><p>推送初始提交到 GitHub 仓库：</p><pre><code class="language-bash">git add .
git commit -m "Initial commit"
git push -u origin master</code></pre><p>我们完成了！</p><p>依据正确的主机，添加或更新的本地 Git 目录的 Git remote，选择正确的 SSH 密钥来验证我们的身份。有了以上这些，我们的 <code>git</code> 操作应该可以无缝运行了。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ GitHub 搜索技巧——如何在 GitHub 上更有效地搜索 issue、repo 和更多信息 ]]>
                </title>
                <description>
                    <![CDATA[ 当我还是一个开源贡献的初学者时，我最大的挑战之一是找到正确的项目/issue 来为其做贡献。 在很长一段时间里，我依靠互联网上不同作者创作的资源（顺便说一下，这些资源很好）。但我一直想找到一种解决这个问题的方法——一种我可以搜索和跟踪适合我的技能组合的项目的方法。 让我们承认一件事：与谷歌不同，在 GitHub 上搜索并不容易。但作为一个开发者，你有可能每天都会与 GitHub 或 GitLab 打交道。 现在的问题不是你用这些版本控制系统做什么，而是你如何使用它们。就像掌握谷歌搜索技巧 [https://chinese.freecodecamp.org/news/how-to-google-like-a-pro-10-tips-for-effective-googling/] 对任何普通互联网用户来说都是必不可少的，我相信对于开发者来说，学会如何有效地在 GitHub 搜索也是至关重要的。 在这篇文章中，我们将看看可以用来正确地在 GitHub 搜索的不同技巧。你将学习如何通过搜索：  * 问题（issues）和拉取请求（pull requests）  * 仓库（repos ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/github-search-tips/</link>
                <guid isPermaLink="false">6361e1f873e419079177bbae</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 02 Nov 2022 04:25:30 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/11/Search--2-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/github-search-tips/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">GitHub Search Tips – How to Search Issues, Repos, and More Effectively on GitHub</a>
      </p><p>当我还是一个开源贡献的初学者时，我最大的挑战之一是找到正确的项目/issue 来为其做贡献。</p><p>在很长一段时间里，我依靠互联网上不同作者创作的资源（顺便说一下，这些资源很好）。但我一直想找到一种解决这个问题的方法——一种我可以搜索和跟踪适合我的技能组合的项目的方法。</p><p>让我们承认一件事：与谷歌不同，在 GitHub 上搜索并不容易。但作为一个开发者，你有可能每天都会与 GitHub 或 GitLab 打交道。</p><p>现在的问题不是你用这些版本控制系统做什么，而是你如何使用它们。就像掌握<a href="https://chinese.freecodecamp.org/news/how-to-google-like-a-pro-10-tips-for-effective-googling/">谷歌搜索技巧</a>对任何普通互联网用户来说都是必不可少的，我相信对于开发者来说，学会如何有效地在 GitHub 搜索也是至关重要的。</p><p>在这篇文章中，我们将看看可以用来正确地在 GitHub 搜索的不同技巧。你将学习如何通过搜索：</p><ul><li>问题（issues）和拉取请求（pull requests）</li><li>仓库（repositories）</li><li>用户（users）</li><li>主题（topics）</li></ul><p>以及更多。让我们开始吧。</p><h2 id="github-">GitHub 搜索查询</h2><p>为了在互联网上找到关于某件事情的详细信息，你需要有正确的搜索技巧。这在 GitHub 上没有任何不同——要找到详细的信息，你可以利用常见的过滤、排序和搜索技术，轻松找到特定项目的 issues 和 pull requests。</p><p>即使你能在互联网上找到许多不同项目的资源，但当你想自己进行搜索时，主要问题就来了：你该如何开始？你应该使用哪些关键词来找到正确的结果？</p><p>大多数维护者倾向于用 issues 来标记他们的项目，这使得贡献者更容易找到合适的项目。下面列出了一些在使用 GitHub 时可能会对你有所帮助的小技巧。</p><h3 id="-github-issues-pull-requests">如何在 GitHub 上搜索 issues 和 pull requests</h3><p>搜索 issues 和相关的 pull requests，是寻找要贡献的项目的最常见的方法之一。这里有一些技巧，你可以用来轻松找到可靠的答案：</p><p>1、<strong><strong><a href="https://github.com/search?q=is%3Aissue+is%3Aopen+label%3Abeginner&amp;type=issues">is:issue is:open label:beginner</a></strong></strong>——这个特别的查询会列出所有具有开放的、并标有 <code>beginner</code> 的 issues 的项目。</p><p>2、<strong><strong><a href="https://github.com/search?q=is%3Aissue+is%3Aopen+label%3Aeasy&amp;type=issues">is:issue is:open label:easy</a></strong></strong>——这将列出所有被标记为 <code>easy</code> 的开放 issues。</p><p>3、<strong><strong><a href="https://github.com/search?q=is%3Aissue+is%3Aopen+label%3Afirst-timers-only&amp;type=issues">is:issue is:open label:first-timers-only</a></strong></strong>——这将列出所有欢迎第一次做贡献的人们的开放 issues。</p><p>4、<strong><strong><a href="https://github.com/search?q=is%3Aissue+is%3Aopen+label%3Agood-first-bug&amp;type=issues">is:issue is:open label:good-first-bug</a></strong></strong>——这将列出带有 <code>good-first-bug</code> 标签的开放 issues 的项目，以吸引贡献者为其工作。</p><p>5、<a href="https://github.com/search?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22&amp;type=issues"><strong><strong>is:issue is:open label:"good first issue"</strong></strong></a>——这将列出所有带有 <code>good first issue</code> 标签的开放 issues，意味着它是初学者入门的好地方。</p><p>6、<strong><strong><a href="https://github.com/search?q=is%3Aissue+is%3Aopen+label%3Astarter&amp;type=issues">is:issue is:open label:starter</a></strong></strong>——这将列出整个 GitHub 中所有标有 <code>starter</code> 的开放 issues。</p><p>7、<a href="https://github.com/search?q=is%3Aissue+is%3Aopen+label%3Aup-for-grabs&amp;type=issues"><strong><strong>is:issue is:open label:up-for-grabs</strong></strong></a>——这将列出开放的 issues，如果你有必要的技能，就可以进行工作。</p><p>8、<a href="https://github.com/search?q=no%3Aproject+type%3Aissue+is%3Aopen&amp;type=issues"><strong><strong>no:project type:issue is:open</strong></strong></a>——这将列出所有没有分配给特定项目的开放 issues。</p><p>9、<a href="https://github.com/search?q=no%3Amilestone+type%3Aissue+is%3Aopen&amp;type=issues"><strong><strong>no:milestone type:issue is:open</strong></strong></a>——很多时候，项目是用里程碑来追踪的。但如果你想找到没有被跟踪的 issues，这个搜索查询将为你列出这些项目。</p><p>10、<strong><strong><a href="https://github.com/search?q=no%3Alabel+type%3Aissue+is%3Aopen&amp;type=issues">no:label type:issue is:open</a></strong></strong>——这将列出所有没有标签的开放 issues。</p><p>11、<a href="https://github.com/search?q=is%3Aissue+is%3Aopen+no%3Aassignee&amp;type=issues"><strong><strong>is:issue is:open no:assignee</strong></strong></a>——这显示所有尚未分配给某人的开放 issues。</p><h3 id="-">如何搜索仓库</h3><p>默认情况下，要进行搜索，你需要在搜索栏中输入仓库的名称，然后就可以得到一些搜索结果。</p><p>但你找到你想要的仓库的机会非常低。</p><p>让我们看看有什么方法可以缩小搜索范围：</p><p><strong>如何通过名称、描述/README 查找</strong></p><p>当你通过 README 文件的名称和描述进行搜索时，需要注意的是，你的搜索短语应该以 <code>in</code> 限定词开始。这样就可以在你要找的东西的“内部”进行搜索。</p><p><strong><strong>示例</strong></strong></p><ul><li>使用 <code>in:name</code>。比方说，你正在寻找资源，以了解更多关于数据科学的信息。在这种情况下，你可以使用 <code>Data Science in:name</code> 命令，它将列出仓库名称中含有 Data Science 的仓库。</li><li>使用 <code>in:description</code>。如果你想找到具有特定描述的仓库，例如，仓库的描述中包含 “freeCodeCamp” 一词，我们的搜索将是：<code>freecodecamp in:description</code>。</li><li>使用 <code>in:readme</code>。你用它来搜索一个文件的 README 中的某一短语。如果我们想找到 README 中包含 freecodecamp 这个词的仓库，我们的搜索将是：<code>freecodecamp in:readme</code>。</li><li>使用 <code>in:topic</code>。你用它来查找某个短语或单词是否被标注在主题中。例如，要找到所有在主题中列出 freecodecamp 的仓库，我们的搜索将是：<code>freecodecamp in:topic</code>。</li></ul><p>你也可以结合多个搜索查询，进一步缩小搜索范围。</p><p><strong>如何按 Stars、Forks 查找</strong></p><p>你也可以根据项目的 star 和 fork 的数量来搜索仓库。这使你更容易知道该项目有多受欢迎。</p><p><strong><strong>示例</strong></strong></p><ul><li>使用 <code>stars:n</code>。如果你搜索一个有 1000 star 的仓库，那么你的搜索查询将是 <code>stars:1000</code>。这将列出正好有 1000 star 的仓库。</li><li>使用 <code>forks:n</code>。这指定了一个仓库应该有的 fork 数。如果你想找到少于 100 个 fork 的仓库，你的搜索将是：<code>forks:&lt;100</code>。</li></ul><p>你可以随时使用关系运算符，如 <code>&lt;</code>、<code>&gt;</code>、<code>&lt;=</code>、<code>&gt;=</code> 和 <code>..</code> 来帮助你进一步缩小搜索范围。</p><p><strong>如何按语言查找</strong></p><p>在 GitHub 上搜索的另一个很酷的方法是按语言搜索。这可以帮助你过滤出特定语言的仓库。</p><p><strong><strong>示例</strong></strong></p><ul><li>使用 <code>language:LANGUAGE</code>。例如，如果你想找到用 PHP 编写的仓库，你的搜索将是：<code>language:PHP</code>。</li></ul><p><strong>如何按组织名称查找</strong></p><p>你也可以搜索由一个特定组织维护或创建的仓库/项目。为此，你需要用关键词 <code>org:...</code> 来开始你的搜索，然后是组织名称。</p><p>例如，如果你搜索 <code>org:freecodecamp</code>，它将列出与 freeCodeCamp 相匹配的仓库。</p><p><strong>如何按日期查找</strong></p><p>如果你希望你的结果基于一个特定的日期，你可以使用这些关键词之一进行搜索：<code>created</code>、<code>updated</code>、<code>merged</code> 和 <code>closed</code>。这些关键词应该伴随着格式为 <code>YYYY-MM-DD</code> 的日期一起使用。</p><p><strong><strong>示例</strong></strong></p><ul><li>使用 <code>keyword:YYYY-MM-DD</code>。举个例子，我们想搜索所有在 2022-10-01 之后创建的带有 freeCodeCamp 这个词的仓库。那么我们的搜索将是：<code>freecodecamp created:&gt;2022-10-01</code>。</li></ul><p>你也可以使用 <code>&lt;</code>、<code>&gt;</code>、<code>&gt;=</code> 和 <code>&lt;=</code> 来搜索指定日期之后、之前和指定日期的日期。要在一个范围内搜索，你可以使用 <code>...</code>。</p><p><strong>如何通过许可证查找</strong></p><p>当你在寻找一个可以贡献的项目时，许可证是非常重要的。不同的许可证对贡献者可以做什么或不可以做什么给予不同的权利。</p><p>为了使你更容易找到有正确许可证的项目，你需要对许可证有一个很好的了解。你可以在<a href="https://www.freecodecamp.org/news/how-open-source-licenses-work-and-how-to-add-them-to-your-projects-34310c3cf94/">这里</a>阅读更多关于它们的信息。</p><p><strong><strong>示例</strong></strong></p><ul><li>使用 <code>license:LICENSE_KEYWORD</code>。这是一个搜索具有特定许可证的项目的好方法。例如，要搜索具有 MIT 许可证的项目，你可以使用 <code>license:MIT</code>。</li></ul><p><strong>如何按可见度搜索</strong></p><p>你也可以根据仓库的可见性来进行搜索。在这种情况下，你可以使用 public 或者 private。这将分别匹配公共或私人仓库中的 issues 和 pull requests。</p><p><strong><strong>示例</strong></strong></p><ul><li>使用 <code>is:public</code>。这将显示一个公共仓库的列表。让我们举个例子，我们想搜索所有由 freeCodCamp 拥有的仓库。那么我们的搜索将是：<code>is:public org:freecodecamp</code>。</li><li>使用 <code>is:private</code>。这个查询是为了列出所有在给定的搜索查询下的私有仓库。</li></ul><h2 id="--1"><strong>总结</strong></h2><p>尽管我们在这里已经涵盖了许多搜索查询，但你总是可以发挥创意，将多个参数组合在一起，进一步缩小搜索范围。</p><p>对于更多的资源和更多的搜索参数，你可以随时参考 <a href="https://docs.github.com/en/search-github/searching-on-github">GitHub Docs</a> 或利用<a href="https://github.com/search/advanced?">高级 GitHub 搜索方法</a>。这些方法总是能派上用场，因为它们提供了更多的归档选项。</p><p>你可以使用大量的搜索参数，使你在 GitHub 上的日常活动更容易。希望这能帮助你更容易、更有效地使用这个平台。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何在你的项目中使用 GitHub Super Linter ]]>
                </title>
                <description>
                    <![CDATA[ 当你开始一个新的项目时，你可能需要添加多个 linting 工具来美化你的代码并防止简单的错误。 你经常会使用多个 linters，其中一个可能支持 npm 安装，另一个可能有 PyPI 安装，等等。你也会想在你的 CI 中设置一些自动化来运行这些 linters，但这个过程是相当乏味的😫。 在这篇文章中，我将向你展示如何使用 GitHub Super Linter，一个可以解决所有这些问题的单一 linter。我的大部分个人项目也都使用 GitHub Super Linter，我个人发现它是一个大救星。 为什么需要 Linting Linting 本质上是一种静态代码分析的形式。它根据一些规则来分析你写的代码，以找出风格上或程序上的错误。可以把它看作是一种在软件中标记出可疑用法的工具。 linter 可以通过以下方式帮助你节省大量的时间：  * 防止有破坏性（broken）的代码被推送  * 帮助建立编码的最佳实践  * 建立代码布局和格式的准则  * 帮助代码审查变得更加顺畅  * 标记出你的代码中的语法错误的 bug 鉴于提示工具的作用，你最好在任何代码审查发生之前 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/github-super-linter/</link>
                <guid isPermaLink="false">631f31fc5826d307a3f9c83a</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Mon, 12 Sep 2022 11:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/09/github-on-the-hunt-for-a-new-diversity-lead-developers-techworld-github-universe-png-800_450.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/github-super-linter/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use GitHub Super Linter in Your Projects</a>
      </p><!--kg-card-begin: markdown--><p>当你开始一个新的项目时，你可能需要添加多个 linting 工具来美化你的代码并防止简单的错误。</p>
<p>你经常会使用多个 linters，其中一个可能支持 npm 安装，另一个可能有 PyPI 安装，等等。你也会想在你的 CI 中设置一些自动化来运行这些 linters，但这个过程是相当乏味的😫。</p>
<p>在这篇文章中，我将向你展示如何使用 GitHub Super Linter，一个可以解决所有这些问题的单一 linter。我的大部分个人项目也都使用 GitHub Super Linter，我个人发现它是一个大救星。</p>
<h2 id="linting">为什么需要 Linting</h2>
<p>Linting 本质上是一种静态代码分析的形式。它根据一些规则来分析你写的代码，以找出风格上或程序上的错误。可以把它看作是一种在软件中标记出可疑用法的工具。</p>
<p>linter 可以通过以下方式帮助你节省大量的时间：</p>
<ul>
<li>防止有破坏性（broken）的代码被推送</li>
<li>帮助建立编码的最佳实践</li>
<li>建立代码布局和格式的准则</li>
<li>帮助代码审查变得更加顺畅</li>
<li>标记出你的代码中的语法错误的 bug</li>
</ul>
<p>鉴于提示工具的作用，你最好在任何代码审查发生之前对推送到你的仓库的每一段代码运行一个 linter。这无疑有助于你写出更好、更可读、更稳定的代码。</p>
<p>下面是一个使用 <a href="https://github.com/psf/black">Black</a> 的例子，这是一个专注于代码格式化的 Python 的提示工具。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/08/Black-Example.png" alt="Black 所做的格式化修改" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Black 所做的格式化修改</figcaption>
</figure>
<p>GitHub Super Linter 在为你的项目带来这些功能方面可以提供相当大的帮助，轻松而有效。GitHub Super Linter 是一个由多个常用 linters 组成的组合，你可以非常容易地使用。它可以让你为这些 linters 设置自动运行，也可以在一个项目中管理多个 linters！它还有大量的自定义功能。</p>
<p>它还有大量的环境变量的定制功能，可以帮助你根据你的个人仓库定制 Super Linter。</p>
<h2 id="githubactionsgithubsuperlinter">如何在 GitHub Actions 中使用 GitHub Super Linter</h2>
<p>Super Linter 主要是为在 GitHub Action 中运行而设计的，这也是我在相当长一段时间内使用它的方式。我们将首先讨论这个问题。为了跟上进度，你应该在你的仓库里创建一个新的 GitHub Action。让我们在<code>.github/workflows/linter.yml</code> 创建一个新文件。</p>
<p>要继续操作，我将假设你知道 GitHub Action 的基本语法。但如果你不知道或需要快速复习，我建议你看一下这个<a href="https://docs.github.com/en/actions/quickstart">快速入门指南</a>。</p>
<h3 id="action">怎样创建一个 Action</h3>
<p>我们已经有了一个空白文件<code>.github/workflows/linter.yml</code>，现在我们要用补充 Action 相关内容，你可以用它来测试项目。</p>
<p>我们将首先给我们的 action 一个名字（name）。这就是出现在 GitHub Action 状态检查下的内容：</p>
<pre><code class="language-yaml">name: Lint Code Base
</code></pre>
<p>接下来，让我们为我们的 action 指定触发器。这将控制什么时候应该对你的代码库进行检查的问题。在这里，我们告诉它在每次推送（push）和每次拉取请求时（pull_request）都要运行 lint。</p>
<pre><code class="language-yaml">name: Lint Code Base

on: [push, pull_request]
</code></pre>
<p>这是另一个非常常用的触发器配置。它只在你向主分支（main 或者 master）发出拉取请求时（pull_request）运行，而不是在向这些分支（master 或者 main 分支）推送时（push）运行。</p>
<pre><code class="language-yaml">on:
  push:
    branches-ignore: [master, main]
  pull_request:
    branches: [master, main]
</code></pre>
<p>接下来，我们要设置一个 job（作业）。你放在一个作业中的所有组件将按顺序运行。在这里，我们把它看作是步骤，以及每当触发器得到满足时，我们希望它们按照哪个顺序运行。</p>
<p>我们将把这个 job 命名为 <code>Lint Code Base</code>，并要求 GitHub 用最新版本的 Ubuntu 运行我们的 job。</p>
<pre><code class="language-yaml">name: Lint Code Base

on: [push, pull_request]

jobs:
  build:
    name: Lint Code Base
    runs-on: ubuntu-latest
</code></pre>
<p>你不一定要像我们这里一样使用单一的 runner（ubuntu-latest）。可以选择多个 runner，但在这种情况下，它将以同样的方式在所有种类的 runner 上运行。你使用多个 runner 来测试你的代码，是否可以在多个平台上都能很好地运行。</p>
<p>GitHub Super Linter 在其他机器类型上的工作方式没有任何不同，所以我们只用一个机器类型。</p>
<p>接下来，我们将开始定义我们希望这个工作流程所具有的步骤。我们基本上有两个步骤：</p>
<ol>
<li>获取对应代码</li>
<li>运行 super linter</li>
</ol>
<p>获取代码。我们会使用 GitHub 官方的 checkout action。</p>
<p>我们设置 <code>fetch-depth: 0</code> 来获取所有分支和标签的所有历史记录，这对 Super linter 来说是必要的，可以获得修改过的文件列表。如果你没有这样做，就只能获取单个提交。</p>
<p>我们还要给我们的步骤起个名字，并告诉它我们要使用 GitHub 官方仓库中的 action，即 <code>actions/checkout@v3</code>。</p>
<pre><code class="language-yaml">name: Lint Code Base

on: [push, pull_request]

jobs:
  build:
    name: Lint Code Base
    runs-on: ubuntu-latest

    steps:

      - name: Checkout Code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
</code></pre>
<p>这段代码在 <code>$GITHUB_WORKSPACE</code> 下检出你的仓库，允许工作流（workflow）的其他部分访问这个仓库。我们要检查的版本库是你的代码所在的版本库，最好是同一个版本库。</p>
<h3 id="linter">如何运行 Linter</h3>
<p>现在我们要添加运行 linter 的步骤，因为我们的代码已经获取。你可以在运行 Action 时使用环境变量（environment variables）来定制 GitHub Super Linter。</p>
<pre><code class="language-yaml">name: Lint Code Base

on: [push, pull_request]

jobs:
  build:
    name: Lint Code Base
    runs-on: ubuntu-latest

    steps:

      - name: Checkout Code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          
   - name: Lint Code Base
        uses: github/super-linter@v4
</code></pre>
<p>现在我们将谈谈在经常使用 GitHub 环境变量的例子。</p>
<ul>
<li><code>VALIDATE_ALL_CODEBASE</code>：这控制 Super Linter 对整个代码库进行检查，还是只对该提交的修改进行检查。这些变化是通过<code>git diff</code>发现的，但你也可以改变搜索算法（但我们不会在这篇文章中研究这个问题）。常用配置: <code>VALIDATE_ALL_CODEBASE: true</code>。</li>
<li><code>GITHUB_TOKEN</code>：顾名思义，这就是 GitHub 令牌。如果你使用它，GitHub 将显示你使用的每个 linter（我们将很快看到如何做到这一点）作为 UI 上的单独检查。例如，你可以这样写  <code>GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}</code>.</li>
<li><code>DEFAULT_BRANCH</code>：存储库默认分支的名称。例子: <code>DEFAULT_BRANCH: main</code>。</li>
<li><code>IGNORE_GENERATED_FILES</code>：如果你有任何由工具生成的文件，你可以将它们标记为 <code>@generated</code>。如果此环境变量设置为 true，Super Linter 将忽略这些文件。例如， <code>IGNORE_GENERATED_FILES: true</code>。</li>
<li><code>IGNORE_GITIGNORED_FILES</code>：从 linting 中排除 .gitignore 中的文件。示例： <code>IGNORE_GITIGNORED_FILES: true</code>。</li>
<li><code>LINTER_RULES_PATH</code>：任何 linter 自定义文件应该位于的自定义路径。默认情况下，你的文件应位于 <code>.github/linters/</code>。示例：<code>LINTER_RULES_PATH: /</code>。</li>
</ul>
<p>这些是你最常使用的一些环境变量，但我们讨论过的还没有一个讨论特定于语言的 linting。</p>
<p>如果你不使用我们讨论的任何环境变量，Super Linter 会自动为你的代码库查找并使用所有适用的 linter。</p>
<h2 id="linterssuperlinter">如何增加特定的 Linters 到 Super Linter</h2>
<p>你通常只会对为你的项目使用特定的 linter 感兴趣。你可以使用以下环境变量模式来添加你想要的任何 linter：</p>
<pre><code class="language-shell">VALIDATE_{LANGUAGE}_{LINTER}
</code></pre>
<p>你可以在列表中找到这些 <a href="https://github.com/github/super-linter#supported-linters">Supported Linters</a> 的命名约定。</p>
<p>这里有几个例子，我们指定要使用 Black 对所有 Python 文件进行 lint，对 JavaScript 文件使用 ESLint，对 HTML 文件使用 HTMLHint。</p>
<pre><code class="language-yaml">name: Lint Code Base

on: [push, pull_request]

jobs:
  build:
    name: Lint Code Base
    runs-on: ubuntu-latest

    steps:

      - name: Checkout Code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          
   - name: Lint Code Base
        uses: github/super-linter@v4

      - name: Lint Code Base
        uses: github/super-linter@v4
        env:
          VALIDATE_ALL_CODEBASE: true
          VALIDATE_JAVASCRIPT_ES: true
          VALIDATE_PYTHON_BLACK: true
          VALIDATE_HTML: true
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
</code></pre>
<p>一旦将其中一个 linter 设置为 <code>true</code>，所有其他 linter 将不会运行。在上面的代码片段中，除了 ESLint、Black 或 HTMLHint 之外的所有 linter 都不会运行。</p>
<p>然而，在这个例子中，我们将单个 linter 设置为 <code>false</code>，所以除了 <code>ESLint</code> 之外的每个 linter 都将在这里运行：</p>
<pre><code class="language-yaml">name: Lint Code Base

on: [push, pull_request]

jobs:
  build:
    name: Lint Code Base
    runs-on: ubuntu-latest

    steps:

      - name: Checkout Code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          
   - name: Lint Code Base
        uses: github/super-linter@v4

      - name: Lint Code Base
        uses: github/super-linter@v4
        env:
          VALIDATE_ALL_CODEBASE: true
          VALIDATE_JAVASCRIPT_ES: false
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
</code></pre>
<h2 id="lintchecks">如何定制化 Lint Checks</h2>
<p>Linter 通常使用配置文件，因此你可以修改 linter 使用的规则。在上面我展示的两个完整示例中，Super Linter 将尝试在 <code>.github/linters/</code> 下查找任何配置文件。</p>
<p>这些可能是用于配置 ESLint 的 <code>.eslintrc.yml</code> 文件，用于配置 HTMLHint 的 <code>.htmlhintrc</code> 等等。</p>
<p>如果你使用 Python 的 Flake8 linter，以下是配置文件的示例：</p>
<pre><code class="language-yaml">[flake8]
max-line-length = 120
</code></pre>
<p>你把它保存在<code>.github/linters/.flake8</code>。然后，你将在运行 Flake8 linter 时使用它。你可以在<a href="https://github.com/github/super-linter/tree/main/TEMPLATES">此处</a>找到可以使用的模板配置文件示例。</p>
<p>但是，这里有两个示例说明如何修改此路径：</p>
<ol>
<li>你所有的 linter 配置文件都在其他目录中</li>
</ol>
<p>将目录路径添加为环境变量，如下所示：</p>
<pre><code class="language-yaml">LINTER_RULES_PATH: configs/
</code></pre>
<ol start="2">
<li>添加配置文件的路径</li>
</ol>
<p>你还可以将特定 linter 的路径硬编码为环境变量。这是一个例子：</p>
<pre><code class="language-yaml">JAVASCRIPT_ES_CONFIG_FILE: configs/linters/.eslintrc.yml
</code></pre>
<h2 id="githubactionssuperlinter">如何在 GitHub Actions 之外运行 Super Linter</h2>
<p>GitHub Super Linter 是为在 GitHub Actions 中运行而构建的。但是在本地或其他 CI 平台上运行它可能特别有用。你可以本地任何其他 CI 平台上一样运行 Super Linter。</p>
<h3 id="superlinter">如何在本地运行 Super Linter</h3>
<p>你首先要使用此命令从 DockerHub 中获取最新的 Docker 容器：</p>
<pre><code class="language-shell">docker pull github/super-linter:latest
</code></pre>
<p>为了运行这个容器，你可以运行以下命令：</p>
<pre><code class="language-shell">docker run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true VALIDATE_PYTHON_BLACK=true -v /project/directory:/tmp/lint github/super-linter
</code></pre>
<p>请注意这里的几件事：</p>
<ul>
<li>我们用<code>RUN_LOCAL</code>标志运行它，以绕过一些 GitHub  action 检查。这将自动设置 <code>VALIDATE_ALL_CODEBASE</code> 为 true。</li>
<li>我们将本地代码库映射到<code>/tmp/lint</code>，这样 linter 就能接收到代码。</li>
<li>当然，我们设置环境变量的方式是不同的，但运行 GitHub Super Linter 的整体过程是相同的。</li>
</ul>
<h3 id="cisuperlinter">如何在其他 CI 平台上运行 Super Linter</h3>
<p>在其他 CI 平台上运行 GitHub Super Linter 与在本地运行 GitHub Super Linter 非常相似。下面是 <a href="https://blog.tyang.org/2020/06/27/use-github-super-linter-in-azure-pipelines/">Tao Yang</a> 在 Azure Pipelines 中运行它的一个例子。</p>
<pre><code class="language-yaml">- job: lint_tests
  displayName: Lint Tests
  pool:
    vmImage: ubuntu-latest
  steps:
  - script: |
      docker pull github/super-linter:latest
      docker run -e RUN_LOCAL=true -v $(System.DefaultWorkingDirectory):/tmp/lint github/super-linter
    displayName: 'Code Scan using GitHub Super-Linter'
</code></pre>
<p>这只是把我们在本地运行 Super Linter 的命令作为一个脚本来运行。你可以在其他 CI 平台上以完全相同的方式运行它。</p>
<h2 id="">总结</h2>
<p>谢谢你坚持到最后。我希望你能从 GitHub Super Linter 的使用中得到一两点启发。它无疑是我最喜欢的开源项目之一。</p>
<p>如果你学到了新的东西，或者喜欢读这篇文章，请分享出去，让别人看到。我们在下一篇文章中再见吧！</p>
<p>你也可以在 Twitter 上找到我，<a href="https://twitter.com/rishit_dagli">@rishit/_dagli</a>，我在那里发布关于开源和机器学习的推文。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 Git 和 GitHub——面向初学者的版本控制教程 ]]>
                </title>
                <description>
                    <![CDATA[ 版本控制系统是一种用于跟踪、执行和管理软件代码更改的工具。它也称为源代码控制。 版本控制系统可帮助开发人员存储他们在不同阶段对文件所做的每项更改，以便他们和团队成员可以在以后检索这些更改。 有三种类型的版本控制系统，它们是：  * 本地版本控制系统  * 集中式版本控制系统  * 分布式版本控制系统 什么是本地版本控制系统（LVCS）？ 这是一种非常常见且易于使用的版本控制系统。但是这种方法很容易出错和受到攻击，因为文件存储在你的本地系统中。 这意味着你可能会丢失系统文件或意外忘记你正在操作的文件的目录/文件夹（然后写入另一个目录）。 什么是集中式版本控制系统（CVCS）？ 在这种类型的版本控制中，服务器充当存储每个版本代码的仓库。 CVCS 帮助不同的开发人员一起协作。 尽管开发人员之间进行了有益的协作和沟通，但如果服务器出现几秒钟的故障或损坏，你就有可能丢失资料。不幸的是，这是 CVCS 的一个非常大的问题。 在 CVCS 中，只有少数开发人员可以在一个项目上一起工作。 什么是分布式版本控制系统（DVCS）？ 这是现在最新和最常用的版本控制系统类型。 在 DVC ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/git-and-github-the-basics/</link>
                <guid isPermaLink="false">630098f060480505ded7a84f</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Miya Liu ]]>
                </dc:creator>
                <pubDate>Mon, 15 Aug 2022 08:30:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/08/git-github.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/git-and-github-the-basics/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use Git and GitHub – Version Control Basics for Beginners</a>
      </p><p>版本控制系统是一种用于跟踪、执行和管理软件代码更改的工具。它也称为源代码控制。</p><p>版本控制系统可帮助开发人员存储他们在不同阶段对文件所做的每项更改，以便他们和团队成员可以在以后检索这些更改。</p><p>有三种类型的版本控制系统，它们是：</p><ul><li>本地版本控制系统</li><li>集中式版本控制系统</li><li>分布式版本控制系统</li></ul><h2 id="-lvcs-">什么是本地版本控制系统（LVCS）？</h2><p>这是一种非常常见且易于使用的版本控制系统。但是这种方法很容易出错和受到攻击，因为文件存储在你的本地系统中。</p><p>这意味着你可能会丢失系统文件或意外忘记你正在操作的文件的目录/文件夹（然后写入另一个目录）。</p><h2 id="-cvcs-">什么是集中式版本控制系统（CVCS）？</h2><p>在这种类型的版本控制中，服务器充当存储每个版本代码的仓库。 CVCS 帮助不同的开发人员一起协作。</p><p>尽管开发人员之间进行了有益的协作和沟通，但如果服务器出现几秒钟的故障或损坏，你就有可能丢失资料。不幸的是，这是 CVCS 的一个非常大的问题。</p><p>在 CVCS 中，只有少数开发人员可以在一个项目上一起工作。</p><h2 id="-dvcs-">什么是分布式版本控制系统（DVCS）？</h2><p>这是现在最新和最常用的版本控制系统类型。</p><p>在 DVCS 中，所有开发人员都拥有服务器中所有数据的完整备份（克隆）。这意味着无论何时服务器关闭或出现故障，你仍然可以处理你的项目，你可以将仓库复制或备份到服务器以恢复它们。</p><p>当你使用 DVCS 时，许多开发人员可以在一个项目上一起工作。一种流行的 DVCS 是 Git，我们现在将对其进行更多讨论。</p><h2 id="-git-">什么是 Git？</h2><p>Git 是一个免费的开源分布式版本控制系统，你可以使用它来跟踪文件中的更改。你可以在 Git 中处理所有类型的项目。</p><p>使用 Git，你可以将更改添加到代码中，然后在准备好时提交（或保存）它们。这意味着你还可以返回之前所做的更改。</p><p>开发者常将 Git 与 GitHub 一起使用——那么 GitHub 是什么？</p><h2 id="-github-"><strong>什么是 GitHub？</strong></h2><p>GitHub 是一个 Web 界面，你可以在其中存储 Git 仓库并有效地跟踪和管理你的更改。它使操作同一项目的各种开发人员可以访问代码。你可以在其他开发人员进行更改的同时对项目进行自己的更改。</p><p>如果你在进行更改时不小心弄乱了项目中的某些代码，你可以轻松地回到尚未发生混乱的上一个阶段。</p><h2 id="-github--1"><strong>为什么使用 GitHub？</strong></h2><p>你应该学习和使用 GitHub 的原因有很多。现在让我们来看看其中的几个。</p><h3 id="-">高效的项目管理</h3><p>GitHub 是存储 Git 仓库的地方。GitHub 使从事同一项目但在不同位置的开发人员可以轻松地在同一页面上工作。</p><p>使用 GitHub，你可以轻松跟踪和管理你所做的更改，并检查你在项目中所做的进度。</p><h3 id="--1">轻松协作与合作</h3><p>借助 GitHub，来自世界各地的开发人员可以在一个项目上协同工作，而不会遇到任何问题。</p><p>团队在共同处理项目时能够保持一致，并且可以轻松有效地组织和管理项目。</p><h3 id="--2">开源</h3><p>GitHub 是一个免费的开源系统。这意味着开发人员可以轻松访问不同类型的代码/项目，他们可以使用这些代码/项目来学习和发展自己的技能。</p><h3 id="--3">多功能性</h3><p>GitHub 的这个属性非常重要。 GitHub 不仅仅是开发人员的 Web 界面，设计师、写作者和任何想要跟踪其项目历史的人都可以使用它。</p><h2 id="-git--1">如何设置 Git？</h2><p>要开始使用 Git，你需要先将它下载到你的计算机（如果你还没有的话）。你可以通过访问他们的<a href="https://git-scm.com/">官方网站</a>来执行此操作。</p><p>当 Git 打开时，向下滚动一点，你应该会看到一个下载按钮，继续并单击它。</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/07/bandicam-2022-07-05-20-23-44-196-1.jpg" class="kg-image" alt="bandicam-2022-07-05-20-23-44-196-1" width="600" height="400" loading="lazy"><figcaption>Git 网站上的下载按钮</figcaption></figure><p>选择你的操作系统，无论是 Windows、MacOS、Linux/Unix。就我而言，我将选择 Windows 选项，因为我使用的是 Windows 计算机：</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/07/bandicam-2022-07-05-20-24-33-919.jpg" class="kg-image" alt="bandicam-2022-07-05-20-24-33-919" width="600" height="400" loading="lazy"><figcaption>选择你的操作系统</figcaption></figure><p>单击页面顶部的第一个链接以下载最新版本的 Git。</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/07/bandicam-2022-07-05-20-25-06-107.jpg" class="kg-image" alt="bandicam-2022-07-05-20-25-06-107" width="600" height="400" loading="lazy"><figcaption>点击第一个链接下载最新版本的 Git</figcaption></figure><p>下载完成后，继续将 Git 安装到你的计算机上。你需要转到下载文件的位置并安装它。<br>安装后，你需要确保 Git 已成功安装在你的系统上。打开命令提示符或 Git bash（无论你选择使用哪个）并运行命令：</p><p><code>git --version</code></p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/bandicam-2022-07-05-20-48-13-907.jpg" class="kg-image" alt="bandicam-2022-07-05-20-48-13-907" width="600" height="400" loading="lazy"></figure><p>如果 Git 已成功安装在你的计算机上，它应该会在你刚刚运行的命令下方显示当前版本的 Git。如果显示了当前版本，恭喜你！</p><h2 id="-git--2"><strong>如何配置 Git？</strong></h2><p>现在我们已经在计算机上安装了 Git，我们必须对其进行配置。这样做是为了在任何时候我们在一个项目的团队中工作时，都可以轻松地识别我们在仓库中所做的提交。</p><p>要配置 Git，我们需要使用 <code>git config --global</code> 命令指定名称、电子邮件地址和分支，例如：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/MINGW64__c_Users_me-7_7_2022-2_38_36-AM.png" class="kg-image" alt="MINGW64__c_Users_me-7_7_2022-2_38_36-AM" width="600" height="400" loading="lazy"></figure><p>从上图中，我们使用 <code>git config --global user.name</code> 来配置用户名。就我而言，我使用了我的名字 <code>“Derek Emmanuel”</code>。这同样适用于 <code>git config --global user.email</code>。</p><p>Git 有一个默认的 <code>master</code> 分支，所以我使用 <code>git config --global init.default branch main</code> 命令将其更改为主分支。</p><p>现在你已准备好开始使用 Git。</p><h2 id="-github--2">如何设置 GitHub 账户？</h2><p>要设置 GitHub 账户，请访问他们的<a href="https://github.com/">官方网站</a>，点击右上角的注册按钮：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/bandicam-2022-07-05-20-27-35-732.jpg" class="kg-image" alt="bandicam-2022-07-05-20-27-35-732" width="600" height="400" loading="lazy"></figure><p>当注册表单打开时，输入你的电子邮件，创建密码，输入你的用户名，然后在单击创建账户按钮之前验证你的账户。</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/07/bandicam-2022-07-05-20-35-26-646.jpg" class="kg-image" alt="bandicam-2022-07-05-20-35-26-646" width="600" height="400" loading="lazy"><figcaption>创建你的 GitHub 账户</figcaption></figure><h2 id="-git--3">常用的 Git 命令</h2><p>每个开发人员都应该知道如何使用一些基本的 Git 命令：</p><ul><li><code>git config</code></li><li><code>git init</code></li><li><code>git add</code></li><li><code>git commit</code></li><li><code>git clone</code></li><li><code>git push</code></li><li><code>git rm</code></li><li><code>git branch</code></li></ul><p>让我们简要介绍一下这些，以便你知道如何使用它们。</p><h3 id="-git-config-">如何使用 git config 命令</h3><p>你可以使用此命令设置用户的用户名、电子邮件和分支，以便在处理项目时识别谁进行了提交。当你将 Git 下载到你的计算机中并且想要自定义它以供你使用时，将使用此命令。</p><p>例如：</p><p><code>git config --global user.name “ [username]”</code></p><p><code>git config --global user.email [email address]</code></p><h3 id="-git-init-">如何使用 git init 命令</h3><p>你可以使用 <code>git init</code> 命令在项目中启动 Git。当你正在处理项目并希望将 Git 初始化到项目以跟踪项目中所做的更改时，将使用此 Git 命令。</p><p>例如：</p><p><code>git init</code></p><p>当你运行此命令时，你应该会看到在你正在处理的当前文件夹中自动创建了一个名为 <code>.git</code> 的文件夹。</p><h3 id="-git-add-">如何使用 git add 命令</h3><p>此命令将你的文件添加到暂存区。暂存区域是添加我们对其进行更改的文件以及它们等待下一次提交的区域。</p><p>要将文件添加到暂存区域，请使用 <code>git add</code> 命令。它将文件夹中的所有文件添加到暂存区域。</p><p><code>git add (file name)</code> 添加你想在暂存区提交的特定文件的名称。</p><p>当你对文件进行更改并希望将它们提交到你的项目时，请使用此命令。</p><h3 id="-git-commit-">如何使用 git commit 命令</h3><p>这将提交你使用 <code>git add</code> 命令添加的任何文件以及暂存区域中的每个文件。</p><p>例如：</p><p><code>git commit –m “first commit”</code></p><p>此命令将文件永久保存到 Git 仓库。只要使用 <code>git add</code> 命令将文件添加到暂存区，就可以使用它。</p><h3 id="-git-clone-">如何使用 git clone 命令</h3><p>你使用 <code>git clone</code> 命令将另一个位置的已有的仓库复制到你想要的当前位置。</p><p>例如：</p><p><code>git clone (repository name)</code></p><p>当你想要将 Git 仓库从 GitHub 复制到本地仓库时，你可以使用此命令。</p><h3 id="-git-push-">如何使用 git push 命令</h3><p>你可以使用此命令将文件从本地仓库上传/推送到另一个仓库，例如 GitHub 等远程仓库。</p><p>例如：</p><p><code>git push (remote storage name)</code></p><p>仅当你对项目所做的更改和提交感到满意并最终希望将其上传/推送到 GitHub 中的 Git 仓库时，才使用此命令。</p><h3 id="-git-rm-">如何使用 git rm 命令</h3><p>你可以使用此 Git 命令从工作仓库中删除文件，例如：</p><p><code>git rm (filename)</code></p><p>仅当你希望从 Git 仓库中删除不需要的更改/文件时，才使用此命令。</p><h3 id="-git-branch-">如何使用 git branch 命令</h3><p>你使用此命令来检查你正在处理的当前分支，例如：</p><p><code>git branch</code></p><p>此命令可帮助你了解你正在处理的当前分支。</p><h2 id="--4"><strong>总结</strong></h2><p>在本教程中，你了解了版本控制系统的全部内容。你还学习了如何在计算机上安装和设置 Git 以及设置 GitHub 账户。最后，我们浏览了一些常用的 Git 命令。</p><p>如果你想更深入地了解 Git 和 GitHub，可以<a href="https://www.freecodecamp.org/news/git-and-github-crash-course/">在 freeCodeCamp YouTube 频道上查看本课程</a>。</p><p>希望本教程对你有所帮助。</p><p>祝你编程愉快！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何在 VS Code 中使用 GitHub Copilot ]]>
                </title>
                <description>
                    <![CDATA[ 原文：How to Use GitHub Copilot with Visual Studio Code [https://www.freecodecamp.org/news/how-to-use-github-copilot-with-visual-studio-code/] ，作者：Nishant Kumar [https://www.freecodecamp.org/news/author/nishant-kumar/] 大家好！在本文中，我们将学习如何将 GitHub Copilot AI 工具与 Visual Studio Code 结合使用。 GitHub Copilot 是什么 GitHub Copilot 是一个可以帮助你更简单、更快速地编写代码的工具，由 GPT-3 提供支持。你只需编写所需代码的描述——例如，编写一个函数来生成一个随机数，或对一个数组进行排序——Copilot 就会为你创建它。 它不只是创建一种解决方案，而是创建多个，你可以选择你想要的一个。 在本教程中，我们将学习如何为 Visual Studio Code 设置 GitHub Copilo ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-use-github-copilot-with-visual-studio-code/</link>
                <guid isPermaLink="false">62d77de18d13aa0845c62b27</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 20 Jul 2022 04:25:13 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/07/pexels-xxss-is-back-777001.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/how-to-use-github-copilot-with-visual-studio-code/">How to Use GitHub Copilot with Visual Studio Code</a>，作者：<a href="https://www.freecodecamp.org/news/author/nishant-kumar/">Nishant Kumar</a></p><p>大家好！在本文中，我们将学习如何将 GitHub Copilot AI 工具与 Visual Studio Code 结合使用。</p><h2 id="github-copilot-">GitHub Copilot 是什么</h2><p>GitHub Copilot 是一个可以帮助你更简单、更快速地编写代码的工具，由 GPT-3 提供支持。你只需编写所需代码的描述——例如，编写一个函数来生成一个随机数，或对一个数组进行排序——Copilot 就会为你创建它。</p><p>它不只是创建一种解决方案，而是创建多个，你可以选择你想要的一个。</p><p>在本教程中，我们将学习如何为 Visual Studio Code 设置 GitHub Copilot AI 工具，以及如何生成 JavaScript、React 和 HTML 代码。</p><h2 id="-github-copilot"><strong>如何安装 GitHub Copilot</strong></h2><p>要添加 GitHub Copilot，请打开你的 <a href="https://chinese.freecodecamp.org/news/how-to-use-github-copilot-with-visual-studio-code/GitHub">GitHub</a> 并转到设置。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot-2022-07-02-181658.png" class="kg-image" alt="Screenshot-2022-07-02-181658" width="600" height="400" loading="lazy"></figure><p>在左侧菜单中选择 GitHub Copilot 并勾选 Allow 以启用它，然后单击 Save 按钮以保存。</p><p>现在打开 Visual Studio Code 并转到 Extensions（扩展），在搜索栏中搜索 GitHub Copilot。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot-2022-07-02-181954.png" class="kg-image" alt="Screenshot-2022-07-02-181954" width="600" height="400" loading="lazy"></figure><p>安装 Github Copilot，并重新启动你的 Visual Studio Code。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot-2022-07-02-182152.png" class="kg-image" alt="Screenshot-2022-07-02-182152" width="600" height="400" loading="lazy"></figure><p>在底部，你会看到 GitHub Copilot 已被激活。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Untitled-design.png" class="kg-image" alt="Untitled-design" width="600" height="400" loading="lazy"></figure><p>但请记住，我们目前只有试用版。而且它只有两个月的有效期——免费试用版将于 8 月 22 日结束。试用结束后，我们必须购买完整版。</p><p>每月花费 10 美元，或每年 100 美元。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot-2022-07-02-182753.png" class="kg-image" alt="Screenshot-2022-07-02-182753" width="600" height="400" loading="lazy"></figure><p>现在我们已经安装了 Copilot，让我们开始使用它的更有趣的部分。</p><h2 id="-github-copilot-javascript-">如何使用 GitHub Copilot 生成 JavaScript 代码</h2><p>让我们从简单的事情开始。让我们创建一个函数来添加两个数字。</p><p>在 JavaScript 文件中，只需写一个注释，例如 “Generate a function to add two numbers（生成一个函数以将两个数字相加）”。</p><pre><code>//Generate a function to add two numbers</code></pre><p>然后按 Enter 键，它会向你抛出建议，你可以通过按 Tab 键接受这些建议。</p><pre><code>//Generate a function to add two numbers
function add(a, b) {</code></pre><p>然后按 Enter 键进入下一行，当出现下一行代码时，再次按 Tab。</p><pre><code>//Generate a function to add two numbers
function add(a, b) {
  return a + b;
}
</code></pre><p>这是添加两个数字的函数。</p><p>现在让我们调用函数 <code>add()</code>。编写函数调用，它会自动接受一些随机参数。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot--267--1.png" class="kg-image" alt="Screenshot--267--1" width="600" height="400" loading="lazy"></figure><p>我们还可以对数字进行减法、乘法和除法。</p><h2 id="-github-copilot-">如何使用 GitHub Copilot 生成函数以在数组中显示彩虹的颜色</h2><p>我们将从注释 “Generate an array of all the colors from the rainbow（从彩虹中生成所有颜色的数组）”开始。</p><pre><code>//Generate an array of all the colors from the rainbow</code></pre><p>然后就像之前一样，我们将按 Enter 键。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot--268-.png" class="kg-image" alt="Screenshot--268-" width="600" height="400" loading="lazy"></figure><p>它将生成彩虹中所有颜色的数组。</p><pre><code>//Generate an array of all the colors from the rainbow
var colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];</code></pre><h2 id="-number-string-boolean-">如何使用类型 Number、String 和 Boolean 创建三个数组并将它们合并到一个对象中</h2><p>现在让我们尝试创建一个包含数字、字符串和布尔值的数组。</p><pre><code>//Create an array of numbers
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

//Create an array of strings
var strings = ["hello", "world", "how", "are", "you"];

//Create an array of booleans
var booleans = [true, false, true, false, true];</code></pre><p>现在，让我们将它们合并到一个对象中。我们将创建一个像这样的对象：</p><pre><code>var objects = [
    {
        number: 1,
        string: "hello",
        boolean: true
    },
    {
        number: 2,
        string: "world",
        boolean: false
    },
    {
        number: 3,
        string: "how",
        boolean: true
    },
]</code></pre><p>写一条注释 “Create an array of objects with the above array items as key value pairs（创建一个对象数组，将上述数组项作为键值对）”。</p><p>你可以按 Tab 键接受解决方案，或按 CTRL + Enter 键打开 Copilot 解决方案页面。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot-2022-07-02-185657.png" class="kg-image" alt="Screenshot-2022-07-02-185657" width="600" height="400" loading="lazy"></figure><p>你可以接受任何你想要的解决方案。只需单击 <strong><strong>Accept</strong></strong> 以接受。</p><pre><code>//Create an array of numbers
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

//Create an array of strings
var strings = ["hello", "world", "how", "are", "you"];

//Create an array of booleans
var booleans = [true, false, true, false, true];

//Create an array of objects with the above array items as key value pairs
var objects = [
  {
    number: 1,
    string: "hello",
    boolean: true,
  },
  {
    number: 2,
    string: "world",
    boolean: false,
  },
  {
    number: 3,
    string: "how",
    boolean: true,
  },
  {
    number: 4,
    string: "are",
    boolean: false,
  },
  {
    number: 5,
    string: "you",
    boolean: true,
  },
];
</code></pre><h2 id="-react-express-">如何在 React 和 Express 中导入东西</h2><p>现在让我们尝试看看 React 和 Express 是如何工作的。</p><p>我们将简单地导入一些模块。</p><p>让我们首先从 React 中导入 useState Hook。</p><pre><code>//Import useState Hook from react</code></pre><p>写下注释，然后按 Enter 键，Copilot 将生成代码。</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot--270-.png" class="kg-image" alt="Screenshot--270-" width="600" height="400" loading="lazy"></figure><pre><code>//Import useState Hook from react
import React, { useState } from 'react';</code></pre><p>让我们再试一试从 React 中导入 useEffect 和 useState Hooks。</p><pre><code>//Import useState and useEffect hook from react
import React, { useState, useEffect } from 'react';</code></pre><p>让我们在 Express 中做点什么。在 Express 中导入 CORS npm 包，它是为 Node 和 Express 制作的。它会在这里。</p><pre><code>//Import cors from express
const cors = require('cors');</code></pre><h2 id="-html-">如何生成 HTML 代码</h2><p>让我们尝试一些 HTML 代码。</p><p>首先，让我们生成一些代码来创建一个无序列表，其中包含 Nishant、25 和 Patna。</p><pre><code>Create an ul tag with list items Nishant, 25, and Patna
    &lt;ul&gt;
      &lt;li&gt;Nishant&lt;/li&gt;
      &lt;li&gt;25&lt;/li&gt;
      &lt;li&gt;Patna&lt;/li&gt;
    &lt;/ul&gt;</code></pre><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot-2022-07-02-191108.png" class="kg-image" alt="Screenshot-2022-07-02-191108" width="600" height="400" loading="lazy"></figure><p>让我们尝试相同的方法，但列表样式为 none。</p><pre><code>Create an ul tag with the list having a class of lists and the items
    Nishant, 25, and Patna and the list style as none
    &lt;ul class="lists" style="list-style: none"&gt;
      &lt;li&gt;Nishant&lt;/li&gt;
      &lt;li&gt;25&lt;/li&gt;
      &lt;li&gt;Patna&lt;/li&gt;
    &lt;/ul&gt;</code></pre><p>就是这样，很神奇吧？</p><h2 id="-">总结</h2><p>在本文中，你了解了 GitHub Copilot 是什么以及如何使用它。</p><p>你还可以查看我关于同一主题的视频，即 <a href="https://chinese.freecodecamp.org/news/how-to-use-github-copilot-with-visual-studio-code/Let's%20Test%20the%20GitHub%20Copilot%20-%20GitHub%20Copilot%20Tutorial%20with%20Visual%20Studio%20Code">Let's Test the GitHub Copilot - GitHub Copilot Tutorial with Visual Studio Code</a>。</p><p>感谢你阅读本文。祝你学习愉快！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何复刻一个 GitHub 仓库——完整工作流程 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：How to Fork a GitHub Repository – A Complete Workflow [https://www.freecodecamp.org/news/how-to-fork-a-github-repository/]，作者：TAPAS ADHIKARY [https://www.freecodecamp.org/news/author/tapas/] GitHub 是一个很棒的应用程序，可以帮助你管理你的 Git 仓库。你也可以用它来为开源生态系统做贡献，与其他贡献者合作。 GitHub 上的公共仓库经常会得到很多贡献者的关注，这有助于提高项目的水平。 那么如何才能轻松地在公共仓库中工作呢？仓库 forking 工具可以让贡献者在本地复制源代码仓库，并做任何他们想做的修改。 但如果你作为一个初学者要使用这个工具，你需要了解仓库复刻的工作流程，这样你就可以无缝地与其他公共仓库合作。 在这篇文章中，我们将通过实际的例子和故事来学习如何复刻一个仓库。如果你喜欢通过视频学习，也可以看这个 YouTube 视频： 什么是 GitHub 仓库复刻？ 假 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-fork-a-github-repository/</link>
                <guid isPermaLink="false">621defe779a578061070cef7</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Miya Liu ]]>
                </dc:creator>
                <pubDate>Sat, 26 Mar 2022 09:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/03/freeCodeCamp-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/how-to-fork-a-github-repository/">How to Fork a GitHub Repository – A Complete Workflow</a>，作者：<a href="https://www.freecodecamp.org/news/author/tapas/">TAPAS ADHIKARY</a></p><p>GitHub 是一个很棒的应用程序，可以帮助你管理你的 Git 仓库。你也可以用它来为开源生态系统做贡献，与其他贡献者合作。</p><p>GitHub 上的公共仓库经常会得到很多贡献者的关注，这有助于提高项目的水平。</p><p>那么如何才能轻松地在公共仓库中工作呢？仓库 <code>forking</code> 工具可以让贡献者在本地复制源代码仓库，并做任何他们想做的修改。</p><p>但如果你作为一个初学者要使用这个工具，你需要了解仓库复刻的工作流程，这样你就可以无缝地与其他公共仓库合作。</p><p>在这篇文章中，我们将通过实际的例子和故事来学习如何复刻一个仓库。如果你喜欢通过视频学习，也可以看这个 YouTube 视频：</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/h8suY-Osn8Q?start=2&amp;feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="-github-">什么是 GitHub 仓库复刻？</h2><p>假设你喜欢在一个特定的框架或库上工作，比如 React.js。有一天，你想出了一个办法，可以自己增强 React 的功能。</p><p>React 的源代码在 GitHub 上以公共仓库的形式提供，所以你可以通过复刻来制作它的本地拷贝。</p><p>一旦你得到了代码的本地拷贝，你可以进行相关的修改，并要求 React 社区审查你的修改。</p><p>在审查了你的代码修改后，React 社区可能会批准它们，或者要求你做更多的修改。他们很有可能在批准（approval）后接受你的代码修改。</p><p>所以，让我们通过两个开发者 Tom 和 Hari 的故事来了解整个复刻工作流程。</p><h2 id="-"><strong>完整的复刻工作流</strong></h2><p>Tom 和 Hari 是两个拥有各自 GitHub 账户的开发者。Tom 正在做一个优秀的项目，他在一个名为 <code>/tom/repo</code> 的公共仓库中管理其源代码。Hari 对这个想法感到惊奇，想为这个项目做出贡献。</p><p>现在，Hari 有两种方法。</p><ul><li>Hari 问 Tom：“嘿，Tom，我想为你的项目做贡献。你能不能把我加为贡献者？” Tom 可能同意也可能不同意。由于该项目已经作为仓库提供，他可能只是要求复刻并使用它。</li><li>第二种方式是 Hari 自己 fork 这个仓库并开始工作。他不需要等着和 Tom 谈，让 Tom 把他加为贡献者。</li></ul><p>这第二种直接复刻仓库的方法对任何贡献者来说都更方便。那么它是如何工作的呢？让我们分步骤来了解一下工作流程。</p><ol><li>Hari 复刻仓库 <code>/tom/repo</code>。我们称 <code>/tom/repo</code> 仓库为上游仓库。</li><li>仓库现在可以在 Hari 的 GitHub 账户中以 <code>/hari/repo</code> 的形式使用。我们称 <code>hari/repo</code> 为复刻仓库（<code>Forked Repository</code>）。同时，它也是上游仓库的一个完全拷贝。这个仓库完全脱离了 Tom 的仓库，Hari 可以开始做任何他想做的修改。Hari 首先会克隆仓库，并开始进行修改。</li><li>Hari 做好所需的修改，并将修改推送（push）到上游仓库中。</li><li>最后，Hari 创建一个从自己的仓库到上游仓库的拉取请求（<code>Pull Request</code>）。Tom 在他方便的时候审查该拉取请求，如果一切顺利，就批准（approve）并合并（merge）它。</li></ol><p>这就是整个工作流程——通过简单而直接的方式来为公共仓库做贡献。一图胜千言，下面是我们讨论的工作流程的演示。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/02/GitHub-Fork.gif" class="kg-image" alt="GitHub-Fork" width="600" height="400" loading="lazy"><figcaption>工作流程 - 复刻一个仓库</figcaption></figure><h2 id="-github--1">如何在 GitHub 中复刻一个仓库</h2><p>复刻一个仓库只需点击一个按钮。</p><p>要跟着做，请浏览你想复刻的公共仓库。在页面的右上方，你会发现 <code>Fork</code> 按钮。点击该按钮并等待几秒钟，你会看到，新复刻的仓库会在你的 GitHub 账户下创建。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/02/image-29.png" class="kg-image" alt="image-29" width="600" height="400" loading="lazy"><figcaption>Fork 按钮</figcaption></figure><p>在复刻仓库后，按照我们上面学到的步骤开始贡献。</p><h2 id="--1">我们来练习复刻</h2><p>作为一个新手，你想通过练习复刻来增强信心吗？让我们来做吧。你可以执行这些任务来练习复刻。</p><ul><li>打开这个公共仓库：<a href="https://github.com/atapas/fork-me">https://github.com/atapas/fork-me</a></li><li>创建一个与你的 GitHub 用户名相同的文件夹（对我来说是 atapas）</li><li>在该文件夹中添加一个 <code>Readme.md</code> 文件，内容可以是你选择的任何文字</li><li>在上游创建一个拉动请求 <code>Pull Request</code>。如果一切顺利，我将审查并合并它</li></ul><p>如果你出错了，不要担心，继续尝试，你会成功的。这将是一个为开源仓库做贡献的练习场。</p><h2 id="--2"><strong>总结</strong></h2><ul><li>Git 是一个在开发者社区中被广泛使用的版本控制工具。如果你是 Git 的初学者，可以在<a href="https://www.youtube.com/watch?v=vWtu4mzUgQo">这里</a>了解它。</li><li>GitHub 是一个流行的管理 Git 仓库的应用程序。任何人都可以为公共仓库做出贡献。</li><li>复刻是一个很好的工具，可以把别人仓库里的源代码复制到你的仓库里，并对其进行贡献。</li><li>学习并使用复刻的工作流程是很简单的。</li></ul><h2 id="--3"><strong>结束之前......</strong></h2><p>我希望你觉得这篇文章有帮助。请练习复刻并为开源项目做出贡献。如果你想进一步讨论，可以在 Twitter 上联系我。</p><p>我也会在这些平台上分享我在 JavaScript、Web 开发、职业和博客方面的学习成果：</p><ul><li><a href="https://twitter.com/tapasadhikary">在 Twitter 上关注我</a></li><li><a href="https://www.youtube.com/tapasadhikary?sub_confirmation=1">订阅我的 YouTube 频道</a></li><li><a href="https://github.com/atapas">GitHub 上的副业项目</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Git 和 GitHub 教程——版本控制入门 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：Git and GitHub Tutorial – Version Control for Beginners [https://www.freecodecamp.org/news/git-and-github-for-beginners/]，作者：Ihechikara Vincent Abba [https://www.freecodecamp.org/news/author/ihechikara/] Git 和 GitHub 是各个领域的开发者都应该学习的两项技术。 如果你是一个初级开发者，你可能会认为这两个术语意味着同样的事情--但它们是不同的。 本教程将帮助你了解什么是 Git 和版本控制，你需要知道的基本 Git 命令，如何使用其功能来提高工作效率，以及如何使用 GitHub 来扩展这些功能。 本指南对初学者友好，因为例子将非常容易理解。它也将是一个通用的教程，所以不管你最喜欢的编程语言是什么，你都可以跟着学。 对于我们的项目，我们将做一个写在文本（txt）文件中的待办事项清单。你将看到我们如何使用 Git 的功能来工作，并创建列表的最终版本。 你需要先准备 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/git-and-github-for-beginners/</link>
                <guid isPermaLink="false">621de5a879a578061070ceb9</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Tue, 01 Mar 2022 09:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/07/git-github.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/git-and-github-for-beginners/">Git and GitHub Tutorial – Version Control for Beginners</a>，作者：<a href="https://www.freecodecamp.org/news/author/ihechikara/">Ihechikara Vincent Abba</a></p><!--kg-card-begin: markdown--><p>Git 和 GitHub 是各个领域的开发者都应该学习的两项技术。</p>
<p>如果你是一个初级开发者，你可能会认为这两个术语意味着同样的事情--但它们是不同的。</p>
<p>本教程将帮助你了解什么是 Git 和版本控制，你需要知道的基本 Git 命令，如何使用其功能来提高工作效率，以及如何使用 GitHub 来扩展这些功能。</p>
<p>本指南对初学者友好，因为例子将非常容易理解。它也将是一个通用的教程，所以不管你最喜欢的编程语言是什么，你都可以跟着学。</p>
<p>对于我们的项目，我们将做一个写在文本（txt）文件中的待办事项清单。你将看到我们如何使用 Git 的功能来工作，并创建列表的最终版本。</p>
<h3 id="">你需要先准备好的</h3>
<p>为了完成本教程，你将需要以下条件：</p>
<ul>
<li>一个命令行界面（或者说是终端）</li>
<li>一个你喜欢的文本编辑器（我将使用 VS Code）</li>
<li>一个 GitHub 账户</li>
</ul>
<h2 id="git">什么是 Git</h2>
<p>Git 是一个版本控制系统，可以让你跟踪你对文件所做的修改。使用 Git，你可以恢复到文件的各种状态（就像一台时间旅行机）。你也可以制作一个文件的副本，对该副本进行修改，然后将这些修改合并到原来的副本中。</p>
<p>例如，你可以在一个网站的登陆页面上工作，发现你不喜欢这个导航栏。但与此同时，你可能不想开始改变它的组件，因为它可能会变得更糟。</p>
<p>有了 Git，你可以为该文件创建一个相同的副本，然后对导航栏进行修改。然后，当你对你的改动感到满意时，你可以把副本合并到原文件中。</p>
<p>你并不局限于将 Git 仅仅用于源代码文件，你也可以用它来跟踪文本文件，甚至是图像。这意味着，Git 不仅仅是为开发者服务的，任何人都可以找到它的帮助。</p>
<h3 id="git">如何安装 Git</h3>
<p>为了使用 Git，你必须在你的电脑上安装它。要做到这一点，你可以在<a href="https://git-scm.com/downloads">官方网站</a> 下载最新版本。你可以从给出的选项中下载适合你的操作系统。</p>
<p>你也可以用命令行来安装 Git，但由于每个操作系统的命令都不一样，我们将专注于更通用的方法。</p>
<h3 id="git">如何配置 Git</h3>
<p>我将假设此时你已经安装了 Git。为了验证这一点，你可以在命令行上运行这个命令：<code>git --version</code>。这将显示当前安装在你电脑上的版本。</p>
<p>接下来，你需要做的是设置你的用户名和电子邮件地址。Git 会使用这些信息来识别谁对文件进行了修改。</p>
<p>要设置你的用户名，输入并执行这些命令：<code>git config --global user.name "YOUR_USERNAME"</code> 和 <code>git config --global user.email "YOUR_EMAIL"</code>。请确保用你选择的值替换 <code>"YOUR_USERNAME"</code> 和<code>"YOUR_EMAIL"</code>。</p>
<h2 id="git">如何在 Git 中创建和初始化一个项目</h2>
<p>我们终于完成了安装和设置 Git 的工作，现在是时候创建我们的项目了。</p>
<p>我在桌面上创建了一个名为 <code>Git and GitHub tutorial</code>的文件夹。使用命令行，进入到你新项目的文件夹位置。对我来说，我将运行以下命令：</p>
<p><code>cd desktop</code></p>
<p><code>cd Git and GitHub tutorial</code></p>
<p>如果你是命令行的新手，并且还在学习如何使用它来查看你的电脑文件夹，那么我建议使用微软的 <code>Visual Studio Code</code>。它是一个代码编辑器，有一个内置的终端来执行命令。你可以下载它 <a href="https://code.visualstudio.com/download">这里</a>。</p>
<p>安装VS Code后，在编辑器中打开你的项目，为你的项目打开一个新的终端。这将自动把终端/命令行指向你的项目的路径。</p>
<p>现在要初始化你的项目，只需运行<code>git init</code>。这将告诉 Git 准备好开始监视你的文件的每一个变化。它看起来像这样：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/Screenshot--95-.png" alt="Screenshot--95-" width="600" height="400" loading="lazy"></p>
<pre><code class="language-shell">git init
</code></pre>
<p>第一行是关于我的电脑的信息和文件夹存在的路径。第二行是命令<code>git init</code>，第三行是发回的响应，告诉我我的仓库（repo）已经被初始化了。它被认为是空的，因为我们还没有告诉 Git 要追踪哪些文件。</p>
<p>仓库只是定义一个被 Git 监视/跟踪的项目的另一种方式。</p>
<h3 id="git">Git 项目文件</h3>
<p>我只创建了一个名为 "todo.txt "的文件。这个文件看起来是这样的：</p>
<pre><code class="language-txt">MY TO-DO LIST

1. Write an article.
2. Code.
3. Study books.
4. Attend classes on time.
5. Visit aunt.
6. Apply for remote jobs. 
</code></pre>
<p>在我们继续学习其他 Git 命令之前，让我们先谈谈 GitHub。</p>
<h2 id="github">什么是 GitHub</h2>
<p>GitHub 是一个为 Git 存储库提供的在线托管服务。想象一下，你在家里做一个项目，当你不在的时候，也许是在朋友那里，你突然想起了一个代码错误的解决方案，让你几天都坐立不安。</p>
<p>你不能进行这些修改，因为你的电脑不在你身边。但如果你的项目托管在 GitHub 上，你就可以在你能接触到的任何电脑上用命令访问和下载该项目。然后你就可以进行修改，并将最新版本推送回 GitHub。</p>
<p>总之，GitHub 可以让你在他们的平台上存储你的 repo。GitHub 的另一个了不起的功能是你可以在任何地方与其他开发者进行合作。</p>
<p>现在我们已经在本地创建并初始化了我们的项目，让我们把它推送到 GitHub。</p>
<p>如果你是初学者，你会遇到一些新的术语，比如推送（push）、提交（commit）、添加（add）等等——但不要被它们吓倒，只要多加练习，你就能记住这些术语和它们的作用。</p>
<h2 id="github">如何推送一个仓库到 GitHub</h2>
<p>我将把本节分为几个步骤，以帮助你更清楚地了解这个过程。</p>
<h3 id="github">第一步 - 创建一个 GitHub 账户</h3>
<p>为了能够使用 GitHub，你必须先创建一个账户。你可以在他们的[网站]（<a href="https://github.com/">https://github.com/</a>) 创建。</p>
<h3 id="">第二步 - 创建一个存储库</h3>
<p>你可以点击页面右上角的<code>+</code>符号，然后选择 “New repository”。给你的版本库起个名字，然后向下滚动并点击 “Create repository”。</p>
<h3 id="">第三步 - 添加和提交文件</h3>
<p>在我们添加（add）和提交（commit）我们的文件之前，你需要了解一个文件被 Git 跟踪的阶段。</p>
<h4 id="">已经提交的状态</h4>
<p>当一个文件的所有修改都被保存在本地的 repo 中时，该文件就处于<strong>提交的（committed）</strong> 状态。处于提交阶段的文件是可以被推送到远程 repo（在 GitHub 上）的文件。</p>
<h4 id="">已修改状态</h4>
<p>处于<strong>修改（modified）</strong> 状态的文件已经做了一些修改，但还没有保存。这意味着该文件的状态与之前在提交状态下的状态有了改变。</p>
<h4 id="">暂存状态</h4>
<p>处于<strong>暂存（staged）</strong> 状态的文件意味着它可以被提交了。在这种状态下，所有必要的修改都已经完成，所以下一步就是把文件移到提交状态。</p>
<p>你可以把 Git 想象成一台摄像机，这样就能更好地理解。只有当文件到达提交状态时，相机才会进行快照。在这个状态之后，相机开始比较正在对同一文件进行的修改和最后一次快照（这就是修改状态）。而当所需的修改完成后，文件就会被分阶段移动到提交状态，以便进行新的快照。</p>
<p>目前，这可能是一个很大的信息量，但不要气馁——随着实践的进行，它变得更容易。</p>
<h3 id="git">如何在 Git 中添加文件</h3>
<p>当我们第一次初始化我们的项目时，该文件没有被 Git 追踪到。要做到这一点，我们使用这个命令<code>git add .</code>，将添加当前目录下所有文件。如果你想添加一个特定的文件，也许是一个名为<code>about.txt</code>的文件，你可以用<code>git add about.txt</code>。</p>
<p>现在，我们的文件已经处于暂存状态（staged state）。这条命令之后你不会得到回应，但要知道你的文件处于什么状态，你可以运行<code>git status</code>命令。</p>
<h3 id="git">如何在 Git 中提交文件</h3>
<p>文件下一个状态处于提交状态（committed state）。为了提交我们的文件，我们使用<code>git commit -m "first commit"</code>命令。</p>
<p>命令的第一部分 "git commit "告诉 Git，所有被暂存的文件都准备好提交了，所以是时候进行快照了。第二部分 <code>-m "first commit"</code> 是提交信息。<code>-m</code>是信息的缩写，而括号内的文字是提交信息。</p>
<p>执行这个命令后，你应该得到一个类似这样的响应：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/Screenshot--97-.png" alt="Screenshot--97-" width="600" height="400" loading="lazy"></p>
<pre><code class="language-shell">git commit
</code></pre>
<p>现在我们的文件已经处于提交状态（committed state）。</p>
<h3 id="github">第四步 - 推送仓库到 GitHub</h3>
<p>创建完仓库后，你的浏览器应该跳到一个页面，告诉你如何在本地创建一个仓库或推送一个现有仓库。</p>
<p>在我们的例子中，项目已经存在于本地，所以我们将使用 “.…or push an existing repository from the command line” 部分的命令。这些是命令：</p>
<pre><code class="language-shell">git remote add origin https://github.com/ihechikara/git-and-github-tutorial.git
git branch -M main
git push -u origin main
</code></pre>
<p>第一个命令 <code>git remote add origin [https://github.com/ihechikara/git-and-github-tutorial.git](https://github.com/ihechikara/git-and-github-tutorial.git)</code>，在你的本地 repo 和 Github 上的远程 repo 之间建立连接。</p>
<p>你的远程项目的 URL 应该与上面的完全不同。所以要根据你的实际 URL，确保你是按照步骤，用你自己的远程 repo 工作。执行这个命令后，你通常不会得到回应，但要确保你有互联网连接。</p>
<p>第二个命令 <code>git branch -M main</code> 将主分支的名字改为 "main"。默认的分支可能被创建为 "master"，但 "main "是现在这个 repo 的标准名称。这里通常没有回应。</p>
<p>最后一个命令 <code>git push -u origin main</code>  将你的 repo 从本地设备推送到 GitHub。你应该得到与此类似的回应：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/Screenshot--102-.png" alt="Screenshot--102-" width="600" height="400" loading="lazy"></p>
<pre><code class="language-shell">git push
</code></pre>
<p>为了帮助你加深对文件阶段的理解，我将对文件进行修改，然后将新版本推送到 GitHub。</p>
<p>回顾一下，我们的文件现在处于提交状态。让我们对文件进行修改，并注意到这些状态。</p>
<p>我将在待办事项列表中添加一个新任务：</p>
<pre><code class="language-txt">MY TO-DO LIST

1. Write an article.
2. Code.
3. Study books.
4. Attend classes on time.
5. Visit aunt.
6. Apply for remote jobs. 
7. Practice code
</code></pre>
<p>添加新任务后，运行<code>git status</code>命令。你应该看到这样的情况：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/Screenshot--98-.png" alt="Screenshot--98-" width="600" height="400" loading="lazy"></p>
<pre><code class="language-shell">git status
</code></pre>
<p>在对文件进行修改后，它移到了修改状态--但它还没有被分期提交，所以你还不能把它推送到 GitHub。Git 并没有对这个当前状态进行最终的快照，因为它只是将我们现在所做的修改与最后的快照进行比较。</p>
<p>现在我们要添加（stage）这个文件，然后提交（commit ）并推送（push）它。这与上一节的做法相同。</p>
<p>我们首先使用<code>git add .</code>添加文件，它添加了当前目录下的所有文件（在我们的例子中是一个文件）。然后我们通过运行<code>git commit -m "added new task"</code>来提交该文件，接着是<code>git push -u origin main</code>。</p>
<p>这就是将修改过的文件推送到 GitHub 的三个步骤。添加（add），提交（commit），然后推送（push）。我希望你现在理解了文件阶段和与之相关的命令。</p>
<h2 id="gitbranch">如何在 Git 中使用分支（branch）</h2>
<p>有了分支，你可以在不破坏原始副本的情况下，创建一个你想要处理的文件副本。你可以把这些修改合并到原始副本上，或者让分支保持独立。</p>
<p>在我们开始使用分支之前，我想给大家看一下我们的 repo 的可视化表示，它看起来像这样：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/g638.png" alt="g638" width="600" height="400" loading="lazy"></p>
<p>main banch</p>
<p>上图显示了我们的主分支和最近的两次提交（第一次提交和添加的新任务提交）。</p>
<p>在这一点上，我想在列表中添加更多的任务，但我还不确定是否要把它们放在主列表中。因此，我将创建一个名为 <code>test</code> 的新分支，看看加入更多任务后我的列表会是什么样子。</p>
<p>要创建一个新的分支，运行这个命令。<code>git checkout -b test</code>。我将把它分开来。</p>
<p><code>checkout</code>告诉 Git 它应该切换到一个新的分支。<code>b</code>告诉 Git 创建一个新的分支。<code>test</code>是要创建和切换到的分支的名字。以下是你应该得到的响应：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/Screenshot--99-.png" alt="Screenshot--99-" width="600" height="400" loading="lazy"></p>
<pre><code class="language-shell">git checkout -b
</code></pre>
<p>现在我们已经创建了一个新的分支，这就是我们的 repo 的样子：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/g664.png" alt="g664" width="600" height="400" loading="lazy"></p>
<pre><code class="language-shell">git branch
</code></pre>
<p>我们从上次提交的状态中创建了新的分支。现在让我们为这个新分支添加更多任务。</p>
<pre><code class="language-txt">MY TO-DO LIST

1. Write an article.
2. Code.
3. Study books.
4. Attend classes on time.
5. Visit aunt.
6. Apply for remote jobs. 
7. Practice code
8. Complete internship task.
9. Practice chess openings.
10. Solve chess puzzles.
11. Check exam schedule. 
</code></pre>
<p>我增加了四个新任务。要把新的状态合并到主分支（main branch）。你必须先把这个分支分阶段并提交，由于我们在上一节中做了两次，我就不详细介绍如何提交。</p>
<p>你应该自己试着做一下，这样你就会明白它是如何工作的。作为提示，先添加文件，然后带着信息提交（参考上一节的细节，告诉你如何做）。</p>
<p>提交完测试分支后，通过运行以下命令切换回主分支，<code>git checkout main</code>。</p>
<p>你是否注意到，我们没有添加<code>-b</code>？这是因为我们不是在创建一个新的分支，而是切换到一个现有的分支。你可以通过运行<code>git branch</code>命令来检查你的 repo 中存在的所有分支。</p>
<p>现在，我们可以通过运行<code>git merge test</code>，将测试分支中的修改合并到主分支中。这时，你会看到测试分支中的所有改动都反映在主分支中。你还应该收到类似这样的回复：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/Screenshot--100-.png" alt="Screenshot--100-" width="600" height="400" loading="lazy"></p>
<pre><code class="language-shell">git merge
</code></pre>
<p>下面是我们的 repo 的图示：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/g816.png" alt="g816" width="600" height="400" loading="lazy"></p>
<p>如果你继续将你的 repo 推送到 GitHub，你会发现测试分支不会被推送。它只会保留在你的本地 repo 中。如果你想推送你的测试分支，可以用<code>git checkout test</code>切换到该分支，然后运行<code>git push -u origin test</code>。</p>
<h2 id="git">如何在 Git 中拉取一个仓库</h2>
<p>在 Git 中拉取意味着将远程仓库的当前状态克隆到你的电脑/仓库中。当你想在不同的电脑上操作你的仓库时，或者当你在网上为一个开源项目做贡献时，这就很方便了。</p>
<p>要测试这一点，不用担心切换到新的电脑。只要运行<code>cd ..</code> 就可以离开当前目录并返回上一层目录。在我自己的例子中，我已经导航回到了我的桌面。</p>
<p>进入 GitHub，在你的仓库的主页上，你应该看到一个绿色的按钮，上面写着 "code"。当你点击这个按钮时，你应该在一个下拉菜单中看到一些选项。继续并复制 HTTPS URL。</p>
<p>之后，运行<code>git clone YOUR_HTTPS_URL</code>。这个命令会把远程版本库拉到你的本地电脑上，放在一个叫<code>git-and-git-tutorial</code>的文件夹里。就是说：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/Screenshot--101-.png" alt="Screenshot--101-" width="600" height="400" loading="lazy"></p>
<pre><code class="language-shell">git clone
</code></pre>
<h2 id="">结语</h2>
<p>本文介绍了有助于你开始使用 Git 的基本命令。我们还开始学习如何使用 GitHub。</p>
<p>如果你已经跟到了这一步，那么恭喜你，你已经可以开始了。现在无论你使用什么编程语言，都可以在你的项目中使用 Git。</p>
<p>你应该知道，这些并不是 Git 中存在的所有命令，所以你可以随时做更多的研究来了解更多的命令和它们的用途。 <a href="https://www.freecodecamp.org/news/what-is-git-learn-git-version-control/">这篇文章</a> 和 <a href="https://www.freecodecamp.org/news/git-cheat-sheet/">手册</a> 是很好的开始。 <a href="https://gist.github.com/brandon1024/14b5f9fcfd982658d01811ee3045ff1e">这个gist</a> 是查看更多 Git 命令的详细列表的好地方。</p>
<p>你可以在推特上找到我 <a href="https://twitter.com/Ihechikara2">@ihechikara2</a>。祝你编程愉快！</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 构建更优的 GitHub Action 完成 Algolia 数据上传 ]]>
                </title>
                <description>
                    <![CDATA[ 场景 程序员喜欢写博客，大都喜欢自己 Host 一个自己的 Blog。通常 Blog 会有一个全局的搜索功能，开源博客一般都会选择 lunrjs [https://lunrjs.com/] 或者 algolia [https://www.algolia.com/] 等。我的 Blog 是基于 Hugo [https://gohugo.io/] 构建的，使用的主题是 LoveIt [https://hugoloveit.com/]，集成的是 algolia 的搜索方式。 对于存储在 algolia 上的数据，我是通过 GitHub Action：Algolia Docsearch Indexer [https://github.com/marketplace/actions/algolia-docsearch-indexer]  来上传的，之前使用是没有问题的。 问题 突然有一天我要使用搜索功能，但是怎么也搜索不到我想搜索的内容，看了 GitHub 项目的构建状态，一切正常，然后登陆到 algolia 后台一看， 最后一次的数据更新是在 21 年 8 月； 然后打开最近的博客构建记录 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/upload-algolia-index-with-github-action-build-by-myself/</link>
                <guid isPermaLink="false">61ebba03141b4e06737474a9</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 谷中仁 ]]>
                </dc:creator>
                <pubDate>Sat, 22 Jan 2022 07:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/01/Github-Action.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="">场景</h2>
<p>程序员喜欢写博客，大都喜欢自己 Host 一个自己的 Blog。通常 Blog 会有一个全局的搜索功能，开源博客一般都会选择 <a href="https://lunrjs.com/">lunrjs</a> 或者 <a href="https://www.algolia.com/">algolia</a> 等。我的 Blog 是基于 <a href="https://gohugo.io/">Hugo</a> 构建的，使用的主题是 <a href="https://hugoloveit.com/">LoveIt</a>，集成的是 algolia 的搜索方式。</p>
<p>对于存储在 algolia 上的数据，我是通过 GitHub Action：<a href="https://github.com/marketplace/actions/algolia-docsearch-indexer">Algolia Docsearch Indexer</a> 来上传的，之前使用是没有问题的。</p>
<h2 id="">问题</h2>
<p>突然有一天我要使用搜索功能，但是怎么也搜索不到我想搜索的内容，看了 GitHub 项目的构建状态，一切正常，然后登陆到 algolia 后台一看， 最后一次的数据更新是在 21 年 8 月； 然后打开最近的博客构建记录，看了执行如下 GitHub workflow 的 yaml 日志，大吃一惊：程序执行错误，但还是在最后给我们送了一个🚀，同样是写代码，你能忍？</p>
<pre><code class="language-yaml">    - uses: darrenjennings/algolia-docsearch-action@master
      with:
        algolia_application_id: ${{secrets.ALGOLIA_APPLICATION_ID}}
        algolia_api_key: ${{secrets.ALGOLIA_API_KEY}}
        file: './public/index.json'
</code></pre>
<p>运行日志</p>
<pre><code class="language-log">Run darrenjennings/algolia-docsearch-action@master
/usr/bin/docker run --name a682564a13d76444749d3b720346ba2365371_9474ad --label 6a6825 --workdir /github/workspace --rm -e INPUT_ALGOLIA_APPLICATION_ID -e INPUT_ALGOLIA_API_KEY -e INPUT_FILE -e HOME -e GITHUB_JOB -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_REPOSITORY_OWNER -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RETENTION_DAYS -e GITHUB_RUN_ATTEMPT -e GITHUB_ACTOR -e GITHUB_WORKFLOW -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GITHUB_EVENT_NAME -e GITHUB_SERVER_URL -e GITHUB_API_URL -e GITHUB_GRAPHQL_URL -e GITHUB_REF_NAME -e GITHUB_REF_PROTECTED -e GITHUB_REF_TYPE -e GITHUB_WORKSPACE -e GITHUB_ACTION -e GITHUB_EVENT_PATH -e GITHUB_ACTION_REPOSITORY -e GITHUB_ACTION_REF -e GITHUB_PATH -e GITHUB_ENV -e RUNNER_OS -e RUNNER_ARCH -e RUNNER_NAME -e RUNNER_TOOL_CACHE -e RUNNER_TEMP -e RUNNER_WORKSPACE -e ACTIONS_RUNTIME_URL -e ACTIONS_RUNTIME_TOKEN -e ACTIONS_CACHE_URL -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "/home/runner/work/_temp/_github_home":"/github/home" -v "/home/runner/work/_temp/_github_workflow":"/github/workflow" -v "/home/runner/work/_temp/_runner_file_commands":"/github/file_commands" -v "/home/runner/work/blog/blog":"/github/workspace" 6a6825:64a13d76444749d3b720346ba2365371  "***" "***" "./public/index.json"
Cloning into 'docsearch-scraper'...
Collecting pipenv
  Downloading pipenv-2021.11.23-py2.py3-none-any.whl (3.6 MB)
Collecting virtualenv
  Downloading virtualenv-20.10.0-py2.py3-none-any.whl (5.6 MB)
Requirement already satisfied: pip&gt;=18.0 in /usr/local/lib/python3.6/site-packages (from pipenv) (21.2.4)
Requirement already satisfied: setuptools&gt;=36.2.1 in /usr/local/lib/python3.6/site-packages (from pipenv) (57.5.0)
Collecting virtualenv-clone&gt;=0.2.5
  Downloading virtualenv_clone-0.5.7-py3-none-any.whl (6.6 kB)
Collecting certifi
  Downloading certifi-2021.10.8-py2.py3-none-any.whl (149 kB)
Collecting importlib-resources&gt;=1.0
  Downloading importlib_resources-5.4.0-py3-none-any.whl (28 kB)
Collecting backports.entry-points-selectable&gt;=1.0.4
  Downloading backports.entry_points_selectable-1.1.1-py2.py3-none-any.whl (6.2 kB)
Collecting six&lt;2,&gt;=1.9.0
  Downloading six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting importlib-metadata&gt;=0.12
  Downloading importlib_metadata-4.8.3-py3-none-any.whl (17 kB)
Collecting filelock&lt;4,&gt;=3.2
  Downloading filelock-3.4.1-py3-none-any.whl (9.9 kB)
Collecting distlib&lt;1,&gt;=0.3.1
  Downloading distlib-0.3.4-py2.py3-none-any.whl (461 kB)
Collecting platformdirs&lt;3,&gt;=2
  Downloading platformdirs-2.4.0-py3-none-any.whl (14 kB)
Collecting zipp&gt;=0.5
  Downloading zipp-3.6.0-py3-none-any.whl (5.3 kB)
Collecting typing-extensions&gt;=3.6.4
  Downloading typing_extensions-4.0.1-py3-none-any.whl (22 kB)
Installing collected packages: zipp, typing-extensions, importlib-metadata, six, platformdirs, importlib-resources, filelock, distlib, backports.entry-points-selectable, virtualenv-clone, virtualenv, certifi, pipenv
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
Successfully installed backports.entry-points-selectable-1.1.1 certifi-2021.10.8 distlib-0.3.4 filelock-3.4.1 importlib-metadata-4.8.3 importlib-resources-5.4.0 pipenv-2021.11.23 platformdirs-2.4.0 six-1.16.0 typing-extensions-4.0.1 virtualenv-20.10.0 virtualenv-clone-0.5.7 zipp-3.6.0
WARNING: You are using pip version 21.2.4; however, version 21.3.1 is available.
You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.
Installing dependencies from Pipfile.lock (aabb41)...
Traceback (most recent call last):
  File "docsearch", line 5, in &lt;module&gt;
    run()
  File "/github/workspace/docsearch-scraper/cli/src/index.py", line 161, in run
    exit(command.run(sys.argv[2:]))
  File "/github/workspace/docsearch-scraper/cli/src/commands/run_config.py", line 21, in run
    return run_config(args[0])
  File "/github/workspace/docsearch-scraper/cli/../scraper/src/index.py", line 33, in run_config
    config = ConfigLoader(config)
  File "/github/workspace/docsearch-scraper/cli/../scraper/src/config/config_loader.py", line 72, in __init__
    for key, value in list(data.items()):
AttributeError: 'list' object has no attribute 'items'
🚀 Successfully indexed and uploaded the results to Algolia

</code></pre>
<p>面对一个磨洋工的工具，作为程序员的我们肯定不能忍。</p>
<p>打开 GitHub 上这个 Action 的<a href="https://github.com/darrenjennings/algolia-docsearch-action">源码</a>, 根据对 Action 构建的了解和现有代码，作者使用的是 Python 和 algolia 自己在 GitHub 上开源的<a href="https://github.com/algolia/docsearch-scraper.git">工具</a>, 然后执行一个  Python 脚本上传文件到 algolia 的；根据以往经验，由 Python 构建的项目镜像一般都比较大，在本地测试了一下，果不其然的大。</p>
<h3 id="">面临的问题</h3>
<ul>
<li>工具损坏</li>
<li>镜像体积大</li>
</ul>
<h2 id="spike">方案Spike</h2>
<table>
<thead>
<tr>
<th>Item</th>
<th>Option1 使用algolia 自己的 Restful 接口</th>
<th>Option 2 algolia SDK</th>
</tr>
</thead>
<tbody>
<tr>
<td>描述</td>
<td>使用 algolia自己的Restful 接口实现上传</td>
<td>使用其官方提供的SDK 编写代码来集成</td>
</tr>
<tr>
<td>是否推荐</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>实现难度</td>
<td>Middle</td>
<td>Low</td>
</tr>
<tr>
<td>优点</td>
<td>流程可控</td>
<td>简单直接，无需担心错误情况的处理</td>
</tr>
<tr>
<td>缺点</td>
<td>需要写更多的代码来控制整个流程</td>
<td>整个上传过程不可控</td>
</tr>
<tr>
<td>安全问题</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>相对难度</td>
<td>Middle</td>
<td>Low</td>
</tr>
<tr>
<td>相对成本</td>
<td>Middle</td>
<td>Low</td>
</tr>
</tbody>
</table>
<p>综上分析，使用 Option2 SDK 的方案更佳。</p>
<h2 id="">执行方案</h2>
<p>新建 GitHub Action 项目，我们使用 Dockerfile 的方式构建上传索引的方案；</p>
<ul>
<li>新建 entrypoint.sh 并写入如下代码, 脚本执行需要传入如下几个变量：</li>
</ul>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr>
<td>FILE_PATH</td>
<td>要上传的文件路径</td>
<td>Yes</td>
</tr>
<tr>
<td>ALGOLIA_APPLICATION_ID</td>
<td>Algolia 平台的应用Id</td>
<td>Yes</td>
</tr>
<tr>
<td>ADMIN_API_KEY</td>
<td>Algolia 上传所用的API Key</td>
<td>Yes</td>
</tr>
<tr>
<td>INDEX_NAME</td>
<td>在 Algolia 上你所创建的索引名</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<pre><code class="language-shell">#!/bin/sh
set -eu

npm install -g @algolia/cli

algolia import -s $FILE_PATH -a $APPLICATION_ID -k $ADMIN_API_KEY -n $INDEX_NAME 

if [ "$?" != "0" ] ; then
  echo "😢 Failed to upload your data to Algolia, PLZ report an issue, thx!"
  exit 1
fi

echo "🚀 Successfully uploaded!"
</code></pre>
<ul>
<li>新建 Dockerfile 并写入如下代码，在此我们使用最小化的 Node 镜像 <code>node:lts-alpine</code></li>
</ul>
<pre><code class="language-dcokerfile">FROM node:lts-alpine
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
</code></pre>
<ul>
<li>更新README, 完善使用文档。</li>
</ul>
<p>详细代码请参考 <a href="https://github.com/guzhongren/algolia-docsearch-upload-action/blob/main/Dockerfile">algolia-docsearch-upload-action</a>。</p>
<h2 id="">验证结果</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2022/01/-------1.svg" class="kg-image" alt="-------1" width="600" height="400" loading="lazy"><figcaption>运行效率对比</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2022/01/------.svg" class="kg-image" alt="------" width="600" height="400" loading="lazy"><figcaption>镜像大小对比</figcaption></figure><!--kg-card-begin: markdown--><h2 id="refs">Refs</h2>
<ul>
<li><a href="https://guzhongren.github.io/">博客: https://guzhongren.github.io/</a></li>
<li><a href="https://github.com/marketplace/actions/algolia-docsearch-indexer">Algolia Docsearch Indexer: https://github.com/marketplace/actions/algolia-docsearch-indexer</a></li>
<li><a href="https://docs.github.com/en/actions">GitHub Action: https://docs.github.com/en/actions</a></li>
</ul>
<!--kg-card-end: markdown--><p>欢迎阅读我的<a href="https://guzhongren.github.io/2022/01/%E4%BD%BF%E7%94%A8cypress%E5%88%9B%E5%BB%BA%E6%B5%8B%E8%AF%95%E9%95%9C%E5%83%8F%E5%B9%B6%E5%AE%8C%E6%88%90e2e%E6%B5%8B%E8%AF%95/">更多文章</a>。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 GitHub Actions 将 Next.js 网站部署到 AWS S3 ]]>
                </title>
                <description>
                    <![CDATA[ Next.js和静态 web应用的好处是，你将它们储存在对象储存里，几乎可以在任何地方运行, 比如AWS S3。但是每次手动更新这些文件可能是一件痛苦的事. 我们如何用Github Actions来自动持续部署我们的应用程序到S3?  * 什么是GitHub Actions  * 什么是持续部署  * 我们怎么去构建  * 第0步: 在GitHub上建立一个新的Next.js项目  * 第1步: 手工创建一个用于部署next.js项目的新S3桶  * 第2步: 创建一个新的GitHub Action工作流来自动化构建一个Next.js项目  * 第3步: 配置一个GitHub Action，部署静态网站到S3上 什么是GitHub Actions GitHub Actions是GitHub的一项免费服务，它允许我们用代码实现任务自动化。 我在这篇文章 [https://www.freecodecamp.org/news/what-are-github-actions-and-how-can-you-automate-tests-and-slack-notifications/] ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-use-github-actions-to-deploy-a-next-js-website-to-aws-s3/</link>
                <guid isPermaLink="false">61dfafe26161280665ed8021</guid>
                
                    <category>
                        <![CDATA[ NextJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Thu, 13 Jan 2022 04:30:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/01/actions-s3.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Next.js和静态 web应用的好处是，你将它们储存在对象储存里，几乎可以在任何地方运行, 比如AWS S3。但是每次手动更新这些文件可能是一件痛苦的事.</p>
<p>我们如何用Github Actions来自动持续部署我们的应用程序到S3?</p>
<ul>
<li>什么是GitHub Actions</li>
<li>什么是持续部署</li>
<li>我们怎么去构建</li>
<li>第0步: 在GitHub上建立一个新的Next.js项目</li>
<li>第1步: 手工创建一个用于部署next.js项目的新S3桶</li>
<li>第2步: 创建一个新的GitHub Action工作流来自动化构建一个Next.js项目</li>
<li>第3步: 配置一个GitHub Action，部署静态网站到S3上</li>
</ul>
<h2 id="githubactions">什么是GitHub Actions</h2>
<p>GitHub Actions是GitHub的一项免费服务，它允许我们用代码实现任务自动化。</p>
<p>我在<a href="https://www.freecodecamp.org/news/what-are-github-actions-and-how-can-you-automate-tests-and-slack-notifications/">这篇文章</a> 里介绍了如何用它来自动化任务，比如在运行代码中的测试，并向Slack发送通知。</p>
<p>它们提供一种灵活的方式，在我们现有的工作流基础上为自动化运行代码。这提供了很多的可能性，比如部署我们的网站。</p>
<h2 id="awss3">什么是 AWS S3？</h2>
<p><a href="https://aws.amazon.com/s3/">S3</a>（简单存储服务）是AWS的一个对象存储服务。它允许你在云上轻松存储文件，使它们在世界各地都可以使用。</p>
<p>它还允许你将这些文件作为一个网站使用。因为我们可以把HTML文件作为一个对象上传，我们也可以配置S3，让一个HTTP请求访问该文件。 这意味着，我们可以<a href="https://www.freecodecamp.org/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/">在S3中直接托管整个网站</a>.</p>
<h2 id="">什么使持续部署？</h2>
<p>持续部署（Continuous Deployment），通常是指将代码处在可发布的状态，自动化部署代码，缩短部署部署时间。</p>
<p>在这个示例中，我们将配置项目，持续将更新推送或者合并到git主分支，部署到网站。</p>
<h2 id="">我们怎样去构建?</h2>
<p>我们首先要使用默认的Next.js起始模板初始化一个简单的<a href="https://nextjs.org/">Next.js</a>应用，并配置将其编译成静态文件。</p>
<p>如果你不向创建一个Next.js项目，你甚至用一个简单的HTML文件跟着做，并不运行任何构建命令。但Next.js是构建动态网站应用的一种现代化方式，所以我将从这里开始。</p>
<p>随着我们的网站文件准备就绪，我们将在AWS中创建和配置一个S3桶，在S3桶上托管我们的网站。</p>
<p>最后，我们将创建一个新的GitHub Action工作流，当我们的主分支（<code>main</code>）发生新的变化时，它将自动更新S3中网站文件。</p>
<h2 id="0githubnextjs">第0步：在GitHub上创建一个新的Next.js项目</h2>
<p>我们将从Next.js的默认模板开始。</p>
<p>在创建你像创建项目的目录后，运行:</p>
<pre><code>yarn create next-app my-static-website
# 或者
npx create-next-app my-static-website
</code></pre>
<p>注意： 请注意将<code>my-static-website</code>替换为你想选择的名称。我们将在本教程的其余部分使用这个名字。</p>
<p>如果进入到该目录并运行开发命令，你应该能够成功启动你的开发服务器。</p>
<pre><code>cd my-static-website
yarn dev
# or
npm run dev
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/new-nextjs-app.jpg" alt="new-nextjs-app" width="600" height="400" loading="lazy"></p>
<p>New Next.js App</p>
<p>接下来，让我们把我们的项目配置为静态编译。</p>
<p>在 <code>package.json</code>文件, 把<code>build</code> 脚本改为 :</p>
<pre><code class="language-json">"build": "next build &amp;&amp; next export",
</code></pre>
<p>这样做的目的时告诉Next将网站导出为静态文件，我们将它用于托管网站。</p>
<pre><code>yarn build
# 或者
npm run build
</code></pre>
<p>一旦完成, 我们将查看 <code>out</code>目录，看到我们新网站的所有文件。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/nextjs-build-export-output.jpg" alt="nextjs-build-export-output" width="600" height="400" loading="lazy"></p>
<p>Next.js 的静态输出</p>
<p>最后，我们要把它推送到GitHub上。</p>
<p>在你的GitHub账号中 <a href="https://docs.github.com/en/free-pro-team@latest/github/getting-started-with-github/create-a-repo">创建一个新的仓库</a>。然后会提供如何 <a href="https://docs.github.com/en/free-pro-team@latest/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line">添加现有项目</a>到该仓库的说明。</p>
<p>一旦把你的项目推送到GitHub上，我们就做好了建立我的新网站项目的准备。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/project-on-github.jpg" alt="project-on-github" width="600" height="400" loading="lazy"></p>
<p>GitHub中的新repo</p>
<p>有下面的提交内容</p>
<ul>
<li><a href="https://github.com/colbyfayock/my-static-website/commit/ca9e4bca3c37fbd8553b0b183890c32836c35296">添加初始Next.js项目</a> 通过 <a href="https://nextjs.org/docs/api-reference/create-next-app">创建Next引用</a></li>
<li><a href="https://github.com/colbyfayock/my-static-website/commit/7907f4a0fac5f0aed2922202c5f0070dfc055f83">配置Next.js导出</a></li>
</ul>
<h2 id="1s3nextjs">第1步: 手动创建新的S3桶，并将Next.js项目部署到上面。</h2>
<p>要开始使用我们的新S3桶，首先登录你的AWS账号，并进入到S3服务。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/aws-s3-console.jpg" alt="aws-s3-console" width="600" height="400" loading="lazy"></p>
<p>发现没有桶</p>
<p>我们要创建一个新桶，使用我们选择的名字命名，用于我们网址托管的S3，我们还要配置我们的S3桶，使其能够托管一个网站。</p>
<p>_注意: 本教程步会指导你如何在S3上托管网站，但是你可以查看我的另一个教程，该教程将一步步地 <a href="https://www.freecodecamp.org/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/">指导你在S3上托管网站</a>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/s3-bucket-website-hosting.jpg" alt="s3-bucket-website-hosting" width="600" height="400" loading="lazy"></p>
<p>静态网站在AWS S3上托管</p>
<p>当我们把S3桶配置成一个网站，我们就可以回到Next.js项目文件夹，运行我们的构建命令，然后把<code>out</code>文件夹中的所有文件上传到我们的新建的S3桶。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/website-files-in-s3.jpg" alt="website-files-in-s3" width="600" height="400" loading="lazy"></p>
<p>S3桶上的静态应用</p>
<p>当这些文件被上传，并且我们已经为网站托管配置了S3桶，我们现在应该能看到我们的项目在网络上线运行！</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/nextjs-s3-website.jpg" alt="nextjs-s3-website" width="600" height="400" loading="lazy"></p>
<p>AWS S3托管Next.js应用程序</p>
<h2 id="2githubactionnextjs">第2步: 创建一个新的GitHub Action工作流来自动构建一个Next.js项目</h2>
<p>首先，我们需要创建一个新的工作流程(workflow)。</p>
<p>如果你熟悉GitHub Actions，你可以手动创建一个，单我们将通过用户界面快速创建一个。</p>
<p>进入GitHub的仓库中的<code>Action</code>标签，点击<code>set up a workflow yourself</code>,来自行设置工作流。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-actions-new-workflow.jpg" alt="github-actions-new-workflow" width="600" height="400" loading="lazy"></p>
<p>新的GitHub Action工作流</p>
<p>GitHub提供了一个模板，我们可以在工作流程中使用，不过我们要做一些修改。</p>
<p>让我们做以下工作。</p>
<ul>
<li>可选: 将文件重名为deploy.yml</li>
<li>可选: 将workflow重名为CD (因为它与CI不同)</li>
<li>可选: 删除所有的注释，使其更容易阅读</li>
<li>删除<code>on</code> 属性中的<code>pull_request</code></li>
<li>删除所有的 <code>steps</code> 除了<code>uses: actions/checkout@v2</code></li>
</ul>
<p>因此，在这一点上，我们应该剩下的是:</p>
<pre><code class="language-yaml">name: CD

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
</code></pre>
<p>仅仅这段代码会触发一个流程，会启动一个新的Ubuntu实例，并在GitHub上有新的改动推送到主分支后，拉取代码到Ubuntu上。</p>
<p>接下来， 当我们获取我们的代码后，我们要构建它。然后将输出文件同步到S3。</p>
<p>这一步将基于你的项目使用yarn还是npm有所不同。</p>
<p>如果你使用yarn，在 <code>steps</code>定义下，添加以下内容。</p>
<pre><code class="language-yaml">- uses: actions/setup-node@v1
  with:
    node-version: 12
- run: npm install -g yarn
- run: yarn install --frozen-lockfile
- run: yarn build
</code></pre>
<p>如果是使用npm，添加以下内容:</p>
<pre><code class="language-yaml">- uses: actions/setup-node@v1
  with:
    node-version: 12
- run: npm ci
- run: npm run build
</code></pre>
<p>在这两个步骤之间，我们要做的是:</p>
<ul>
<li>设置 node: 这是为了我们能够使用npm 和node 来安装和运行的脚本</li>
<li>安装Yarn (仅对使用Yarn): 如果我们使用Yarn，我们将为其安装全局依赖，以便我们使用它</li>
<li>安装依赖: 我们安装我们的依赖，我们使用一个特定命令，确保我们使用<code>lock</code>文件，以避免任何意外的软件包升级</li>
<li>构建: 最后, 我们运行我们的构建命令，将我们的Next.js项目编译到<code>out</code>目录中。</li>
</ul>
<p>现在我们可以将该该文件直接提交到我们的<code>main</code>分支，这触发我们的workflow的运行，我们可以子啊<code>Actions</code>标签里看到。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-action-run-workflow.jpg" alt="github-action-run-workflow" width="600" height="400" loading="lazy"></p>
<p>在GitHub Actions中新的workflow</p>
<p>为了看到它的运行状态，我们进入运行的<code>workflow</code>，选择我们的<code>workflow</code>，看到所有我们的项目包含的步骤。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-action-successful-build.jpg" alt="github-action-successful-build" width="600" height="400" loading="lazy"></p>
<p>GitHub Action成功构建日志</p>
<p><a href="https://github.com/colbyfayock/my-static-website/commit/59e0a5158d6afbf54793d826d05455f5205c98fb">随着提交!</a></p>
<h2 id="3githubactions3">第3步: 配置一个GitHub Action，将静态网站部署到S3上</h2>
<p>现在我们正在自动构建我们的项目，我们想在S3中自动更新我们的网站。</p>
<p>为了做到这一点，我们将使用GitHub Action <a href="https://github.com/aws-actions/configure-aws-credentials">aws-actions/configure-aws-credentials(配置aws凭证)</a> 和 the AWS CLI(AWS提供的命令行)。</p>
<p>我们使用GitHub Action 将接收我们的AWS凭证和配置，并在workflow的生命周期内使用。</p>
<p>目前，GitHub Action中的Ubuntu实例允许使用AWS CLI，因为它包含在其中。因此，我们将能够在workflow中使用CLI命令。</p>
<p>另外，我们也可以使用<a href="https://github.com/jakejarvis/s3-sync-action">S3 Sync action</a>。但是通过使用AWS CLI，我们可以获得更多的灵活性来定制我们的设置，我们可以使用它来获得额外的CLI命令，一般来说，熟悉AWS CLI也是不错的。</p>
<p>为了开始，让我们在workflow添加以下片段作为附加步骤。</p>
<pre><code class="language-yaml">- uses: aws-actions/configure-aws-credentials@v1
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1
</code></pre>
<p>上面要做的是使用AWS凭证配置action，根据我们的设置来设置我们的AWS的Access Key和Secret Key还有region(区域)。</p>
<p>AWS Region可以自定义为你通常使用的AWS账号的任何区域，我在美国东北部，所以我设置为<code>us-east-1</code>。</p>
<p>Access Key和Secret Key是你需要你的AWS账号生成的凭证。我们的代码设置方式是，我们将这些值存储在GitHub Secrets里，要防止这些密钥被泄。当action运行时，GitHub会将这些值改为星星(<code>***</code>)，这样人们就无法访问这些密钥。</p>
<p>为了设置这些secrets,我们首先要在AWS生成 <code>Access Keys</code>。</p>
<p>进入了AWS控制台。在用户菜单下，选择 <strong>My Security Credentials</strong>，然后选择 <strong>Create access key</strong>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/aws-console-create-access-key.jpg" alt="aws-console-create-access-key" width="600" height="400" loading="lazy"></p>
<p>在AWS创建一个 <code>Access Key</code></p>
<p>这会生成两个值  <strong>Access key ID</strong> 和<strong>Secret access key</strong>。必须保存好这些值，因为你将无法再次访问<code>Secret key ID </code>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/aws-secret-access-keys.jpg" alt="aws-secret-access-keys" width="600" height="400" loading="lazy"></p>
<p>在AWS中寻找 <code>Secret Key</code> 和 <code>Access Key</code></p>
<p><em>注意: 记住不要再你的代码中包含<code>Access Key</code>和<code>Secret Key</code>。这可能导致有人破坏你的AWS凭证。</em></p>
<p>下一步，在GitHub repo中，进入到 Settings -&gt; Secrets，然后选择 <code>New secret</code>。</p>
<p>在这里，我们要使用AWS keys添加到下面的secrets:</p>
<ul>
<li>AWS_ACCESS_KEY_ID: your AWS Access key ID</li>
<li>AWS_SECRET_ACCESS_KEY: your AWS Secret key</li>
</ul>
<p>当保存下来，你就应该记住这两个新的<code>secrets</code>。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-secrets-access-keys.jpg" alt="github-secrets-access-keys" width="600" height="400" loading="lazy"></p>
<p>在GitHub中创建<code>Secrets</code></p>
<p>现在我们已经配置好了我们的凭证，我们应该为运行命令，将我们的项目同步到S3，做好准备。</p>
<p>在Github Action，添加以下步骤:</p>
<pre><code class="language-yaml">- run: aws s3 sync ./out s3://[bucket-name]
</code></pre>
<p><em>注意: 请确保<code>[bucket-name]</code> 替换为你的S3桶的名称。</em></p>
<p>这个命令会触发与我们的S3桶的同步(sync)，使用<code>out</code>目录的文件，也就是我们项目构建的地方。</p>
<p>现在，如果我们提交我们的修改，我们可以看到，一旦提交到<code>main</code>分支，我们的actions会自动触发，我们构建我们的项目并同步到S3！</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-action-sync-s3-bucket.jpg" alt="github-action-sync-s3-bucket" width="600" height="400" loading="lazy"></p>
<p>成功通过GitHub Action workflow 同步到AWS S3</p>
<p><em>注意: 请确保在设置这个action之前，你已经将S3桶配置为网站托管(包括解除S3桶权限) --否则这个action可能失败。</em></p>
<p>在这一点上，我们的项目可能看起来是一样的，因为我们对代码进行任何修改。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/nextjs-s3-website.jpg" alt="nextjs-s3-website" width="600" height="400" loading="lazy"></p>
<p>AWS S3的Next.js应用程序</p>
<p>但如果你做了一个代码修改，比如在<code>pages/index.js</code>中改变主页的标题，并提交该修改:</p>
<pre><code class="language-jsx">&lt;h1 className={styles.title}&gt;
  Colby's &lt;a href="https://nextjs.org"&gt;Next.js!&lt;/a&gt; Site
&lt;/h1&gt;
</code></pre>
<p>我们可以看到，我们的修改触发了workflow的启动:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/github-action-commit-workflow.jpg" alt="github-action-commit-workflow" width="600" height="400" loading="lazy"></p>
<p>新的GitHub Action workflow的触发来自代码改变</p>
<p>一旦我们的workflow完成，我们可以看到我们的内容已经在我们的网站上自动更新。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/updated-nextjs-site-title.jpg" alt="updated-nextjs-site-title" width="600" height="400" loading="lazy"></p>
<p>AWS S3托管的应用程序，代码已经更新</p>
<p>随着内容的提交</p>
<ul>
<li><a href="https://github.com/colbyfayock/my-static-website/commit/f891412b827aca4b06e9bf3de8e4e5b4c5704fc8">添加ASW的配置和S3 sync命令</a></li>
<li><a href="https://github.com/colbyfayock/my-static-website/commit/bb9b981416645e35c6d3442e02d6b61f2ba032d2">测试workflow的标题的更新</a></li>
</ul>
<h2 id="">我们还能做什么?</h2>
<h3 id="cloudfront">设置CloudFront</h3>
<p>这个篇文章的目的不是要经历AWS配置网站的整个过程，但是你在S3上运行网站服务，你可能在之前考虑过CloudFront。</p>
<p>你可以查看以下<a href="https://www.freecodecamp.org/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/">我的另一个指南</a>，它指导你如何设置CloudFront，以及如何在S3中创建网站的手把手指南。</p>
<h3 id="cloudfront">CloudFront的缓存失效</h3>
<p>如果你的S3网站在CloudFront后面，有可能你会确保CloudFront没有缓存新的变化。</p>
<p>通过AWS CLI，我们也可以触发CloudFront的缓存失效，以确保它正在抓取最新的变化。</p>
<p><a href="https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-invalidation.html">请看这里的文档</a>学习更多的知识.</p>
<h3 id="pullrequest">pull request部署</h3>
<p>如果你不断地在pull request中的网站修改，有时候更容易看到网站的修改。</p>
<p>你可以设置一个新的workflow，只在pull request上运行，workflow可以根据分支或者环境动态创建一个新的桶，并在pull request上添加一个带有该URL的comment。</p>
<p>你也许能找到一个GitHub Action 作为你管理你pull request上带的comments,你可以查询<a href="https://docs.github.com/en/free-pro-team@latest/rest/reference/actions">GitHub Actions文档</a>.</p>
<p><a href="https://twitter.com/colbyfayock"><img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="关注我，了解更多的Javascript、UX和其他有趣的事情!" width="600" height="400" loading="lazy"></a></p>
<ul>
<li><a href="https://twitter.com/colbyfayock">🐦 在推特上关注我</a></li>
<li><a href="https://youtube.com/colbyfayock">🎥 在油管上订阅我</a></li>
<li><a href="https://www.colbyfayock.com/newsletter/">✉️ 订阅我的Newsletter</a></li>
<li><a href="https://github.com/sponsors/colbyfayock">💝 赞助我</a></li>
</ul>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/how-to-use-github-actions-to-deploy-a-next-js-website-to-aws-s3/">How to Use Github Actions to Deploy a Next.js Website to AWS S3</a>，作者：<a href="https://www.freecodecamp.org/news/author/colbyfayock/">Colby Fayock</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 用 Jenkins 和 Github Actions 实现前端项目自动化部署 ]]>
                </title>
                <description>
                    <![CDATA[ 本教程主要讲解怎么使用 Jenkins 和 Github Actions 部署前端项目。  1. 第一部分是使用 Gitea 配置局域网 git 服务器，再使用 Jenkins 将 Gitea 下的项目部署到局域网服务器。  2. 第二部分是使用 Github Actions 将 Github 项目部署到 Github Page 和阿里云。 阅读本教程并不需要你提前了解 Jenkins 和 Github Actions 的知识，只要按照本教程的指引，就能够实现自动化部署项目。 PS：本人所用电脑操作系统为 windows，即以下所有的操作均在 windows 下运行。其他操作系统的配置大同小异，不会有太大差别。 Gitea + Jenkins 自动构建前端项目并部署到服务器 Gitea 用于构建 Git 局域网服务器，Jenkins 是 CI/CD 工具，用于部署前端项目。 配置 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/automated-deployment-of-front-end-projects-with-jenkins-and-github-actions/</link>
                <guid isPermaLink="false">61650bdb21a1350622df53c5</guid>
                
                    <category>
                        <![CDATA[ Jenkins ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 前端开发 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ woai3c ]]>
                </dc:creator>
                <pubDate>Tue, 12 Oct 2021 04:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/10/article-cover-pic.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>本教程主要讲解怎么使用 Jenkins 和 Github Actions 部署前端项目。</p><ol><li>第一部分是使用 Gitea 配置局域网 git 服务器，再使用 Jenkins 将 Gitea 下的项目部署到局域网服务器。</li><li>第二部分是使用 Github Actions 将 Github 项目部署到 Github Page 和阿里云。</li></ol><p>阅读本教程并不需要你提前了解 Jenkins 和 Github Actions 的知识，只要按照本教程的指引，就能够实现自动化部署项目。</p><p>PS：本人所用电脑操作系统为 windows，即以下所有的操作均在 windows 下运行。其他操作系统的配置大同小异，不会有太大差别。</p><h2 id="gitea-jenkins-">Gitea + Jenkins 自动构建前端项目并部署到服务器</h2><p>Gitea 用于构建 Git 局域网服务器，Jenkins 是 CI/CD 工具，用于部署前端项目。</p><h3 id="-gitea">配置 Gitea</h3><ol><li>下载 <a href="https://link.juejin.cn?target=https%3A%2F%2Fdl.gitea.io%2Fgitea" rel="nofollow noopener noreferrer">Gitea</a>，选择一个喜欢的版本，例如 1.13，选择 <code>gitea-1.13-windows-4.0-amd64.exe</code> 下载。</li><li>下载完后，新建一个目录（例如 gitea），将下载的 Gitea 软件放到该目录下，双击运行。</li><li>打开 <code>localhost:3000</code> 就能看到 Gitea 已经运行在你的电脑上了。</li><li>点击注册，第一次会弹出一个初始配置页面，数据库选择 <code>SQLite3</code>。另外把 <code>localhost</code> 改成你电脑的局域网地址，例如我的电脑 IP 为 <code>192.168.0.118</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9ff66f2a9ff4436bb46216f9777b1ca8~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="977" height="930" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5685667856ca40ed8f534bc6e344e4ca~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="568" height="542" loading="lazy"></figure><ol><li>填完信息后，点击立即安装，等待一会，即可完成配置。</li><li>继续点击注册用户，第一个注册的用户将会成会管理员。</li><li>打开 Gitea 的安装目录，找到 <code>custom\conf\app.ini</code>，在里面加上一行代码 <code>START_SSH_SERVER = true</code>。这时就可以使用 ssh 进行 push 操作了。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/89726b25bd634c17973e8eeb1d7944a3~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="661" height="319" loading="lazy"></figure><p>8. 如果使用 http 的方式无法克隆项目，请取消 git 代理。</p><pre><code class="language-git">git config --global --unset http.proxy
git config --global --unset https.proxy
复制代码</code></pre><h3 id="-jenkins">配置 Jenkins</h3><ol><li>需要提前安装 JDK，JDK 安装教程网上很多，请自行搜索。</li><li>打开 <a href="https://www.jenkins.io/zh/download/">Jenkins</a> 下载页面。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f34a061c138a445ca7e35105e68e0d0d~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="499" height="714" loading="lazy"></figure><ol><li>安装过程中遇到 <code>Logon Type</code> 时，选择第一个。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/79c134e86d404d37af1df9ec71c5cf2c~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="373" height="161" loading="lazy"></figure><ol><li>端口默认为 8080，这里我填的是 8000。安装完会自动打开 <code>http://localhost:8000</code> 网站，这时需要等待一会，进行初始化。</li><li>按照提示找到对应的文件（直接复制路径在我的电脑中打开），其中有管理员密码。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/68c2491dc87f40b7a2ed2f3a7c64c624~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>6. 安装插件，选择第一个。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/de722d8c3568470f8401f696da106848~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>创建管理员用户，点击完成并保存，然后一路下一步。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bdbb765c02c14e1f8e839d8abd37f321~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>8. 配置完成后自动进入首页，这时点击 <code>Manage Jenkins</code> -&gt; <code>Manage plugins</code> 安装插件。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a9b6d41606164e1f80047d0327c05306~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>9. 点击 <code>可选插件</code>，输入 nodejs，搜索插件，然后安装。 10. 安装完成后回到首页，点击 <code>Manage Jenkins</code> -&gt; <code>Global Tool Configuration</code> 配置 nodejs。如果你的电脑是 win7 的话，nodejs 版本最好不要太高，选择 v12 左右的就行。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dc81f6a6588e4ddf9602a400c884bea7~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><h3 id="-">创建静态服务器</h3><ol><li>建立一个空目录，在里面执行 <code>npm init -y</code>，初始化项目。</li><li>执行 <code>npm i express</code> 下载 express。</li><li>然后建立一个 <code>server.js</code> 文件，代码如下：</li></ol><pre><code class="language-js">const express = require('express')
const app = express()
const port = 8080

app.use(express.static('dist'))

app.listen(port, () =&gt; {
    console.log(`Example app listening at http://localhost:${port}`)
})
复制代码</code></pre><p>它将当前目录下的 <code>dist</code> 文件夹设为静态服务器资源目录，然后执行 <code>node server.js</code> 启动服务器。</p><p>由于现在没有 <code>dist</code> 文件夹，所以访问网站是空页面。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/de3ae14bee7040709ef8c9e80f68e1c3~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>不过不要着急，一会就能看到内容了。</p><h3 id="--1">自动构建 + 部署到服务器</h3><ol><li>下载 Jenkins 提供的 demo 项目 <a href="https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fjenkins-docs%2Fbuilding-a-multibranch-pipeline-project" rel="nofollow noopener noreferrer">building-a-multibranch-pipeline-project</a>，然后在你的 Gitea 新建一个仓库，把内容克隆进去，并提交到 Gitea 服务器。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c452046cb4d446728b2a44eb432b4850~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>2. 打开 Jenkins 首页，点击 <code>新建 Item</code> 创建项目。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5427e0513e0e4ce783bf6abd32730c54~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>3. 选择<code>源码管理</code>，输入你的 Gitea 上的仓库地址。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a95f9287739445c28eb0d95ac5e37494~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>你也可以尝试一下定时构建，下面这个代码表示每 5 分钟构建一次。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/06f2030ea15044c6980270ce6e58335b~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>选择你的构建环境，这里选择刚才配置的 nodejs。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/352fcfd12ee141c68557b6b42ffe640a~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>6. 点击增加构建步骤，windows 要选 <code>execute windows batch command</code>，linux 要选 <code>execute shell</code>。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e6ee4b6dd10431f9ac16318766661e9~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="2e6ee4b6dd10431f9ac16318766661e9~tplv-k3u1fbpfcp-watermark" width="600" height="400" loading="lazy"></figure><ol><li>输入 <code>npm i &amp;&amp; npm run build &amp;&amp; xcopy .\build\* G:\node-server\dist\ /s/e/y</code>，这行命令的作用是安装依赖，构建项目，并将构建后的静态资源复制到指定目录 <code>G:\node-server\dist\ </code>。这个目录是静态服务器资源目录。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/01af43df1d80491e9dd8a489ef57c2f4~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>8. 保存后，返回首页。点击项目旁边的小三角，选择 <code>build now</code>。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5cea232eb18e4725b0c57678561bd5d6~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>9. 开始构建项目，我们可以点击项目查看构建过程。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/faad4f4509964a849c1796d69874ef32~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>10. 构建成功，打开 <code>http://localhost:8080/</code> 看一下结果。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c6ef1268d3c7409cabd96e33cf944c93~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f46e5659f8b04211b7497852209960da~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>11. 由于刚才设置了每 5 分钟构建一次，我们可以改变一下网站的内容，然后什么都不做，等待一会再打开网站看看。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fb5de42e30e44348b0bcc762f3624f02~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>12. 把修改的内容提交到 Gitea 服务器，稍等一会。打开网站，发现内容已经发生了变化。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/50b88365d90549eb81f0a86954ff62d5~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><h3 id="-pipeline-">使用 pipeline 构建项目</h3><p>使用流水线构建项目可以结合 Gitea 的 <code>webhook</code> 钩子，以便在执行 <code>git push</code> 的时候，自动构建项目。</p><ol><li>点击首页右上角的用户名，选择<code>设置</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5cd283b66d204fa6934509e1891d0f3d~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>添加 token，记得将 token 保存起来。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ead6e4c2346d4b5fa03601d1ff7d4824~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>打开 Jenkins 首页，点击 <code>新建 Item</code> 创建项目。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/51273f6a00384a3d9564b572f83695be~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>4. 点击<code>构建触发器</code>，选择<code>触发远程构建</code>，填入刚才创建的 token。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1c9bdcd8fd8549c4a21be519069f620f~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>5. 选择流水线，按照提示输入内容，然后点击<code>保存</code>。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b343b4750c0a4c959125a0832e3de5fe~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>6. 打开 Jenkins 安装目录下的 <code>jenkins.xml</code> 文件，找到 <code>&lt;arguments&gt;</code> 标签，在里面加上 <code>-Dhudson.security.csrf.GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION=true</code>。它的作用是关闭 <code>CSRF</code> 验证，不关的话，Gitea 的 <code>webhook</code> 会一直报 403 错误，无法使用。加好参数后，在该目录命令行下输入 <code>jenkins.exe restart</code> 重启 Jenkins。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c46ea1cc26664600a231615fa2ab0d5c~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>7. 回到首页，配置全局安全选项。勾上<code>匿名用户具有可读权限</code>，再保存。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6f2fdfa1709e460ab73fb4429c147ac4~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3eb4cf1f1637407abfe7ac811104a3df~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>打开你的 Gitea 仓库页面，选择<code>仓库设置</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d66a648f453d4393810a7bfaed1edd7c~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>点击<code>管理 web 钩子</code>，添加 web 钩子，钩子选项选择 <code>Gitea</code>。</li><li>目标 URL 按照 Jenkins 的提示输入内容。然后点击<code>添加 web 钩子</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f0f7c0356f1d4126936d77976873c69a~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/10831af1048c48a5918e2a1dfd05677b~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>11. 点击创建好的 web 钩子，拉到下方，点击测试推送。不出意外，应该能看到推送成功的消息，此时回到 Jenkins 首页，发现已经在构建项目了。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f2cbe9a5c87e44ed9d82359893cc8c2f~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>12. 由于没有配置 <code>Jenkinsfile</code> 文件，此时构建是不会成功的。所以接下来需要配置一下 <code>Jenkinsfile</code> 文件。将以下代码复制到你 Gitea 项目下的 <code>Jenkinsfile</code> 文件。jenkins 在构建时会自动读取文件的内容执行构建及部署操作。</p><pre><code>pipeline {
    agent any
    stages {
        stage('Build') {
            steps {  // window 使用 bat， linux 使用 sh
                bat 'npm i'
                bat 'npm run build'
            }
        }
        stage('Deploy') {
            steps {
                bat 'xcopy .\\build\\* D:\\node-server\\dist\\ /s/e/y' // 这里需要改成你的静态服务器资源目录
            }
        }
    }
}
复制代码</code></pre><ol><li>每当你的 Gitea 项目执行 <code>push</code> 操作时，Gitea 都会通过 <code>webhook</code> 发送一个 post 请求给 Jenkins，让它执行构建及部署操作。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5fc2ee216393438aa91fd26d0e8096db~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><h3 id="--2">小结</h3><p>如果你的操作系统是 Linux，可以在 Jenkins 打包完成后，使用 ssh 远程登录到阿里云，将打包后的文件复制到阿里云上的静态服务器上，这样就能实现阿里云自动部署了。具体怎么远程登录到阿里云，请看下文中的 《Github Actions 部署到阿里云》 一节。</p><h2 id="github-actions-">Github Actions 自动构建前端项目并部署到服务器</h2><p>如果你的项目是 Github 项目，那么使用 Github Actions 也许是更好的选择。</p><h3 id="-github-page">部署到 Github Page</h3><p>接下来看一下如何使用 Github Actions 部署到 Github Page。</p><p>在你需要部署到 Github Page 的项目下，建立一个 yml 文件，放在 <code>.github/workflow</code> 目录下。你可以命名为 <code>ci.yml</code>，它类似于 Jenkins 的 <code>Jenkinsfile</code> 文件，里面包含的是要自动执行的脚本代码。</p><p>这个 yml 文件的内容如下：</p><pre><code class="language-yml">name: Build and Deploy
on: # 监听 master 分支上的 push 事件
  push:
    branches:
      - master
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest # 构建环境使用 ubuntu
    steps:
      - name: Checkout
        uses: actions/checkout@v2.3.1  
        with:
          persist-credentials: false

      - name: Install and Build # 下载依赖 打包项目
        run: |
          npm install
          npm run build

      - name: Deploy # 将打包内容发布到 github page
        uses: JamesIves/github-pages-deploy-action@3.5.9 # 使用别人写好的 actions
        with:  # 自定义环境变量
          ACCESS_TOKEN: ${{ secrets.VUE_ADMIN_TEMPLATE }} # VUE_ADMIN_TEMPLATE 是我的 secret 名称，需要替换成你的
          BRANCH: master
          FOLDER: dist
          REPOSITORY_NAME: woai3c/woai3c.github.io # 这是我的 github page 仓库
          TARGET_FOLDER: github-actions-demo # 打包的文件将放到静态服务器 github-actions-demo 目录下

复制代码</code></pre><p>上面有一个 <code>ACCESS_TOKEN</code> 变量需要自己配置。</p><ol><li>打开 Github 网站，点击你右上角的头像，选择 <code>settings</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9bb947ff3c7243af92c62b221b472056~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>点击左下角的 <code>developer settings</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ea820b671dc64a24b2863c7dd1bdb093~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>在左侧边栏中，单击 <code>Personal access tokens（个人访问令牌）</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/50368065e962408cb53c938c4b509848~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>单击 <code>Generate new token（生成新令牌）</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ebc0215aa43d4bdba85a79d97beeeca3~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>输入名称并勾选 <code>repo</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b2aed7e43e6a423199c0a583355d03d5~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>拉到最下面，点击 <code>Generate token</code>，并将生成的 token 保存起来。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/314f733f0ead462ca497fd49531874ee~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><ol><li>打开你的 Github 项目，点击 <code>settings</code>。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5a7a9afc78d048049e9fcb7e2af40eec~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>点击 <code>secrets</code>-&gt;<code>new secret</code>。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5dc749949c5c4238863f12e2eae08805~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>创建一个密钥，名称随便填（中间用下划线隔开），内容填入刚才创建的 token。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f112c870534a4ed4878045f4fd4c39fd~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d714a34c74e5463cace0552442da3880~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>将上文代码中的 <code>ACCESS_TOKEN: ${{ secrets.VUE_ADMIN_TEMPLATE }}</code> 替换成刚才创建的 secret 名字，替换后代码如下 <code>ACCESS_TOKEN: ${{ secrets.TEST_A_B }}</code>。保存后，提交到 Github。</p><p>以后你的项目只要执行 <code>git push</code>，Github Actions 就会自动构建项目并发布到你的 Github Page 上。</p><p>Github Actions 的执行详情点击仓库中的 <code>Actions</code> 选项查看。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9afeda4174cd45b0814552c3a4eaa180~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/537454335ae947e18a9454988f7e57fc~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>具体详情可以参考一下我的 demo 项目 <strong><a href="https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fwoai3c%2Fgithub-actions-demo" rel="nofollow noopener noreferrer">github-actions-demo</a></strong>。</p><p>构建成功后，打开 Github Page 网站，可以发现内容已经发布成功。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/11313081f18b40b0a474be43de79ae1c~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><h3 id="github-actions--1">Github Actions 部署到阿里云</h3><h4 id="--3">初始化阿里云服务器</h4><ol><li>购买阿里云服务器，选择操作系统，我选的 ubuntu</li><li>在云服务器管理控制台选择实例-&gt;更多-&gt;密钥-&gt;重置实例密码（一会登陆用）</li><li>选择远程连接-&gt;VNC，会弹出一个密码，记住它，以后远程连接要用（ctrl + alt + f1~f6 切换终端，例如 ctrl + alt + f1 是第一个终端）</li><li>进入后是一个命令行 输入 <code>root</code>（默认用户名），密码为你刚才重置的实例密码</li><li>登陆成功， 更新安装源 <code>sudo apt-get update &amp;&amp; sudo apt-get upgrade -y</code></li><li>安装 npm <code>sudo apt-get install npm</code></li><li>安装 npm 管理包 <code>sudo npm install -g n</code></li><li>安装 node 最新稳定版 <code>sudo n stable</code></li></ol><h4 id="--4">创建一个静态服务器</h4><pre><code class="language-js">mkdir node-server // 创建 node-server 文件夹
cd node-server // 进入 node-server 文件夹
npm init -y // 初始化项目
npm i express
touch server.js // 创建 server.js 文件
vim server.js // 编辑 server.js 文件
复制代码</code></pre><p>将以下代码输入进去（用 vim 进入文件后按 i 进行编辑，保存时按 esc 然后输入 :wq，再按 enter），更多使用方法请自行搜索。</p><pre><code class="language-js">const express = require('express')
const app = express()
const port = 3388 // 填入自己的阿里云映射端口，在网络安全组配置。

app.use(express.static('dist'))

app.listen(port, '0.0.0.0', () =&gt; {
    console.log(`listening`)
})
复制代码</code></pre><p>执行 <code>node server.js</code> 开始监听，由于暂时没有 <code>dist</code> 目录，先不要着急。</p><p>注意，监听 IP 必须为 <code>0.0.0.0</code> ，详情请看<a href="https://www.alibabacloud.com/help/zh/doc-detail/50775.htm">部署 Node.js 项目注意事项</a>。</p><p>阿里云入端口要在网络安全组中查看与配置。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aabf84961fa146ed8345785b069788bd~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><h4 id="--5">创建阿里云密钥对</h4><p>请参考<a href="https://www.alibabacloud.com/help/zh/doc-detail/51793.htm">创建 SSH 密钥对</a>和<a href="https://www.alibabacloud.com/help/zh/doc-detail/51796.htm?spm=a2c63.p38356.879954.9.cf992580IYf2O7#concept-zzt-nl1-ydb">绑定 SSH 密钥对</a> ，将你的 ECS 服务器实例和密钥绑定，然后将私钥保存到你的电脑（例如保存在 ecs.pem 文件）。</p><p>打开你要部署到阿里云的 Github 项目，点击 setting-&gt;secrets。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8c335a8b83c945428221ff8b064a2bd8~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>点击 new secret</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bbb7d4958522458fb3238388044710df~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>secret 名称为 <code>SERVER_SSH_KEY</code>，并将刚才的阿里云密钥填入内容。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ac2bf48aae7c46bdb1e641f832db57c6~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>点击 add secret 完成。</p><p>在你项目下建立 <code>.github\workflows\ci.yml</code> 文件，填入以下内容：</p><pre><code class="language-yml">name: Build app and deploy to aliyun
on:
  #监听push操作
  push:
    branches:
      # master分支，你也可以改成其他分支
      - master
jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Install Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '12.16.2'
    - name: Install npm dependencies
      run: npm install
    - name: Run build task
      run: npm run build
    - name: Deploy to Server
      uses: easingthemes/ssh-deploy@v2.1.5
      env:
          SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
          ARGS: '-rltgoDzvO --delete'
          SOURCE: dist # 这是要复制到阿里云静态服务器的文件夹名称
          REMOTE_HOST: '118.190.217.8' # 你的阿里云公网地址
          REMOTE_USER: root # 阿里云登录后默认为 root 用户，并且所在文件夹为 root
          TARGET: /root/node-server # 打包后的 dist 文件夹将放在 /root/node-server
复制代码</code></pre><p>保存，推送到 Github 上。</p><p>以后只要你的项目执行 <code>git push</code> 操作，就会自动执行 <code>ci.yml</code> 定义的脚本，将打包文件放到你的阿里云静态服务器上。</p><p>这个 Actions 主要做了两件事：</p><ol><li>克隆你的项目，下载依赖，打包。</li><li>用你的阿里云私钥以 SSH 的方式登录到阿里云，把打包的文件上传（使用 rsync）到阿里云指定的文件夹中。</li></ol><p>如果还是不懂，建议看一下我的 <a href="https://github.com/woai3c/github-actions-aliyun-demo">demo</a>。</p><h3 id="ci-yml-"><code>ci.yml</code> 配置文件讲解</h3><ol><li><code>name</code>，表示这个工作流程（workflow）的名称。</li><li><code>on</code>，表示监听的意思，后面可以加上各种事件，例如 <code>push</code> 事件。</li></ol><p>下面这段代码表示要监听 <code>master</code> 分支的 <code>push</code> 事件。当 Github Actions 监听到 <code>push</code> 事件发生时，它就会执行下面 <code>jobs</code> 定义的一系列操作。</p><pre><code class="language-yml">name: Build app and deploy to aliyun
on:
  #监听push操作
  push:
    branches:
      # master分支，你也可以改成其他分支
      - master
jobs:
...
复制代码</code></pre><ol><li><code>jobs</code>，看字面意思就是一系列的作业，你可以在 <code>jobs</code> 字段下面定义很多作业，例如 <code>job1</code>、<code>job2</code> 等等，并且它们是并行执行的。</li></ol><pre><code class="language-yml">jobs:
  job1:
  	...
  job2:
  	...
  job3:
	...
复制代码</code></pre><p>回头看一下 <code>ci.yml</code> 文件，它只有一个作业，即 <code>build</code>，作业的名称是自己定义的，你叫 <code>good</code> 也可以。</p><ol><li><code>runs-on</code>，表示你这个工作流程要运行在什么操作系统上，<code>ci.yml</code> 文件定义的是最新稳定版的 <code>ubuntu</code>。除了 ubuntu，它还可以选择 Mac 或 Windows。</li></ol><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aa286b043d2f455d81c1ac034387481c~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="aa286b043d2f455d81c1ac034387481c~tplv-k3u1fbpfcp-watermark" width="600" height="400" loading="lazy"></figure><ol><li><code>steps</code>，看字面意思就是一系列的步骤，也就是说这个作业由一系列的步骤完成。例如先执行 <code>step1</code>，再执行 <code>step2</code>...</li></ol><h4 id="setps-"><code>setps</code> 步骤讲解</h4><p><code>setps</code> 其实是一个数组，在 YAML 语法中，以 <code>-</code> 开始就是一个数组项。例如 <code>['a', 'b', 'c']</code> 用 YAML 语法表示为：</p><pre><code class="language-yml">- a
- b
- c
复制代码</code></pre><p>所以 <code>setps</code> 就是一个步骤数组，从上到下开始执行。从 <code>ci.yml</code> 文件来看，每一个小步骤都有几个相关选项：</p><ol><li><code>name</code>，小步骤的名称。</li><li><code>uses</code>，小步骤使用的 actions 库名称或路径，Github Actions 允许你使用别人写好的 Actions 库。</li><li><code>run</code>，小步骤要执行的 <code>shell</code> 命令。</li><li><code>env</code>，设置与小步骤相关的环境变量。</li><li><code>with</code>，提供参数。</li></ol><figure class="kg-card kg-image-card"><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/71864f3ffec24798a7ccb1ca6eb3b1cf~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="71864f3ffec24798a7ccb1ca6eb3b1cf~tplv-k3u1fbpfcp-watermark" width="600" height="400" loading="lazy"></figure><p>综上所述，<code>ci.yml</code> 文件中的 <code>setps</code> 就很好理解了，下面从头到尾解释一边：</p><pre><code class="language-yml">    steps:
    - uses: actions/checkout@v1
    - name: Install Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '12.16.2'
    - name: Install npm dependencies
      run: npm install
    - name: Run build task
      run: npm run build
    - name: Deploy to Server
      uses: easingthemes/ssh-deploy@v2.1.5
      env:
          SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
          ARGS: '-rltgoDzvO --delete'
          SOURCE: dist # 这是要复制到阿里云静态服务器的文件夹名称
          REMOTE_HOST: '118.190.217.8' # 你的阿里云公网地址
          REMOTE_USER: root # 阿里云登录后默认为 root 用户，并且所在文件夹为 root
          TARGET: /root/node-server # 打包后的 dist 文件夹将放在 /root/node-server
复制代码</code></pre><ol><li>使用 <code>actions/checkout@v1</code> 库克隆代码到 <code>ubuntu</code> 上。</li><li>使用 <code>actions/setup-node@v1</code> 库安装 nodejs，<code>with</code> 提供了一个参数 <code>node-version</code> 表示要安装的 nodejs 版本。</li><li>在 <code>ubuntu</code> 的 <code>shell</code> 上执行 <code>npm install</code> 下载依赖。</li><li>执行 <code>npm run build</code> 打包项目。</li><li>使用 <code>easingthemes/ssh-deploy@v2.1.5</code> 库，这个库的作用就是用 <code>SSH</code> 的方式远程登录到阿里云服务器，将打包好的文件夹复制到阿里云指定的目录上。</li></ol><p>从 <code>env</code> 上可以看到，这个 actions 库要求我们提供几个环境变量：</p><ol><li><code>SSH_PRIVATE_KEY</code>: 阿里云密钥对中的私钥（需要你提前写在 github secrets 上），</li><li><code>ARGS: '-rltgoDzvO --delete'</code>，没仔细研究，我猜是复制完文件就删除掉。</li><li><code>SOURCE</code>：打包后的文件夹名称</li><li><code>REMOTE_HOST</code>: 阿里云公网 IP 地址</li><li><code>REMOTE_USER</code>: 阿里云服务器的用户名</li><li><code>TARGET</code>: 你要拷贝到阿里云服务器指定目录的名称</li></ol><p>如果你想了解一下其他 actions 库的实现，可以直接复制 actions 库的名称去搜索引擎搜索一下，例如搜索 <code>actions/checkout</code> 的结果为：</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2e353600ce54f1e8b0517c13d596b9c~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="d2e353600ce54f1e8b0517c13d596b9c~tplv-k3u1fbpfcp-watermark" width="600" height="400" loading="lazy"></figure><p>都看到这了，给个赞再走吧。</p><h2 id="--6">参考资料</h2><ul><li><a href="https://www.jenkins.io/zh/doc/">Jenkins 用户手册</a></li><li><a href="https://docs.github.com/cn/actions">GitHub Actions 文档</a></li><li><a href="https://docs.github.com/cn/pages/getting-started-with-github-pages/about-github-pages">GitHub Pages 文档</a></li><li><a href="https://docs.gitea.io/zh-cn/">Gitea 文档</a></li><li><a href="https://docs.github.com/cn/actions/learn-github-actions/workflow-syntax-for-github-actions">GitHub 操作的工作流程语法</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何在 Netlify 或 GitHub Pages 上发布 HTML 网站 ]]>
                </title>
                <description>
                    <![CDATA[ 你已经完成了 HTML 网站的创建，并为自己的辛勤工作感到自豪。但是仍然缺少一件事：你不知道如何发布你的网站。 在本教程中，你将学习如何使用两个流行的平台——Netlify 和 GitHub 发布 HTML 网站。 在我们开始之前，请确保你有一个 GitHub 帐户 [https://github.com/]，因为你需要在 GitHub 上托管你的仓库（源代码）。没有它，你将无法按照本教程发布 HTML 网站。 如何在 Netlify 上发布网站 我们要探索的第一种方法是如何在 Netlify 上发布你的网站。 Netlify 是一个托管网站的平台。在 Netlify 上托管站点很容易，因为你不需要手动配置它——最重要的是，它是免费的。如果你还没有注册帐户，现在是注册的好时机。 以下是在 Netlify 上发布网站的步骤： 第 1 步：添加你的新站点 登录后，它会将你带到主页界面。点击 New site from git 按钮将你的新网站添加到 Netlify。 第 2 步：链接到你的 GitHub 当你单击 New site from git 按钮时，它会将你带到 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/publish-your-website-netlify-github/</link>
                <guid isPermaLink="false">615fe8df21a1350622df50c7</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Miya Liu ]]>
                </dc:creator>
                <pubDate>Tue, 12 Oct 2021 03:50:10 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/10/responsive-web-design.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>你已经完成了 HTML 网站的创建，并为自己的辛勤工作感到自豪。但是仍然缺少一件事：你不知道如何发布你的网站。</p><p>在本教程中，你将学习如何使用两个流行的平台——<strong>Netlify</strong> 和 <strong>GitHub</strong> 发布 HTML 网站。</p><p>在我们开始之前，请确保你有一个 <a href="https://github.com/">GitHub 帐户</a>，因为你需要在 GitHub 上托管你的仓库（源代码）。没有它，你将无法按照本教程发布 HTML 网站。</p><h2 id="-netlify-"><strong>如何在 Netlify 上发布网站</strong></h2><p>我们要探索的第一种方法是如何在 Netlify 上发布你的网站。</p><p>Netlify 是一个托管网站的平台。在 Netlify 上托管站点很容易，因为你不需要手动配置它——最重要的是，它是免费的。如果你还没有注册帐户，现在是注册的好时机。</p><p>以下是在 Netlify 上发布网站的步骤：</p><h3 id="-1-">第 1 步：添加你的新站点</h3><p>登录后，它会将你带到主页界面。点击 <strong>New site from git</strong> 按钮将你的新网站添加到 Netlify。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_1.png" class="kg-image" alt="netlify_1" width="600" height="400" loading="lazy"></figure><h3 id="-2-github">第 2 步：链接到你的 GitHub</h3><p>当你单击 <strong><strong>New site from git</strong></strong> 按钮时，它会将你带到 “Create a new site” 页面。确保在 GitHub 上推送你的仓库，以便 Netlify 可以链接到你的 GitHub 帐户。</p><p>单击 <strong>GitHub</strong> 按钮，如下面的屏幕截图所示：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_2.png" class="kg-image" alt="netlify_2" width="600" height="400" loading="lazy"></figure><h3 id="-3-netlify">第 3 步：授权 Netlify</h3><p>接下来，单击 <strong><strong>Authorize Netlify by Netlify</strong> </strong>按钮。需要权限，以便 Netlify 和 GitHub 可以连接。</p><h3 id="-4-">第 4 步：选择你的仓库</h3><p>授予 Netlify 权限后，你可以看到所有仓库的列表。选择要发布的网站。你可以通过向下滚动列表或使用搜索栏缩小列表来找到它。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_4.png" class="kg-image" alt="netlify_4" width="600" height="400" loading="lazy"></figure><h3 id="-5-">第 5 步：配置</h3><p>选择你的网站后，系统会提示你配置用于部署网站的设置。由于你的网站只是一个静态网站，因此这里没有什么可做的 只需单击 <strong><strong>Deploy site</strong></strong> 即可继续。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_5-1.png" class="kg-image" alt="netlify_5-1" width="600" height="400" loading="lazy"></figure><h3 id="-6-">第 6 步：发布你的网站</h3><p>你的网站现在可以发布了！ Netlify 会为你完成剩下的工作，完成整个过程只需要一分钟左右。</p><p>现在你完成了！ 你的新网站已发布，可以通过单击绿色链接进行查看。</p><p>现在，你的 URL 看起来是随机的，但你可以通过单击 <strong><strong>Site settings</strong></strong> 按钮，然后单击 <strong><strong>Change site name</strong></strong> 按钮来编辑它。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_6-1.png" class="kg-image" alt="netlify_6-1" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/netlify_7.png" class="kg-image" alt="netlify_7" width="600" height="400" loading="lazy"></figure><p>祝贺你发布了你的第一个新网站！ 现在我们将学习如何使用 GitHub 发布网站。</p><h2 id="-github-">如何在 GitHub 上发布网站</h2><p>我们将研究的第二种方法是使用 GitHub 发布你的站点。GitHub 是一个用于存储、跟踪和管理项目源代码的平台。你还可以在此处发布你的 HTML 网站——与 Netlify 一样，你可以在这里免费托管。</p><p>以下是在 GitHub 上发布网站的步骤。</p><blockquote>注意：如果你将仓库的可见性设置为公开，你只能在 GitHub 上发布你的网站。如果你想在私密的情况下部署网站，请将你的帐户升级到 Pro 或使用 Netlify 在那里托管。</blockquote><h3 id="-1--1">第 1 步：转到你网站的仓库</h3><p>登录后，转到左侧边栏上的仓库，然后选择要发布的仓库。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_1.png" class="kg-image" alt="Github_1" width="600" height="400" loading="lazy"></figure><h3 id="-2-">第 2 步：选择设置</h3><p>在你的仓库中，单击 <strong><strong>Settings</strong></strong> 链接，它将带你到仓库的设置页面。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_2.png" class="kg-image" alt="Github_2" width="600" height="400" loading="lazy"></figure><h3 id="-3-github-pages">第 3 步：转到 <strong>GitHub Pages</strong></h3><p>当你在仓库的设置中时，向下滚动一点，直到你在左侧边栏中看到 <strong><strong>Pages</strong></strong> 链接。单击它，它将带你进入 GitHub Pages。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_3.png" class="kg-image" alt="Github_3" width="600" height="400" loading="lazy"></figure><h3 id="-4--1">第 4 步：选择分支</h3><p>在源部分，单击下拉菜单，选择主分支，并保存。根据你的命名方式，它可能是 <strong>master</strong> 或 <strong>main</strong>。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_4.png" class="kg-image" alt="Github_4" width="600" height="400" loading="lazy"></figure><h3 id="-5--1">第 5 步：全部完成</h3><p>你已经完成了！ 你的网站将被发布，只需一分钟左右即可完成该过程。刷新页面，你将看到一个指向你新发布的网站的链接。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Github_5.png" class="kg-image" alt="Github_5" width="600" height="400" loading="lazy"></figure><h2 id="-"><strong>总结</strong></h2><p>希望本教程对你有所帮助。你已经学习了如何使用 Netlify 和 GitHub 发布 HTML 网站。现在，你可以继续向世界展示你优秀的作品了！</p><p>如果你想了解有关现代 Web 开发的更多信息，我邀请你加入我的<a href="https://js.coderslang.com/">全栈 JavaScript 课程</a>，或<a href="https://learn.coderslang.com/">在我的编程博客上阅读更多关于 JS、HTML 和 CSS 的文章</a>。</p><p>原文：<a href="https://www.freecodecamp.org/news/publish-your-website-netlify-github/">How to Publish an HTML Website on Netlify or GitHub Pages</a>，作者：<a href="https://www.freecodecamp.org/news/author/coderslang/">Vasyl Lagutin</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 Git 和 Git 工作流——实用指南 ]]>
                </title>
                <description>
                    <![CDATA[ 每个人都说你应该学习 Git——你确实应该——但是说实话：Git 有点难。 甚至在我近 10 年的软件开发生涯后，我还在学习 Git 的基本原理和如何更有效地使用 Git。 就在不久前我意识到我对一个已经使用了无数次的关键命令存在最基本的误解 [https://twitter.com/johnmosesman/status/1306255666718310401]。 就像编程的其他很多领域一样，我相信最好的学习方法就是“实践”。 只要开始使用这个工具，就会有成效——随着时间的推移，基本原理和边缘案例最终会得到解决的。 这正是我们在这个教程中要做的。我们会通过一些系列的实例来全面了解如何使用 Git 以及与队友协作。 在这个过程中，我们会使用简单的命令并且解释它们的基本概念，因为它们有用——但是只限于有助于理解的程度。 Git 的内容肯定比这里介绍的多得多，但这些都是你在长期使用过程中会学到东西。 我也不会使用树状图（像下面这个），因为它们会把我搞糊涂，而且作为一名软件开发者，我从来不需要像这样来理解 Git。 https://www.atlassian.com/g ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/practical-git-and-git-workflows/</link>
                <guid isPermaLink="false">612b55eb0cd9ee0623e15a4f</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Luxu Fan ]]>
                </dc:creator>
                <pubDate>Sat, 28 Aug 2021 09:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/08/60635fd39618b008528a9920.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>每个人都说你应该学习 Git——你确实应该——但是说实话：Git 有点难。</p>
<p>甚至在我近 10 年的软件开发生涯后，我还在学习 Git 的基本原理和如何更有效地使用 Git。</p>
<p>就在不久前我意识到我对一个已经使用了无数次的关键命令<a href="https://twitter.com/johnmosesman/status/1306255666718310401">存在最基本的误解</a>。</p>
<p>就像编程的其他很多领域一样，我相信最好的学习方法就是“实践”。</p>
<p>只要开始使用这个工具，就会有成效——随着时间的推移，基本原理和边缘案例最终会得到解决的。</p>
<p>这正是我们在这个教程中要做的。我们会通过一些系列的实例来全面了解如何使用 Git 以及与队友协作。</p>
<p>在这个过程中，我们会使用简单的命令并且解释它们的基本概念，因为它们有用——但是只限于有助于理解的程度。</p>
<p>Git 的内容肯定比这里介绍的多得多，但这些都是你在长期使用过程中会学到东西。</p>
<p>我也不会使用树状图（像下面这个），因为它们会把我搞糊涂，而且作为一名软件开发者，我从来不需要像这样来理解 Git。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/hero.svg" alt="hero" width="600" height="400" loading="lazy"></p>
<p><a href="https://www.atlassian.com/git/tutorials/using-branches/git-checkout">https://www.atlassian.com/git/tutorials/using-branches/git-checkout</a></p>
<p>这些是我们要涉及到的内容。不要被这份清单吓到，我们会一步一步的进行。</p>
<ul>
<li>安装 git 并创建一个 GitHub 帐户</li>
<li>如何在 GitHub 上创建一个新的仓库（repository）</li>
<li>克隆仓库</li>
<li>Git 分支（branch）</li>
<li>如何查看一个 Git 项目的状态</li>
<li>如何做出我们的第一个提交（commit）</li>
<li>如何将我们的第一个提交推送（push）到 GitHub</li>
<li>如何在 Git 中添加另一个提交</li>
<li>如何在 Git 中缓存修改（stage change）</li>
<li>如何查看 Git 差异（diff）</li>
<li>如何在 Git 中与他人协作</li>
<li>Git 中的功能分支（feature branch）</li>
<li>用于协作的 Git 工作流程</li>
<li>如何在 Git 中合并（merge）分支</li>
<li>拉取请求（pull request）工作流程</li>
<li>如何使本地保持同步</li>
<li>如何获取远端数据</li>
<li>如何修复 Git 中的合并冲突（merge conflict）</li>
<li>回顾：如何启动一个新功能的工作流程</li>
<li>总结</li>
</ul>
<p>说了这么多，我鼓励你在自己的机器上跟着例子一起进行——让我们开始吧！</p>
<h2 id="gitgithub">如何安装 Git 并创建一个 GitHub 账号</h2>
<p>首先，在开始前我们需要处理一些无聊的事情。</p>
<p>如果你已经安装了 Git，注册了 GitHub 账户（或者使用任何其他的服务商，像是 GitLab 或者 Bitbucket），并且已经设置了 SSH 密匙的话，可以跳过这个章节。</p>
<p>否则，你将首先需要 <a href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">安装 Git</a>。</p>
<p>其次，在本教程里我们会使用 GitHub，所以需要注册一个 <a href="https://github.com/join">GitHub 帐户</a>。</p>
<p>GitHub 账户注册完成后，你将会需要创建一个 SSH 密匙以便将你的代码从本地推送到 GitHub （在推送代码时这个密匙可以向 GitHub 证明你是“你”）。</p>
<p>这并不难——只要<a href="https://docs.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">遵循这里的步骤就可以了</a>。</p>
<h2 id="githubrepository">如何在 GitHub 里创建一个新的仓库（repository）</h2>
<p>下一步我们会在 GitHub 里创建一个新的仓库。</p>
<p>这很简单。只需要点击你的主页上的 “New” 仓库按钮。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/Screen-Shot-2021-03-31-at-7.30.33-PM.png" alt="Screen-Shot-2021-03-31-at-7.30.33-PM" width="600" height="400" loading="lazy"></p>
<p>创建一个新仓库</p>
<p>接下来，为仓库选择一个名字，以及你希望仓库是公开的还是私有的。你还可以选择添加一个 README 文件，然后点击 “Create repository”。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/Screen-Shot-2021-03-31-at-7.29.07-PM.png" alt="Screen-Shot-2021-03-31-at-7.29.07-PM" width="600" height="400" loading="lazy"></p>
<p>设置新仓库</p>
<p>我把我的仓库命名为 <a href="https://github.com/johnmosesman/practical-git-tutorial">practical-git-tutorial</a>。这个仓库里已经有了这个教程所有的完成步骤，你可以把它作为参考。</p>
<h2 id="git">如何克隆一个 Git 仓库</h2>
<p>首先，我们将“克隆”这个仓库。克隆一个仓库意味着从源头（在这里就是指 GitHub）上下载所有项目的代码和元数据。</p>
<p>我们使用 <code>git clone &lt;URL&gt;</code> 来克隆仓库。</p>
<p>我使用了我刚刚创建的仓库的 URL，但是你应该使用你自己仓库的 URL：</p>
<pre><code>$ git clone git@github.com:johnmosesman/practical-git-tutorial.git
Cloning into 'practical-git-tutorial'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.
</code></pre>
<blockquote>
<p><strong>注意：</strong> 在你终端运行的命令将以 <code>$</code> 作为前缀。</p>
</blockquote>
<p>我们很快会详细介绍 <code>git clone</code> 的作用，但现在只需知道它会下载项目并将其放在你当前工作目录的一个文件夹中。</p>
<p>接下来，让我们用 <code>cd</code> 切换到新的目录：</p>
<pre><code>$ cd practical-git-tutorial/
/practical-git-tutorial (main)$
</code></pre>
<p>我们已经切换到了这个文件夹（和其他文件夹一样），你的终端可能会在目录名旁边显示一些东西： <code>(main)</code> 。</p>
<h2 id="gitbranch">Git 分支（branch）</h2>
<p>这个 <code>(main)</code> 意味着我们现在位于一个叫做 <code>main</code> 的 <strong>分支</strong> 上。你可以把一个 Git 分支看作是项目 <em>在某一特定时间点</em> 的副本，可以独立于其他分支进行修改。</p>
<p>例如，如果我们用 Git 来跟踪一本书的写作，我们可能会有这样的分支：</p>
<ul>
<li><code>main</code> 分支</li>
<li><code>table-of-contents</code> 分支</li>
<li><code>chapter-1</code> 分支</li>
<li><code>chapter-2</code> 分支</li>
<li>等等。</li>
</ul>
<p><code>main</code> 分支是，嗯，“主”分支——我们将把书中的所有内容合并成一本最终完成的书的地方。</p>
<p>我们可以创建其他分支来分离和跟踪特定的工作。</p>
<p>如果我在写第一章，而你在写第二章，我们可以创建两个不同的分支， <code>chapter-1</code> 和 <code>chapter-2</code> ——实际上就是这本书当前状态的两个不同副本。</p>
<p>这样我们就可以在各自的章节上工作，而互不影响，也不会改动到对方的内容——我们都有自己的工作副本，彼此是分开的。</p>
<p>当我们中的任何一个人完成了自己的章节，我们就可以把我章节分支的内容加回到 <code>main</code> 分支中。当我们两个都完成后， <code>main</code> 分支就会同时包含第一章和第二章。</p>
<p>然而，有些时候，你 <em>会</em> 覆盖或更改和别人的内容相同的内容，我们必须想办法解决这些分歧——很快就会看到。</p>
<blockquote>
<p><strong>注意：</strong> 根据项目的不同，<a href="https://github.com/github/renaming">你可能会看到一个分支</a> 被命名为 <code>master</code> 而不是 <code>main</code> 。它们没有任何功能上的区别，只需要根据你的项目中的内容输入 <code>master</code> 或 <code>main</code> 即可。</p>
</blockquote>
<h2 id="git">如何查看一个 Git 项目的状态</h2>
<p>我们经常会做的一件事是检查我们项目的状态，已经做了哪些改动，我们想用它们做什么？</p>
<p>我们使用 <code>git status</code> 查看项目的状态：</p>
<pre><code>(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
</code></pre>
<p>这个命令的结果中有一些东西，让我们把它们分解一下。</p>
<p><code>git status</code> 告诉我们的第一件事是我们在 <code>main</code> 分支上：</p>
<pre><code> On branch main
</code></pre>
<p>第二句话比较有意思：</p>
<pre><code>Your branch is up to date with 'origin/main'.
</code></pre>
<p>Git 告诉我们，我们的分支是与 <code>origin/main</code> “同步”的。</p>
<p><code>origin</code> 是一个新的概念，被称为 <strong>远端（remote）</strong>。远端是一个不同于你本地机器的“远程源”。</p>
<p>在这个项目中，我们有自己的本地副本，但我们也可以添加可以协作的远程源。毕竟，这是 Git 最大的好处之一：与他人的可控协作。</p>
<p>继续我们写书的例子，如果我在我的机器上写第一章，你在你的机器上写第二章，我们都可以把对方的电脑添加为“远端”并发送和接收对方的修改。</p>
<p>在实践中，编程社区广泛认同最好有一个代码的 <strong>可信单一数据源（SSOT）</strong>。一个代码库的当前状态总是“正确”的地方。按照惯例，我们称这个地方为 <strong>源（origin）</strong>。</p>
<p>在这种情况下，GitHub 是我们的“源”。</p>
<p>事实上，我们可以通过运行 <code>git remote -v</code>（<code>-v</code> 代表 “verbose”）命令看到这一点：</p>
<pre><code>(main)$ git remote -v
origin  git@github.com:johnmosesman/practical-git-tutorial.git (fetch)
origin  git@github.com:johnmosesman/practical-git-tutorial.git (push)
</code></pre>
<p>这个命令列出我们所有的远端。从结果我们们可以看到我们有一个远端叫做 <code>origin</code> ，这个远端的 Git URL 指向我们在 Github.com 上的仓库。它是在我们运行 <code>git clone</code> 时自动设置的。</p>
<p>回到 <code>git status</code> 的结果中的这句话：</p>
<p><code>Your branch is up to date with 'origin/main'.</code></p>
<p>当我们查询项目状态时， Git 告诉我们，我们本地的 <code>main</code> 分支与源（即 GitHub ）的 <code>main</code> 分支是同步的。</p>
<p>事实上， <code>git clone</code> 自动为我们在本地创建了一个 <code>main</code> 分支，因为它看到我们克隆的源有一个叫 <code>main</code> 的分支作为主分支。</p>
<p>基本上，我们本地机器上没有与 GitHub 不同的变化，反之亦然——我们的本地 <code>main</code> 分支和 GitHub 上的 <code>main</code> 分支是相同的。</p>
<p>随着我们进行修改，我们会看到这条信息的变化，来反映我们本地仓库和源（GitHub）仓库的差异。</p>
<p><code>git status</code> 的最后一条信息是关于本地项目的状态：</p>
<pre><code>nothing to commit, working tree clean
</code></pre>
<p>当我们进行修改时会在这里进行更详细的说明，但这个消息基本上是说我们没有做任何事——所以没有变化要报告。</p>
<p>总结一下 <code>git status</code> 的结果：</p>
<ul>
<li>我们在 <code>main</code> 分支上</li>
<li>我们的本地 <code>main</code> 分支与 <code>origin</code> 的（GitHub 的） <code>main</code> 分支相同</li>
<li>我们还没有对该项目做出任何修改</li>
</ul>
<h2 id="commit">如何做出我们的第一次提交（commit）</h2>
<p>现在我们了解了我们项目的初始状态，让我们做一些修改并看看结果。</p>
<p>继续我们写书的比喻，让我们创建一个新的文件，命名为 <code>chapter-1.txt</code> 并在其中插入一个句子。</p>
<p>（你可以使用下面的终端命令（terminal command），或者在任何文本编辑器中创建和编辑该文件——这无关紧要。）</p>
<pre><code>(main)$ touch chapter-1.txt
(main)$ echo "Chapter 1 - The Beginning" &gt;&gt; chapter-1.txt
(main)$ cat chapter-1.txt
Chapter 1 - The Beginning
</code></pre>
<p>上面的命令用 <code>touch</code> 创建了一个名为 <code>chapter-1.txt</code> 的新文件，用 <code>echo</code> 和 <code>&gt;&gt;</code> 操作符插入句子“第一章——开端”，并且为了检查我们的工作，用 <code>cat</code> 现实文件内容。</p>
<p>结果是一个里面有一句话的简单文本文件。</p>
<p>让我们再次运行 <code>git status</code> ，看看它的输出有什么不同：</p>
<pre><code>(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)
        chapter-1.txt

nothing added to commit but untracked files present (use "git add" to track)
</code></pre>
<p>在这里我们看到一个与之前不同的输出结果。我们看到一个描述“未跟踪文件（untracked file）”的部分，我们的新文件 <code>chapter-1.txt</code> 被列在那里。</p>
<p>在 Git 开始跟踪（track）一个文件的变化之前，我们首先需要告诉 Git 去跟踪它——正如消息底部所显示的——我们可以用 <code>git add</code> 来实现：</p>
<pre><code>(main)$ git add chapter-1.txt
</code></pre>
<p>（除了为 <code>git add</code> 制定文件名外，你可以使用（<code>.</code>）来添加目录中的所有修改）。</p>
<p>让我们再检查一下状态：</p>
<pre><code>(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged &lt;file&gt;..." to unstage)
        new file:   chapter-1.txt

john:~/code/practical-git-tutorial (main)$
</code></pre>
<p>信息又变了。它现在说我们有一些修改，已经准备好“提交”。</p>
<p>Git 中的 <strong>commit</strong> 是一个被保存的工作块，但它与你在文本编辑器中保存一个文本文件时的保存有点不同。</p>
<p>你可以把提交想成是一个 <em>完成的想法或工作单元（unit of work）</em>。</p>
<p>例如，如果我们继续写书中第一章的内容，它可能看起来是这样的：</p>
<ul>
<li>写下这一章的标题。 <em>*在我们的编辑器中点击保存*</em></li>
<li>写下这一章的第一段。 <em>*在我们的编辑器中点击保存*</em></li>
<li>写下这一章的第二段。 <em>*再次点击保存*</em></li>
<li>写下这一章的最后一段。 <em>*再次点击保存*</em></li>
</ul>
<p>在这里，这个文件我们已经“保存”了四次，但在这四次保存结束后，我们现在有了这一章的第一稿，这一稿就是一个“工作单元”。</p>
<p>我们想把这个文件保存在我们的电脑上，但是我们也想表明这是一个已经完成的工作单元——即使它只是一个草稿。这是值得被保留的一大块工作。将来我们可能会想重温它、再次编辑它、或者把这个草稿合并到整本书的当前草稿中。</p>
<p>为此，我们创建一个新的提交来表示这个里程碑。每个提交有自己独特的标识符，而且提交的顺序也被保留下来。</p>
<p>要提交我们的修改，必须先用  <code>git add</code> 把它们添加到 <strong>缓存区（staging area）</strong> 。</p>
<p>（我们很快会讨论更多关于 <strong>缓存区</strong> 的问题。）</p>
<p>接下来，我们需要 <code>git commit</code> 来最终完成提交。</p>
<p>最佳实践是提供一个详细的信息，说明你做了 <em>那些修改</em> ——更重要的是——你 <em>为什么</em> 要提交这些修改。</p>
<p>一旦提交历史达到几百或几千条，如果没有一个好的提交信息（commit message）就几乎不可能理解 <em>为什么</em> 会有这些修改。Git 会显示哪些文件有修改，以及这些修改是什么，但这些 <em>修改的意义</em> 要靠我们自己提供。</p>
<p>让我们来提交我们制作的新文件并用 <code>-m</code> 或 "message" 标志来附上提交信息：</p>
<pre><code>(main)$ git commit -m "New chapter 1 file with chapter heading"
[main a8f8b95] New chapter 1 file with chapter heading
 1 file changed, 1 insertion(+)
 create mode 100644 chapter-1.txt
</code></pre>
<p>我们现在已经提交了那块工作，可以通过 <code>git log</code> 查看 Git 日志：</p>
<pre><code>(main)$ git log
commit a8f8b95f19105fe10ed144fead9cab84520181e3 (HEAD -&gt; main)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Fri Mar 19 12:27:35 2021 -0500

    New chapter 1 file with chapter heading

commit 2592324fae9c615a96f856a0d8b8fe1d2d8439f8 (origin/main, origin/HEAD)
Author: John Mosesman &lt;johnmosesman@users.noreply.github.com&gt;
Date:   Wed Mar 17 08:48:25 2021 -0500

    Update README.md

commit 024ea223ee4055ae82ee31fc605bbd8a5a3673a0
Author: John Mosesman &lt;johnmosesman@users.noreply.github.com&gt;
Date:   Wed Mar 17 08:48:10 2021 -0500

    Initial commit
</code></pre>
<p>看一下这个日志，我们会发现在项目历史中有三个提交。</p>
<p>最新的提交是我们刚刚做的那个。我们可以看到刚才使用的提交信息： <em>"New chapter 1 file...".</em></p>
<p>还有两个之前的提交：一个是我初始化项目的时候，另一个是我更新 GitHub 上的 <code>README.md</code> 文件的时候。</p>
<p>注意每个提交都有一长串与之相关的数字和字符：</p>
<pre><code>commit a8f8b95f19105fe10ed144fead9cab84520181e3 (HEAD -&gt; main)
</code></pre>
<p>这一串数字和字符叫作<a href="https://en.wikipedia.org/wiki/SHA-1">安全散列函数（ SHA ）</a>——它是由散列算法（也叫哈希算法）为该提交生成的唯一 ID。现在只需要注意一下，我们很开就会再来讨论这个问题。</p>
<p>在提交 SHA 后面，我们还看到另外两个有趣的东西：</p>
<ul>
<li>在 <code>(HEAD -&gt; main)</code> 旁边是我们最新的提交内容</li>
<li>而 <code>(origin/main, origin/HEAD)</code> 则在该提交之前的提交旁边。</li>
</ul>
<p>这些信息告诉我们 <em>我们的分支和远程的当前状态</em> （据我们所知——稍后我们再详细讨论）。</p>
<p>关于最新的提交，我们看到 <code>HEAD</code> （也就是项目历史中的“我们现在的位置”）只想我们的 <em>本地</em> <code>main</code> 分支——由 <code>HEAD -&gt; main</code> 表示。</p>
<p>这是有道理的，因为我们刚刚做了那个提交，并且我们还没有做其他事情——我还在做那个提交的时间点上。</p>
<p>如果我们看一下以 <code>25923</code> 开头的前一个提交，我们可以看到 <code>(origin/main, origin/HEAD)</code>。这告诉我们，  <em>在源点（即 GitHub）</em> ， GitHub 的 <code>HEAD</code> 或 “当前位置”在我们之前的提交上。</p>
<p>基本上，我们的本地机器认为本地 <code>main</code> 分支的最新改动是我们添加第一章的提交，而我们的本地机器认为 <em>GitHub 上</em> 的最新改动是我们在写这篇文章之前更新 README 的提交。</p>
<p>这也是合理的——我们还没有告诉 GitHub 我们最新的提交。GitHub 仍然认为这个仓库是它看到的最新的。</p>
<p>现在让我们把我们的新提交推送（push）到 GitHub。</p>
<h2 id="pushgithub">如何将我们的第一个提交推送（push）到 GitHub</h2>
<p>我们在本地机器上有一个新的提交，我们需要更新我们的“可信单一数据源”—— <code>origin</code> 远端——即 GitHub。</p>
<p>我们目前在本地的 <code>main</code> 分支上，所以我们需要告诉 GitHub 用我们的新提交来更新它自己的 <code>main</code> 。</p>
<p>我们使用 <code>git push</code> 命令做到这一点，我们可以指定 <em>我们要推送到哪里</em> 以及 <em>我们要推送到哪个分支</em>。</p>
<pre><code>(main)$ git push origin main
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 16 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 326 bytes | 326.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:johnmosesman/practical-git-tutorial.git
   2592324..a8f8b95  main -&gt; main
</code></pre>
<p>这里我们推送到 <code>origin</code> 远端（GitHub）的 <code>main</code> 分支。</p>
<p>输出结果告诉我们 Git 为此所做的一些文件操作，最后一行告诉我们它推送了哪些提交，推送到了哪里。</p>
<pre><code>To github.com:johnmosesman/practical-git-tutorial.git
   2592324..a8f8b95  main -&gt; main
</code></pre>
<p>这里显示我们把我们的 <code>main</code> 分支推送到了 GitHub 的 <code>main</code> 分支。</p>
<p>如果我们回头开一下 <code>git log</code> 的输出，会发现我们的本地和 <code>origin</code> 现在都指向同一个提交：</p>
<pre><code>(main)$ git log
commit f5b6e2f18f742e2b851e38f52a969dd921f72d2f (HEAD -&gt; main, origin/main, origin/HEAD)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Mon Mar 22 10:07:35 2021 -0500

    Added the intro line to chapter 1
</code></pre>
<p>简而言之，在 <code>origin</code> （GitHub）上 <code>main</code> 分支（也写成 <code>origin/main</code> ）现在已经将我们的新提交标记为了历史中的最新提交。</p>
<p>如果我们和其他合作者一些工作，现在他们可以从 GitHub 上拉取（pull）我们的最新修改并开始编辑第一章。</p>
<h2 id="git">如何在 Git 中添加另一个提交</h2>
<p>在我们和其他人合作之前，让我们再做一个小改动，看看当我们编辑一个现有文件时会发生什么。</p>
<p>让我们在第一章的文件里再添加一行：</p>
<pre><code>(main)$ echo "It was the best of times, it was the worst of times" &gt;&gt; chapter-1.txt
(main)$ cat chapter-1.txt
Chapter 1 - The Beginning
It was the best of times, it was the worst of times
</code></pre>
<p>使用 <code>cat</code> 我们可以看到我们的文件现在包含两行。</p>
<p>让我们再看看我们的 Git 仓库的状态：</p>
<pre><code>(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git restore &lt;file&gt;..." to discard changes in working directory)
        modified:   chapter-1.txt

no changes added to commit (use "git add" and/or "git commit -a")
</code></pre>
<p>从顶部开始，我们会注意到输出结果显示 <code>Your branch is up to date with 'origin/main'.</code></p>
<p>这可能看起来比较奇怪，我们刚刚修改了一个文件，但是 Git 只是将我们做的 <em>提交</em> 和 <code>origin/main</code> 中的提交进行比较。</p>
<p>输出的下一部分内容做了更多解释：</p>
<pre><code>Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git restore &lt;file&gt;..." to discard changes in working directory)
        modified:   chapter-1.txt
</code></pre>
<p>在这里 Git 告诉我们，我们有“修改未为提交缓存 (changes not staged for commit)”。</p>
<p>在我们提交一组修改之前，我们首先需要把它们 <strong>缓存（stage）</strong>。</p>
<h3 id="gitstagechange">如何在 Git 中缓存修改（stage change）</h3>
<p>为了说明缓存区域（staging area）的作用，让我们先用  <code>git add</code> 来缓存我们的修改：</p>
<pre><code>(main)$ git add .
(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged &lt;file&gt;..." to unstage)
        modified:   chapter-1.txt
</code></pre>
<p>这些修改现在可以提交了，但在提交之前，让我们在 <code>chapter-1.txt</code> 文件中再添加一个修改。</p>
<p>我要把 <code>chapter-1.txt</code> 的内容完全替换成新的文本：</p>
<blockquote>
<p><strong>注意：</strong> 我在这里使用 <code>&gt;</code> 而不是 <code>&gt;&gt;</code> ，这将替换文件的内容而不是追加到文件中。</p>
</blockquote>
<pre><code>(main)$ echo "New file contents" &gt; chapter-1.txt

(main)$ cat chapter-1.txt
New file contents

(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged &lt;file&gt;..." to unstage)
        modified:   chapter-1.txt

Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git restore &lt;file&gt;..." to discard changes in working directory)
        modified:   chapter-1.txt
</code></pre>
<p>从输出中我们可以看到我们现在有 <em>已缓存的修改（staged changes）</em> ，和 <em>未缓存的修改（not staged changes）</em>。</p>
<p>虽然文件本身只能包含一个东西，但 Git 为我们记录了这两个修改——尽管它们是对同一行的修改！</p>
<p>然而，从上面的输出中我们无法真正知道这些修改是什么——我们只知道它们存在。</p>
<p>我们首先看一下使用命令行（command line）怎么查看这些修改（我从来不用），然后看一下使用图形用户界面（GUI）（100%更好用）。</p>
<h3 id="gitdiff">如何查看 Git 差异（diff）</h3>
<p>要查看这些修改，我们需要查看 Git <strong>差异</strong>。</p>
<p><em>差异</em> 是两组修改之间的差异。这些修改可以是从已缓存的修改到未缓存的修改再到提交中的任何一个。</p>
<p>使用命令行的方式查看需要用到 <code>git diff</code>。</p>
<p>为了全面的展示，我们将在这里看一下这个简单案例的输出。但是，正如我之前提到的，我们对有效的 Git 工作流程感兴趣，一旦你要在多个文件中进行任何大规模的修改，这种命令行输出就变得无效了。</p>
<p>但是为了全面，请看下面：</p>
<pre><code>(main)$ git diff
diff --git a/chapter-1.txt b/chapter-1.txt
index 0450d87..4cbeaee 100644
--- a/chapter-1.txt
+++ b/chapter-1.txt
@@ -1,2 +1 @@
-Chapter 1 - The Beginning
-It was the best of times, it was the worst of times
+New file contents
</code></pre>
<p>我的终端试图对这个输出进行着色来帮助提高可读性，但是需要注意的地方是，它告诉我们这在比较的文件是 <code>chapter-1.txt</code> ，并且在底部显示了实际差异。让我们来看看这几行输出：</p>
<pre><code>-Chapter 1 - The Beginning
-It was the best of times, it was the worst of times
+New file contents
</code></pre>
<p>以减号（ <code>-</code> ）开始的几行是我们完全或部分删除的行，以加号（ <code>+</code> ）开头的行代表完全或部分添加的行。</p>
<p>随着多个文件和修改行的增加，这种输出会很快变得不方便。有一个更好的方法，在我近十年的编程生涯中，我一直使用一个简单的 GUI 程序来帮助查看和管理差异。</p>
<p>我使用的程序叫做 <a href="http://gitx.frim.nl/">GitX</a>，它是一个老旧过时的软件，甚至已经没有了真正的维护。然而，我只是用它来查看和管理文件差异，所以它对我有用。</p>
<p>我不会着重推荐这个软件，它是免费的。虽然我没有用过，但 <a href="https://desktop.github.com/">GitHub Desktop client</a> 很可能是一个不错的选择。</p>
<p>现在把这个小问题解决了，这是差异在我的工具中的样子。</p>
<p>首先，右侧的已缓存修改显示了我们对第二句话的原始添加：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/staged_changes.png" alt="staged_changes" width="600" height="400" loading="lazy"></p>
<p>GitX 中的已缓存修改 (staged changes)</p>
<p>在左侧的未缓存修改 (unstaged changes) 中，我们可以看到完全删除了这两行并添加了新的一行：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/unstaged_changes.png" alt="unstaged_changes" width="600" height="400" loading="lazy"></p>
<p>GitX 中的未缓存修改 (unstaged changes)</p>
<p>这与我们运行的文件替换命令相对应。</p>
<p>在一个 GUI 程序中了解差异要容易的多。这个程序还允许我们通过拖动文件来快速缓存和取消缓存。我甚至可以缓存或取消缓存一个文件中的个别行。</p>
<p>使用命令行与 GUI 程序相比没有任何加分。使用任何能帮你完成工作的方法。</p>
<p>现在我们已经看到了缓存区域和 Git 差异是如何工作的，让我们放弃未缓存修改，这样我们就可以回去提交我们第一个修改。</p>
<p>在我的 GUI 程序中，我可以右键点击文件并点击“放弃修改”，但是我在这里也会演示命令行是怎么工作的。</p>
<p>我们上个 <code>git status</code> 的输出实际上已经告诉我们如何通过使用 <code>git restore</code> 来做到这一点了。我们可以输入文件路径或者 <code>.</code> 来代表整个目录：</p>
<pre><code>(main)$ git restore .
</code></pre>
<p>如果我们再次查看状态，我们就回到了只有我们的已缓存修改，我们可以继续下一步。</p>
<blockquote>
<p><strong>注意：</strong> Git 只提交已缓存修改，所以我们可以把那些未缓存的修改留在我们的工作目录中，这样不会干扰提交过程。</p>
<p>但是，这会使我们未来的修改处理起来更麻烦——所以放弃这些修改以保持我们的工作目录的良好状态是合理的。</p>
</blockquote>
<p>现在，让我们提交这些修改，并附上关于我们所做的事情的信息：</p>
<pre><code>(main)$ git commit -m "Added the intro line to chapter 1"
[main f5b6e2f] Added the intro line to chapter 1
 1 file changed, 1 insertion(+)
</code></pre>
<p>再次检查状态，可以发现我们的分支“比 'origin/main' 领先一次提交 (Your branch is ahead of 'origin/main' by 1 commit)”：</p>
<pre><code>(main)$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
</code></pre>
<p>最后让我们推送我们的修改：</p>
<pre><code>(main)$ git push origin main
</code></pre>
<h2 id="git">如何在 Git 中与他人协作</h2>
<p>到目前为止，我们一直在研究最简单的例子：自己单独在一个分支上工作。</p>
<p>在现实中，我们通常会和多人在多个不同的分支上一起工作。这是 Git 的真正威力：一个可以在众多合作者之间进行协作并跟踪变化的系统。</p>
<p>现在让我们先像项目中只有一个人一样继续工作，但让我们稍微调整一下我们的工作流程，为情况发生变化时做准备。</p>
<p>通常最佳实践是 <em>不</em> 直接在 <code>main</code> 分支上工作。</p>
<p><code>main</code> 分支应该是项目的“可信单一数据源”——对它的改动应该受到仔细审查。<code>origin/main</code> 中的任何改动都会成为其他在项目中工作的任何人的新的“可信数据源”，所以我们不应该在没有经过思考并通过他人审查的情况下随便改动它。</p>
<p>与其直接在 <code>main</code> 上直接工作，不如从 <code>main</code>  <em>分支</em> 出我们自己的 <strong>功能分支 (feature branch)</strong>， 然后将这些修改 <strong>合并 (merge)</strong> 到 <code>main</code>。</p>
<p>这里出现了很多新的术语，所以让我们一步步来。</p>
<h3 id="gitfeaturebranch">Git 中的功能分支 (feature branch)</h3>
<p>首先，让我们从 <code>main</code> 分支出来，创建我们自己的功能分支。</p>
<p>当你从另一个分支上创建一个分支时，你就在 <em>那个时间点上</em> 创建了一个该分支的副本。现在你可以独立于原始分支修改这个新分支。</p>
<p>为了尝试这一点，让我们建立一个名为 <code>chapter-2</code> 的新分支。我们使用 <code>git checkout</code> 加上 <code>-b</code> 标志和我们想要的新分支的名称：</p>
<pre><code>(main)$ git checkout -b chapter-2
Switched to a new branch 'chapter-2'
(chapter-2)$
</code></pre>
<p>现在终端显示我们在 <code>chapter-2</code> 分支上。在 <code>chapter-2</code> 分支上的修改完全不会影响 <code>main</code> 分支。我们有了一个新的游乐场，可以在不影响 <code>main</code> 的情况下做任何我们想做的修改。</p>
<p>这里在后台有很多有趣的事情发生，但就本教程而言，我们只需要知道在 Git 中“检出 (checkout)”某个东西意味着“将我的本地项目改变成与该项目 <em>在某个特定时间点上的样子一模一样。</em> ”你可以把一个分支看作是指向 Git 历史上某个特定时间线的指针。</p>
<p>这里实际发生了很多事情，但现在这个定义应该是足够了。</p>
<p>我们有了一个新的分支，现在这个新的分支和 <code>main</code> 是一样的（我们还没有做任何修改）。</p>
<p>接下来，让我们重复之前已经做过的事情，创建一个名为 <code>chapter-2.txt</code> 的新文件，给它一些内容，然后提交：</p>
<pre><code>(chapter-2)$ touch chapter-2.txt
(chapter-2)$ echo "Chapter 2 - The next chapter" &gt;&gt; chapter-2.txt

(chapter-2)$ git status
On branch chapter-2
Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)
        chapter-2.txt

nothing added to commit but untracked files present (use "git add" to track)

(chapter-2)$ git add .

(chapter-2)$ git commit -m "Creates chapter 2 and adds the topic sentence"
[chapter-2 741822a] Creates chapter 2 and adds the topic sentence
 1 file changed, 1 insertion(+)
 create mode 100644 chapter-2.txt
</code></pre>
<p>这里面没有什么新东西——与我们为第一章所做的事情一样。</p>
<p>现在我们在 <code>chapter-2</code> 分支上有了新的提交，让我们看看 Git 日志，将这个新分支与 <code>main</code> 进行比较：</p>
<pre><code>(chapter-2)$ git log
commit 741822a9fd7b15b6e3caf437dd0617fabf918449 (HEAD -&gt; chapter-2)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Mon Mar 22 10:33:26 2021 -0500

    Creates chapter 2 and adds the topic sentence

commit f5b6e2f18f742e2b851e38f52a969dd921f72d2f (origin/main, origin/HEAD, main)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Mon Mar 22 10:07:35 2021 -0500

    Added the intro line to chapter 1

commit a8f8b95f19105fe10ed144fead9cab84520181e3
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Fri Mar 19 12:27:35 2021 -0500

    New chapter 1 file with chapter heading
...
</code></pre>
<p>我们会注意到在日志中，我们最新的提交显示在最上面，而且我们的 <code>HEAD</code> 又与 <code>origin</code> 不同。这也是合理的——我们是在本地做了一些修改，还不在 GitHub 上。</p>
<p>现在我们需要把我们的新改动放到 <code>main</code> 分支中。</p>
<h2 id="git">用于协作的 Git 工作流程</h2>
<p>有几种方法可以让我们新的第二章进入 <code>main</code> 分支和 GitHub，我们选择的方法取决于项目和我们与他人协作所使用的工作流程。</p>
<p>首先让我们来谈谈我们可以使用的几个不同的工作流程。</p>
<p>第一种是最直接的：</p>
<ol>
<li>将 <code>chapter-2</code> 的修改合并到我们的本地 <code>main</code> 分支</li>
<li>将本地 <code>main</code> 分支推送到 <code>origin/main</code></li>
</ol>
<p>第二种方法更复杂一些：</p>
<ul>
<li>将我们的本地 <code>chapter-2</code> 分支推送到 <code>origin</code> （这会在 <code>origin</code> 上创建一个名为 <code>origin/chapter-2</code> 的新分支）</li>
<li>将 <code>origin/chapter-2</code> 合并到 GitHub 上的 <code>origin/main</code></li>
<li>从 <code>origin/main</code> 拉取新的修改到我们本地的 <code>main</code></li>
</ul>
<p>第一个工作流程肯定更容易，如果我自己一个人做这个项目，没有任何其他合作者，我会用这个工作流程。</p>
<p>然而，如果我有合作者，我不会想从我的本地直接推送到 <code>main</code> 分支。这样做的话，我就会仅凭自己的修改改变和控制了项目历史，而没有获得合作者的任何意见或审查。</p>
<p>出于这个原因，如果多人在同一个项目上工作，我会使用第二种工作流程，因为这对团队来说是一个更好的协作流程。</p>
<p>既然如此，我们就来看看这两种工作流程，让我们从第一个工作流程开始，它不那么复杂。</p>
<h2 id="gitmerge">如何在 Git 中合并 (merge) 一个分支</h2>
<p>当你在 Git 中想把两个分支的内容合并成一个时，有几种方法可以做到。第一种，也可能是最简单的方法，是做一个 <strong>合并</strong></p>
<p>合并，就像它听起来那样，试图将一个分支的内容（修改）应用（或“合并”）到另一个分支。</p>
<p>在我们的情境中，我们想要把 <code>chapter-2</code> 分支的内容 <em>合并到</em> <code>main</code> 中。换句话说，我们先在 <code>main</code> 的当前状态中加入 <code>chapter-2</code> 分支的修改。</p>
<p>我们可以通过使用 <code>git merge</code> 来实现，之后我们会看一下它的结果。</p>
<p>我需要做的第一件事是切换到我们想要把修改合并_进去_ 的主要分支上。因为我们想要 <code>main</code> 吸收 <code>chapter-2</code> 的修改，所以我们首先需要在 <code>main</code> 分支上。</p>
<p>要切换回 <code>main</code> 分支，我们可以再次使用 <code>git checkout</code> 并指定 <code>main</code> 的分支名称。这一次我们不使用 <code>-b</code> 标志，因为我们想切换到一个现有分支，而不是创建一个新分支：</p>
<pre><code>(chapter-2)$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
(main)$
</code></pre>
<p>我们现在回到了 <code>main</code> 分支，我们得到了一个简短的信息，告诉我们已经与 <code>origin/main</code>进行了更新。</p>
<p>接下来，让我们把 <code>chapter-2</code> 分支合并到 <code>main</code>：</p>
<pre><code>(main)$ git merge chapter-2
Updating f5b6e2f..741822a
Fast-forward
 chapter-2.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 chapter-2.txt
</code></pre>
<p>让我们再看一下 Git 日志：</p>
<pre><code>(main)$ git log
commit 741822a9fd7b15b6e3caf437dd0617fabf918449 (HEAD -&gt; main, chapter-2)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Mon Mar 22 10:33:26 2021 -0500

    Creates chapter 2 and adds the topic sentence

commit f5b6e2f18f742e2b851e38f52a969dd921f72d2f (origin/main, origin/HEAD)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Mon Mar 22 10:07:35 2021 -0500

    Added the intro line to chapter 1
    
...
</code></pre>
<p>我们可以看到我们的 <code>main</code> 分支现在包含了 <code>chapter-2</code> 的新提交，而我们的 <code>origin</code> 仍然在前一个提交（因为我们还没有更新 <code>origin</code> ）。</p>
<p>最后，让我们推送修改到 <code>origin/main</code>：</p>
<pre><code>(main)$ git push origin main
Total 0 (delta 0), reused 0 (delta 0)
To github.com:johnmosesman/practical-git-tutorial.git
   f5b6e2f..741822a  main -&gt; main
</code></pre>
<p>我们已经成功合并了 <code>chapter-2</code> 分支，并将修改推送到 GitHub 上了！</p>
<p>作为最后的清理步骤，让我们删除 <code>chapter-2</code> 功能分支，因为它已经被合并到 <code>main</code> 里了：</p>
<pre><code>(main)$ git branch -d chapter-2
Deleted branch chapter-2 (was 741822a).
</code></pre>
<blockquote>
<p><strong>注意：</strong> <code>git branch</code> 命令如果没有分支名称参数，将列出你在本地的所有分支。</p>
<p>加上 <code>-d</code> 标志和一个分支名称，就会删除传递进来的分支。</p>
</blockquote>
<h2 id="pullrequest">拉取请求（pull request）工作流程</h2>
<p>为了完成我们的协作工作流程，让我们在一个名为 <code>chapter-3</code> 的新分支上重复我们对第一章和第二章所做的同样的事情：</p>
<p>（现在是你自己尝试的好时机！）</p>
<pre><code>(main)$ git checkout -b chapter-3
(chapter-3)$ touch chapter-3.txt
(chapter-3)$ echo "Chapter 3 - The End?" &gt;&gt; chapter-3.txt
(chapter-3)$ git add .
(chapter-3)$ git commit -m "Adds Chapter 3"
</code></pre>
<p>现在我们在一个叫做 <code>chapter-3</code> 的新分支上有一个新的提交。</p>
<p>让我们回顾一下如何将这个新的分支合并到 <code>main</code> 中， <em>而不直接在 <code>main</code> 上操作：</em></p>
<ul>
<li>将本地 <code>chapter-3</code> 分支推送到<code>origin</code> （这在 <code>origin</code> 上创建一个名为 <code>origin/chapter-3</code> 的新分支）</li>
<li>将 <code>origin/chapter-3</code> 合并到 GitHub 上的 <code>origin/main</code></li>
<li>从 <code>origin/main</code> 拉取 (pull) 新的修改到我们本地的 <code>main</code></li>
</ul>
<p>还有几个步骤，但都不太复杂。</p>
<p>第一步是把我们的新分支推送到 GitHub。由于这个分支在 GitHub 上还不存在，GitHub 将为我们创建一个新分支，它是我们推送内容的副本：</p>
<pre><code>(chapter-3)$ git push origin chapter-3
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 16 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 299 bytes | 299.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote:
remote: Create a pull request for 'chapter-3' on GitHub by visiting:
remote:      https://github.com/johnmosesman/practical-git-tutorial/pull/new/chapter-3
remote:
To github.com:johnmosesman/practical-git-tutorial.git
 * [new branch]      chapter-3 -&gt; chapter-3
</code></pre>
<p>现在我们在 GitHub 上有了自己的分支，我们可以创建一个 <strong>拉取请求</strong>，让我们的队友审查。</p>
<p>GitHub 甚至在上面的输出中为我们提供了要访问的 URL： <code>https://github.com/johnmosesman/practical-git-tutorial/pull/new/chapter-3</code></p>
<blockquote>
<p><strong>几个注意事项：</strong> 接下来的部分展示了 GitHub 的用户界面和拉取请求的流程，但这个流程应该与其他服务非常相似（如 GitLab、Bitbucket 等）。</p>
<p>另外请记住，我使用的是我自己的仓库，所以你在这里看到的一些 URL 会与你的不同。</p>
</blockquote>
<p>访问上面的 URL， 我们到达了一个发起新拉取请求的页面。</p>
<p>我们可以看到几个东西：</p>
<ul>
<li>一个指定拉取请求名称的地方（一个主题句，以便于理解这个拉取请求是关于什么的）</li>
<li>一个描述框，以解释我们所做的修改和我们想提供的任何其他背景信息（你也可以在这里添加图片、gif或视频）。</li>
<li>而在所有这些下面是我们修改的文件的列表和其中的变化（差异）。</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2021-03-24-at-10.22.13-AM.png" alt="Screen-Shot-2021-03-24-at-10.22.13-AM" width="600" height="400" loading="lazy"></p>
<p>发起一个新的拉取请求</p>
<p>可以注意到，用户界面显示 <code>base: main &lt;- compare: chapter-3</code>。这是 GitHub 在告诉我们，我们正在设置拉取请求将 <code>chapter-3</code>合并 <em>到</em> <code>main</code>。</p>
<p>在拉取请求的描述下方是我们所做修改的差异：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2021-03-24-at-10.26.42-AM.png" alt="Screen-Shot-2021-03-24-at-10.26.42-AM" width="600" height="400" loading="lazy"></p>
<p>拉取请求差异</p>
<p>我们会注意到只有文件 <code>chapter-3.txt</code> 被显示出来，这是因为它是我们修改的唯一文件。</p>
<p>目前在我们的项目中还有其他文件（ <code>chapter-1.txt</code>，<code>chapter-2.txt</code> ），但这些文件没有修改，所有没有必要显示。</p>
<p>我们看到我们插入到 <code>chapter-3.txt</code> 中的一行——在该行的开头有一个 <code>+</code> 号，绿色的背景标志着对该文件的补充。</p>
<p>点击“创建拉取请求（Create Pull Request）”后，我们会被带到我们刚刚制作的新的拉取请求。</p>
<p>在这一点上，我们可以个拉取请求指定一个审查员，通过在差异中的特定行上留言，围绕代码进行讨论。在代码被审查，我们进行了任何必要的修改后，我们就可以准备合并了。</p>
<p>在本教程中，我们将跳过审查过程，直接点击绿色的合并（Merge）按钮：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2021-03-24-at-10.39.55-AM.png" alt="Screen-Shot-2021-03-24-at-10.39.55-AM" width="600" height="400" loading="lazy"></p>
<p>合并的拉取请求</p>
<p>就这样，我们的拉取请求被合并到了 <code>main</code>！</p>
<h2 id="">如何使我们的本地保持同步</h2>
<p>我们现在以一种安全、可控、经过同行审查的方式对 <code>origin/main</code> 进行了修改。</p>
<p>但是，我们的本地对这个修改一无所知。在本地， Git 仍然认为我们的 <code>chapter-3</code> 分支没有合并到 <code>main</code>：</p>
<pre><code>(chapter-3)$ git log
commit 085ca1ce2d0010fdaa1c0ffc23ff880091ce1692 (HEAD -&gt; chapter-3, origin/chapter-3)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Tue Mar 23 09:19:14 2021 -0500

    Adds Chapter 3

commit 741822a9fd7b15b6e3caf437dd0617fabf918449 (origin/main, origin/HEAD, main)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Mon Mar 22 10:33:26 2021 -0500

    Creates chapter 2 and adds the topic sentence

...
</code></pre>
<p>我们的本地显示 <code>origin/main</code> 还在之前的以 <code>741822</code> 开始的提交。我们需要从 <code>origin</code> 拉取新的信息来更新我们的本地仓库。</p>
<h3 id="">如何获取远端数据</h3>
<p>与 Git 的其他许多事情一样，完成同样的任务可以有许多不同的方法。</p>
<p>以我们的目标出发，我们将研究一种在大多数情况下都能发挥作用的简单方法。</p>
<p>首先，让我们切换到本地的 <code>main</code> 分支：</p>
<pre><code>(chapter-3)$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
</code></pre>
<p>我们的本地认为我们的与 <code>origin/main</code> 保持了更新，因为自从我们在一开始使用 <code>git clone</code> 拉取了项目后，我们没有向远端仓库 (<code>origin</code>) 索要新的信息。</p>
<p>Git 仓库不是实时更新的——它们只是某个时间点上的历史快照。要收到关于仓库的任何新信息，我们必须再次发出请求。</p>
<p>我们使用 <code>git fetch</code> 从远程获取任何新信息：</p>
<pre><code>(main)$ git fetch
From github.com:johnmosesman/practical-git-tutorial
   741822a..10630f2  main       -&gt; origin/main
</code></pre>
<p>输出结果显示， <code>origin/main</code> 现在指向一个以 <code>10630f2</code> 开头的提交。这个提交前缀与我们拉取请求的 SHA 一致。</p>
<p>有几种方法可以将两个分支合并到一起，其中一种方法是创建一个 <strong>合并提交 (merge commit)</strong>。这就是这里发生的事情。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/commit.png" alt="commit" width="600" height="400" loading="lazy"></p>
<p>我们的拉取请求的合并提交</p>
<p>我们的本地仓库现在知道这些新提交的存在，但我们还没有对它做任何事情。</p>
<p>运行 <code>git fetch</code> 实际上并没有修改我们的任何文件——它只是从远程下载关于仓库状态的新信息。</p>
<p>现在我们的本地仓库已经知道了每个分支的状态（ 但还没有 <em>修改或更新</em> 任何分支），让我们再次检查一下状态：</p>
<pre><code>(main)$ git status
Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
</code></pre>
<p>我们的本地现在知道我们的本地 <code>main</code> 比 <code>origin/main</code> 晚了两个提交（来自 <code>chapter-3</code> 分支的提交和拉取请求的合并提交）。</p>
<p>它还提示我们使用 <code>git pull</code> 来更新我们的本地分支：</p>
<pre><code>john:~/code/practical-git-tutorial (main)$ git pull origin main
From github.com:johnmosesman/practical-git-tutorial
 * branch            main       -&gt; FETCH_HEAD
Updating 741822a..10630f2
Fast-forward
 chapter-3.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 chapter-3.txt
</code></pre>
<p><code>git pull</code> 命令实际上是运行两条命令的简写：运行 <code>git fetch</code> 后运行 <code>git merge</code>。</p>
<p>由于 <code>git fetch</code> 不会在本地运行任何修改，所以用 <code>git fetch</code> 来查看我们的分支是否与远端同步（也许我们还不想合并修改），或者拉取存在与远端而不在本地上的新分支，都是很有用的。</p>
<p>从远端获取 (fetch) 一个新的分支就会把这个分支也下载到你的本地机器上——因为它是一个新的分支，所以不会与你本地设置中的任何东西冲突。</p>
<p>我们最初可以只做 <code>git pull</code> 而不是先做 <code>git fetch</code> ，但我想解释一下 <code>git fetch</code>，因为它本身非常有用。</p>
<p>运行了 <code>git pull</code> 之后，如果我们再运行一次 <code>git status</code> ，我们会看到所有东西都已经更新了。</p>
<p>就这样，我们从远端拉取了修改，使我们的本地获得了更新！</p>
<h2 id="gitmergeconflict">如何修复 Git 中的合并冲突（merge conflict）</h2>
<p>我们要讨论的最后一个话题是如何处理冲突。</p>
<p>到目前为止， Git 神奇地处理了所有的文件更新，而且大多数时候 Git 都能直接处理。但是，有些时候 Git 不知道如何把修改合并起来，这就产生了 <strong>冲突</strong>。</p>
<p>当合并两个改动了文件中同一行的修改时，就会发生冲突。如果两个提交修改了文件中的同一行， Git 不知道该使用哪一个提交的修改，它就会要求你做出选择。</p>
<p>为了模拟这种情境，我在 Github 上创建了另一个名为 <code>chapter-3-collaboration</code> 的分支。让我们想象一下，一个队友已经开始在个分支上工作，他要求你和他合作完成第三章。</p>
<p>由于这是一个我们本地没有的新分支，我可以用 <code>git fetch</code> 从远程获取新分支的信息，然后用 <code>git checkout</code> 切换到该分支：</p>
<pre><code>(main)$ git fetch
From github.com:johnmosesman/practical-git-tutorial
 * [new branch]      chapter-3-collaboration -&gt; origin/chapter-3-collaboration

(main)$ git checkout chapter-3-collaboration
Branch 'chapter-3-collaboration' set up to track remote branch 'chapter-3-collaboration' from 'origin'.
Switched to a new branch 'chapter-3-collaboration'
(chapter-3-collaboration)$
</code></pre>
<p>现在我们已经把新的分支拉取到本地仓库并切换到了它上。这是目前这个新分支上 <code>chapter-3.txt</code> 的内容：</p>
<pre><code>(chapter-3-collaboration)$ cat chapter-3.txt
Chapter 3 - The End?

This is a sentence.
</code></pre>
<p>这是一个标题和一个句子。让我们把标题改成新的东西，比如 <em>"Chapter 3 - The End Is Only The Beginning."</em></p>
<p><code>chapter-3.txt</code> 的内容现在看起来像这样：</p>
<pre><code>(chapter-3-collaboration)$ cat chapter-3.txt
Chapter 3 - The End Is Only The Beginning

This is a sentence.
</code></pre>
<p>在提交修改后，如果我们试图推送它就会得到这个消息：</p>
<pre><code>(chapter-3-collaboration)$ git push origin chapter-3-collaboration
To github.com:johnmosesman/practical-git-tutorial.git
 ! [rejected]        chapter-3-collaboration -&gt; chapter-3-collaboration (non-fast-forward)
error: failed to push some refs to 'git@github.com:johnmosesman/practical-git-tutorial.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
</code></pre>
<p>我们的队友已经在我们之前做了一些提交，并推送到了远端分支。我们的本地分支现在与远端分支不一致， 直到我们合并队友的修改前 GitHub 会持续拒绝我们的推送。</p>
<pre><code>... the tip of your current branch is behind its remote counterpart. Integrate the remote changes ... before pushing again.
</code></pre>
<p>它也给了我们一个如何操作的提示： <code>git pull</code>。</p>
<pre><code>(chapter-3-collaboration)$ git pull origin chapter-3-collaboration
From github.com:johnmosesman/practical-git-tutorial
 * branch            chapter-3-collaboration -&gt; FETCH_HEAD
Auto-merging chapter-3.txt
CONFLICT (content): Merge conflict in chapter-3.txt
Automatic merge failed; fix conflicts and then commit the result.
</code></pre>
<p>在拉取后——正如我们可能已经预料到的那样，考虑到我们目前正在讨论的主题——我们有一个合并冲突。</p>
<p>Git 试图将我们队友的修改自动合并到我们的文件中，但文件中有一个地方它不能自动合并——我们都修改了同一行。</p>
<p>Git 在“合并中途”停止了工作，并告诉我们在完成合并之前需要修复合并冲突。让我们看看当前的 <code>git status</code> ：</p>
<pre><code>(chapter-3-collaboration)$ git status
On branch chapter-3-collaboration
Your branch and 'origin/chapter-3-collaboration' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add &lt;file&gt;..." to mark resolution)
        both modified:   chapter-3.txt

no changes added to commit (use "git add" and/or "git commit -a")
</code></pre>
<p>Git 告诉我们，我们的分支和远端分支之间有一个不同的提交，它还告诉我们，我们有一些“未合并的路径（unmerged paths）”——我们目前正在合并中途，需要修复冲突。</p>
<p>它显示 <code>chapter-3.txt</code> 目前已被修改，那让我们来看看 <code>chapter-3.txt</code> 的内容：</p>
<pre><code>(chapter-3-collaboration)$ cat chapter-3.txt
&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
Chapter 3 - The End Is Only The Beginning
=======
Chapter 3 - The End But Not The Ending
&gt;&gt;&gt;&gt;&gt;&gt;&gt; 2f6874f650a6a9d2b7ccefa7c9618deb1d45541e

This is a sentence.
</code></pre>
<p>Git 在文件中添加了一些标记以显示冲突发生的位置。我们和队友都修改了标题，所以它被 Git 的冲突标记包裹着： <code>&lt;&lt;&lt;</code> 和 <code>&gt;&gt;&gt;</code> 箭头，以一行 <code>===</code> 分隔。</p>
<p>最上面的一行，以 <code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</code> 为标志，后面是 <em>"Chapter 3 - The End Is Only The Beginning"</em> ，是我们刚刚做的修改。 Git 告诉我们，这一行是我们当前 <code>HEAD</code> 所在的位置——也就是说，这是我们当前提交的修改。</p>
<p>它下面的一行，<em>"Chapter 3 - The End But Not The Ending"</em> 后面跟着 <code>&gt;&gt;&gt;&gt;&gt;&gt;&gt; 2f6874f650a6a9d2b7ccefa7c9618deb1d45541e</code> ，是我们队友的行和提交。</p>
<p>基本上， Git 是在告诉我们，“你想保留这些行（或这些行的组合）里的哪一条？”</p>
<p>你会注意到文件底部那一行没有被冲突标记包裹——他没有被两个提交同时修改。</p>
<p>我们需要通过删除一行或将两行合并为一行来解决冲突（记得也要删除 Git 放在那里的所有额外标记）。</p>
<p>我将组合这些行，所以最后的文件看起来像这样：</p>
<pre><code>(chapter-3-collaboration)$ cat chapter-3.txt
Chapter 3 - The End Is Not The Ending--But Only The Beginning

This is a sentence.
</code></pre>
<p>我们只需要提交我们的冲突解决方案来完成合并：</p>
<pre><code>(chapter-3-collaboration)$ git add .
(chapter-3-collaboration)$ git commit -m "Merge new title from teammate"
[chapter-3-collaboration bd621aa] Merge new title from teammate

(chapter-3-collaboration)$ git status
On branch chapter-3-collaboration
Your branch is ahead of 'origin/chapter-3-collaboration' by 2 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
</code></pre>
<p><code>git status</code> 的结果告诉我们，我们的本地分支比 <code>origin/chapter-3-collaboration</code> 领先两个提交（ <code>is ahead of 'origin/chapter-3-collaboration' by 2 commits.</code>）。</p>
<p>看一下 <code>git log</code> 就可以证明这一点：</p>
<pre><code>commit bd621aa0e491a291af409283f5fd1f68407b94e0 (HEAD -&gt; chapter-3-collaboration)
Merge: 74ed9b0 2f6874f
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Thu Mar 25 09:20:42 2021 -0500

    Merge new title from teammate

commit 74ed9b0d0d9154c912e1f194f04dbd6abea602e6
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Thu Mar 25 09:02:03 2021 -0500

    New title

commit 2f6874f650a6a9d2b7ccefa7c9618deb1d45541e (origin/chapter-3-collaboration)
Author: John Mosesman &lt;johnmosesman@gmail.com&gt;
Date:   Thu Mar 25 08:58:58 2021 -0500

    Update title

...
</code></pre>
<p>由此产生的提交历史中，分支上的提交和我们的合并提交都在顶部。</p>
<p>从这里，我们只需要把我们的修改推送到远端：</p>
<pre><code>(chapter-3-collaboration)$ git pull origin chapter-3-collaboration
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 16 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 647 bytes | 647.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To github.com:johnmosesman/practical-git-tutorial.git
   2f6874f..bd621aa  chapter-3-collaboration -&gt; chapter-3-collaboration
</code></pre>
<p>现在我们已经改变了远端分支，我们的队友需要做一个 <code>git pull</code> 来合并我们新的合并修改。</p>
<p>而且，最好是告诉我们的队友，我们推送了一个新的修改，这样他们就可以在继续编辑之前把它拉取下来——减少他们在未来也要修复合并冲突的可能性。</p>
<h3 id="">在分支上创建分支</h3>
<p>我们也可以在 <code>chapter-3-collaboration</code> 分支上创建自己的分支。这将使我们直到工作最后才需要解决合并冲突。</p>
<p>当我们在自己的独立分支上完成了工作，我们就可以将 <em>我们</em> 的功能分支合并到 <em>队友</em> 的功能分支上，然后再合并到 <code>main</code>。</p>
<blockquote>
<p><code>chapter-3-collaboration-john</code> -&gt; <code>chapter-3-collaboration</code> -&gt; <code>main</code></p>
</blockquote>
<p>正如你所看到的，随着越来越多的分支彼此分离，并成为彼此的前分支和后分支，分支结构会变得相当复杂。</p>
<p>因此，一般来说，保持分支的 **小而独立 (small and isolated) ** 并尝试 <strong>快速和经常合并它们（merge them quickly and often）</strong> 是个好主意。</p>
<p>这可以帮助避免很多痛苦的合并冲突。</p>
<h2 id="">回顾：如何启动一个新的功能工作流程</h2>
<p>最后，我将快速的回顾一下如何着手开始一个新的任务，以及做这个任务需要的命令和流程。</p>
<p>假设你在新工作中得到了第一个任务：处理你的团队产品中的一个小错误（bug）。</p>
<p>你需要做的第一件事时使用 <code>git clone &lt;URL&gt;</code> 拉取该仓库。</p>
<p>接下来，你要用 <code>git checkout -b &lt;BRANCH_NAME&gt;</code> 在 <code>main</code> 之外创建一个功能分支。之后，你将修复这个错误，并使用 <code>git add</code> 和 <code>git commit</code> 提交修改。</p>
<p>也许解决这个问题需要进行多次提交，或者你在试图解决这个问题时做了多次提交，最后才找到了解决方案。这也是可以的。</p>
<p>提交后，将新分支推送到 <code>origin</code> （<code>git push origin &lt;BRANCH_NAME&gt;</code>）并创建一个拉取请求。经过代码审查，你的分支被合并了（耶！）。</p>
<p>现在你已经完成了你的功能，是时候切换回 <code>main</code> 了（使用 <code>git checkout main</code>），使用 <code>git pull</code> 获取你的最新修改和其他人的修改，然后从新的分支开始。</p>
<h2 id="">总结</h2>
<p>正如开头所提到的，使用 Git 和 Git 工作流程有很多种方法。</p>
<p>还有很多 Git 底层的“魔法”（也就是说，正在运行的代码你还不了解），但是随着时间的推移，你会学到并掌握更多东西。</p>
<p>在我职业生涯的前几年，我只是通过记忆命令和工作流程来使用 Git，这很有效。当我遇到问题或与队友协作时，我学到了更多的东西，最终我的 Git 技能得到了扩展。</p>
<p>刚开始的时候，不要把事情搞得太复杂！随着时间的推移，你会学会的。</p>
<p>如果你喜欢这篇文章，我在<a href="https://johnmosesman.com/">我的网站</a>上写像这样的技术话题以及非技术话题。</p>
<p>我也在推特（Twitter）<a href="https://twitter.com/johnmosesman">@johnmosesman</a> 上写相似的东西。</p>
<p>无论怎样，请随时给我发信息。</p>
<p>谢谢阅读！</p>
<p>John</p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/practical-git-and-git-workflows/">How to Use Git and Git Workflows – a Practical Guide</a>，作者：<a href="https://www.freecodecamp.org/news/author/johnmosesman/">John Mosesman</a></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
