版本控制(5)

学习目标

完成本单元后,您将能够:

  • 将GitHub工作流转换为适用于团队的有效分支策略。
  • 解决GitHub上的合并冲突。
  • 创建代表单个工作单元的原子提交。

Illustration of branching in GitHub.

分支策略的工作

现在您已经通过一个简单的GitHub流程示例,想象一下工作流程如何与团队一起工作。将所有分支,提交和请求数量乘以团队中同事的数量,并积极开发。以前有很多团队都做过这样的事情,而且有一些成功的模式。

一般来说,分支机构应该是短暂的,创建完成一个功能,并在合并后删除。短命的分支可避免混淆,鼓励最新的代码,并设置开发人员对项目进行迭代改进。

长时间运行的分支可能会造成问题,如果不被有意使用。有时候,长期的分支对于开发分支是有意义的,或者对于需要多级部署级代码的其他情况。长期运行的分支所面临的大部分挑战来自于没有每个分支的最新版本的开发人员,造成了不必要的合并冲突,混淆和工作。虽然更复杂的分支工作流可能看起来是正确的解决方案,但是它们经常过于复杂,更简单的GitHub流更有效地工作。

有些问题要问你的团队分支:

  • 我们将使用哪种分支策略?
  • 哪个分支将作为我们的主或部署代码?
  • 我们将使用命名约定为我们的分支机构?
  • 我们将如何使用标签和受让人?
  • 我们会使用里程碑吗?
  • 我们会使用项目委员会(项目)吗?
  • 我们是否需要用于问题或拉取请求的模板/元素(例如运输清单)?
  • 我们将如何表示签收请求?
  • 谁将合并请求?

请记住,分支的力量来自一个安全的地方进行更改,并有权审查和测试拉请求。当你决定团队的分支期望时,保持轻量级,易于学习。专注于协作。

处理合并冲突

当你和一个团队一起工作(甚至有时候你独自一人工作),你偶尔会产生合并冲突。起初,合并冲突可能是令人恐惧的,但解决它们其实很简单。

让我们尝试创建一个合并冲突,看看会发生什么。

创建具有冲突提交的多个分支

我们知道合并冲突是在多个提交到相同文件的相同部分的分支上进行的。所以,要练习,让我们来设置。

  1. 从master创建一个新分支(new-branch-1),更改README.md文件,并创建一个pull请求:
    1. 输入命令: git checkout -b new-branch-1
    2. 更改存储库中的README.md文件。记下你改变的哪一行。
    3. 在新分支1上,添加并提交更改:
      1. 输入命令: git add README.md 
      2. 输入命令: git commit -m "Changes to the README"
        注意:在正常的实践中,我们不建议直接向主人提交 – 我们只是在这里做,它很快就会造成合并冲突。
    4. 将分支推到远程仓库: git push -u origin new-branch-1
    5. 打开GitHub并为此提交创建一个拉取请求
  2. 现在,为了创建下一个分支和拉取请求,请检查返回给master:
    git checkout master
  3. 创建一个新分支(new-branch-2),对同一个文件进行更改并创建一个pull请求:
    1. 输入命令: git checkout -b new-branch-2
    2. 将README.md文件更改为文件的同一行。
    3. 输入命令: git add README.md
    4. 输入命令: git commit -m “More changes to the README”
    5. 输入命令: git push -u origin new-branch-2
    6. 在GitHub中,创建一个拉请求

合并一个请求

到目前为止,两个分支都没有合并冲突。在GitHub中,合并您的第一个请求(来自new-branch-1)。

解决对方的冲突

合并new-branch-1的拉取请求后,转到 new-branch-2的拉取请求。

Screenshot showing merge conflict in GitHub.

你会看到有一个合并冲突!不要惊慌,你可以通过这个。根据合并冲突的复杂程度,您可以选择在GitHub上的浏览器中解决冲突,也可能需要在本地解决冲突。

要在本地解决它,您将合并到您的分支,解决冲突,然后再完成合并。

  1. 签出有冲突的分支: git checkout new-branch-2
  2. 确保你的本地仓库是最新的: git pull
  3. 合并主功能分支:git merge origin / master注意你正在合并远程追踪分支,而不是你的本地主副本 – 这是因为pull命令更新了远程分支和你正在使用的分支,但没有更新主分支。
  4. 当你看到有冲突的时候,没关系!输入git status来验证哪个文件有冲突。有冲突的文件列在Unmerged Paths下。
  5. 在文本编辑器中打开该文件,然后查找合并冲突标记。(<<<<<<<=======>>>>>>>)
  6. 两个分支的代码版本都存在 – 选择你想要保留的代码并删除其他代码。确保删除由Git创建的合并冲突标记并保存更改。
  7. 添加并提交保存的更改以解决合并冲突:
    1. 输入命令: git add README.md
    2. 输入命令: git commit -m “Commit to resolve merge conflict”
  8. 将功能分支推到远程: git push
  9. 回到GitHub的pull请求。拉请求现在没有冲突。没有冲突的分支屏幕截图。Screenshot of branch with no conflicts.
  10. 合并拉取请求。

现在我们已经完成了这些分支,在GitHub中删除它们,然后使用git checkout master和git pull命令来同步您的本地存储库。

工艺原子提交

编写原子提交是创建项目可读和信息丰富的历史的重要组成部分。

在一个完美的世界里,你永远不需要去看或改变你的历史。然而,我们并不是生活在一个完美的世界,所以版本控制让我们在必要的时候查看我们的变化历史。每个提交都应该是一个小小的逻辑变化单元,并讲述你的存储库的故事。

让我们练习一个命令来帮助您完成原子提交,git add –patch或git add -p。这个命令可以让您将文件的不同部分添加到暂存区域,帮助您进行确实是逻辑单元更改的提交。

  1. 将 bigFile.md 下载到您的计算机(您可能需要右键单击并选择 Save Target A
  2. 在GitHub中,将bigFile.md上传到最好的仓库中的master分支:
    1. 点击 Upload files
    2. 从您的电脑中选择 bigFile.md
    3. 选择 Commit directly to the master branch
    4. 点击 Commit changes
  3. 确保您的本地主存储库是最新的:
    1. 输入命令: git checkout master
    2. 输入命令: git pull
  4. 看看git跟踪的是什么: git status
    你应该看到没有什么可以提交的。
  5. 对第1行和第100行上的bigFile.md进行更改(修改内容无关紧要)。
  6. 使用–patch标志将一些文件的某些部分移至暂存区: git add -p
  7. 按y或n选择是否放弃hunk。

想知道为什么所有这些其他选项是为人?使用 ?查看大块以上的选项列表。

版本控制(4)

学习目标

完成本单元后,您将能够:

  • 列出GitHub工作流程中的步骤。
  • 解释远程和本地工作环境之间的区别。
  • 完成创建新文件并更改现有文件的步骤。

Flow chart showing code, then collaborate, and then ship.

GitHub工作流程概述

GitHub流程是一个轻量级的工作流程,可让您安全地尝试新想法,而不必担心会影响项目。主要步骤是:

  1. 创建一个分支关闭master
  2. 提交
  3. 打开pull request
  4. 合作
    1. 做更多的承诺
    2. 与集成进行交互
  5. 合并master branch

创建一个分支

分支是Git中的一个关键概念。 Git中的所有东西都生活在一个分支上。默认情况下,项目的生产版本位于主分支中。

当您准备好尝试新功能或解决问题时,请创建项目的新分支。首先,分支看起来和主人一模一样,但是你所做的任何更改只会反映在你的分支中。

提交
在对项目中的文件进行更改时,将它们提交到您的功能分支。

打开合并请求并进行协作
打开拉取请求开始讨论您的更改。拉取请求意味着进一步完善代码的起点 – 不需要它是一个完美的艺术作品。

合并到主分支
一旦您的团队批准您的更改,请将功能分支中的拉取请求合并到主分支中。
所以这在理论上是很好的,但是让我们把它付诸实践吧。

安装Git

在我们完成本单元其余部分的Git示例之前,首先要做的就是在您的计算机上安装Git。这使您可以在本地使用存储库。访问Git网站并按照说明安装Git的正式版本。在安装过程中接受所有的默认设置。

注册一个GitHub帐户并创建一个存储库

你要做的第一件事就是注册一个GitHub个人账户。这是GitHub的免费版本,可以让您按照以下步骤进行操作。

接下来创建一个工作库。

  1. 在标题中,单击并选择 New repository
  2. 输入存储库名称: best-repo-ever
  3. 选择项目是公共还是私人。 (我们现在推荐公众,公共仓库是免费的。)
  4. 选择 Initialize this repository with a README
  5. 点击Create repository

Screenshot of the repository creation screen.

在GitHub和本地工作

您可以直接在GitHub上对项目进行更改,但大多数人更喜欢在本地机器上工作,以便可以在自己喜欢的IDE或文本编辑器中进行更改。我们来回顾几个重要的术语:

  • Remote repository 是GitHub上仓库的副本。所有的协作者都将这些变化与这个同步,使之成为这个组织的真实来源。
  • Local repositories 是存储在用户计算机上的git存储库。如果本地目录链接到远程存储库,则本地存储库是远程存储库上所有内容的完整副本,包括其所有文件,分支和历史记录。

本地和远程存储库仅在Git中运行四个网络命令之一时进行交互:git clonegit fetchgit pull, and git push.

要在存储库上进行本地工作,首先需要在计算机上创建一个克隆。要克隆存储库,请按照下列步骤操作:

  1. 在GitHub中,点击你刚创建的版本库的代码标签
  2. 点击 Clone or download
  3. 将克隆网址复制到剪贴板
  4. 打开你的命令行应用程序。如果您在Mac或Linux上,则可以使用您的终端。如果你在Windows上,我们推荐你使用Git Bash,它随Git一起安装。
  5. 从GitHub中检索存储库的完整副本:git clone <CLONE-URL>。将<CLONE-URL>替换为上面复制的克隆URL。你应该看到这样的东西:
    Cloning into 'best-repo-ever'... 
    remote: Counting objects: 3, done. 
    remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 
    Unpacking objects: 100% (3/3), done.
  6. 克隆完成后,cd进入由克隆操作创建的新目录: cd best-repo-ever

注意:您也可以使用桌面应用程序(如GitHub桌面)与Git和GitHub进行交互。这些提供了与存储库更直观的交互,通常非常强大。

配置您的本地环境

在对代码进行更改之前,您需要设置一些基本配置。您通常只需要设置一次这些配置。 Git允许您在三个不同的级别设置配置选项。

git config --system 这些是系统范围的配置。它们适用于此计算机上的所有用户。
git config --global 这些是用户级配置。他们只适用于您的用户帐户。
git config--local 这些是存储库级配置。它们只适用于设置它们的特定存储库。 git config的默认值是 --local.

Git会自动添加几个配置 – 输入git config –list来查看所有三个级别的配置设置。

Git使用您的用户名和电子邮件地址的配置设置为您创建的每个提交生成一个唯一的指纹。你不能创建提交没有这些设置,所以使用你的命令行应用程序自己设置它们:

$ git config --global user.name "First Last" 
$ git config --global user.email "you@email.com"
注意:您不会看到任何确认这个工作,但只要您没有看到错误,您就设置好了!

配置autocrlf
接下来,我们设置core.autocrlf(autocrlf代表自动回车换行符)。不同的系统处理换行符和换行符的方式不同。如果你打开在另一个操作系统上创建的文件,并且没有设置这个配置选项,Git会认为你根据你的操作系统处理行结束的方式对文件进行了修改。

对于Windows用户输入:

$ git config --global core.autocrlf true
对于Mac或Linux用户输入:
$ git config --global core.autocrlf input
使用GitHub跟踪文件

现在你已经有一个仓库的本地副本,并且已经配置好了Git,你可以使用GitHub工作流对项目进行一些修改。我们首先对README.md文件进行简单的更改。

第1步:创建一个Branch

要查看本地分支的列表,请键入 git branch. 现在,你可能只看到一个分支:master.

让我们为我们的工作创建一个新的分支(myfeaturebranch):

  1. 键入git branch myfeaturebranch
  2. Checkout 到该分支 git checkout myfeaturebranch

你应该看到这样的东西:

kjameson-ltm:best-repo-ever kjameson$ git branch 
   * master  
kjameson-ltm:best-repo-ever kjameson$ git branch myfeaturebranch 
kjameson-ltm:best-repo-ever kjameson$ git checkout myfeaturebranch 
  Switched to branch 'myfeaturebranch'
注意:如果你听说过从其他VCS签出,这可能意味着不同于Git。使用Git,checkout会移动一个名为HEAD的重要指针,在这种情况下,我们将移动到另一个分支。 HEAD指向你的分支的尖端。我们在最后一个单元中讨论HEAD。

步骤2:更改README.md文件并将更改提交到本地存储库

现在你已经签出了新的分支,做一些改变,看看Git的行动。

  1. 打开存储库中的README.md
  2. 用你最喜欢的文本编辑器添加一些内容。
  3. 完成后,保存您的更改。

在对文件进行了一些更改之后,是时候创建第一个快照了。从命令行工作时,您需要熟悉 two stage commit.

当您在本地工作时,您的文件存在四种状态之一。他们要么未被追踪,修改,上演或承诺。 Git跟踪这些文件,并通过在三棵树中组织文件和更改来跟踪您的历史。他们正在工作,分期(也称为索引)和历史。当我们添加,删除和更改文件时,我们在工作树中执行此操作。

Diagram of the three trees of Git, working, staging, and history.

要将更改添加到版本控制中,请创建一组表示离散工作单元的文件集合 – 我们在暂存区域中构建此单元。

当我们对我们组装的工作单元感到满意时,我们会在暂存区域对所有内容进行快照。这被称为 commit.

让我们使用命令git add和git commit来完成这个过程:

  1. 检查我们的工作树的状态: git status
    kjameson-ltm:best-repo-ever kjameson$ git status 
    On branch myfeaturebranch 
    Changes not staged for commit: 
          (use "git add <file>..." to update what will be committed) 
          (use "git checkout -- <file>..." to discard changes in working directory)  
       modified: README.md 
    no changes added to commit (use "git add" and/or "git commit -a")
  2. 将文件从工作树移动到临时区域:git add README.md
  3. 输入git status来查看发生了什么事情:
    kjameson-ltm:best-repo-ever kjameson$ git status
    On branch myfeaturebranch 
    Changes to be committed: 
          (use "git reset HEAD <file>..." to unstage)
       modified: README.md 
  4. 拿我们的第一个快照:git commit -m “My first commit”
  5. 让我们再看看我们的存储库状态: git status
    kjameson-ltm:best-repo-ever kjameson$ git status
    On branch myfeaturebranch 
    nothing to commit, working tree clean

步骤3:将更改发送到远程存储库

现在,这个提交只是本地的。如果您检查远程存储库,您将看不到您的分支或您刚才所做的更改。要查看远程更改,首先需要将更改推送到远程存储库。

既然我们在本地创建了这个分支,我们将首先创建一个与我们本地分支匹配的远程分支。用同样的命令,我们也会建立两个分支之间的跟踪关系。

  1. 输入 git push -u origin myfeaturebranch
  2. 当被问及时,输入你的GitHub用户名,然后输入你的密码。
    kjameson-ltm:best-repo-ever kjameson$ git push -u origin myfeaturebranch 
    Username for 'https://github.com': kierenjameson 
    Password for 'https://kierenjameson@github.com': 
    Counting objects: 6, done. 
    Delta compression using up to 4 threads. 
    Compressing objects: 100% (4/4), done. 
    Writing objects: 100% (6/6), 551 bytes | 0 bytes/s, done. 
    Total 6 (delta 1), reused 0 (delta 0) 
    remote: Resolving deltas: 100% (1/1), done. 
    To https://github.com/kierenjameson/best-repo-ever.git
       * [new branch] myfeaturebranch -> myfeaturebranch 
    Branch myfeaturebranch set up to track remote branch myfeaturebranch from origin.

注意:第一次推新分支时只需要这么长的命令。之后,你可以简单地输入git push。

第4步:创建合并请求

现在,您已将更改推送到远程存储库,让我们在GitHub上打开一个pull请求。

  1. 转到您的GitHub帐户在网上
  2. 点击Pull Request 标签
  3. 点击 New Pull Request
  4. 在Base下拉菜单中选择 master
  5. 在比较下拉列表中,选择myfeaturebranch。你应该看到这样的东西:

Screenshot from GitHub showing comparison of changes between master branch and myfeaturebranch.

  1. 点击 Create pull request
  2. 输入 Subject 和Comment
  3. 点击 Create pull request

通过代码审查控制代码质量

拉取请求不仅仅是分支的比较,它们是通过人工和自动化努力来检查代码并确保质量的一种方式。

一般对话
使用“对话”选项卡在拉取请求上添加常规注释。

行评论
在“文件已更改”选项卡(A)中,可以将鼠标悬停在一行上以查看蓝色+图标(B)。点击此图标将允许您在特定的行上输入注释(C)。这些行级别注释是提供建议更改的附加上下文的好方法。它们也将显示在会话视图中。

Screenshot showing how to add line comments.

评论
在进行评论时,您也可以选择开始评论(D)。创建审阅时,可以将许多行注释与摘要消息一起分组。提交评论时,您可以指出是评论,批准还是要求更改。在与受保护分支结合使用时,请求评论在GitHub中具有特殊的功能,如果没有至少一个评论,则可以防止合并请求。

自动化测试
如果您已经将CI / CD与您的项目集成在一起,您将看到正确的报告请求状态。这些测试是高度可定制的。

合并您的更改

当你合并你的分支时,你从你的功能分支中获取内容和历史记录,并将其添加到主分支的内容和历史记录中。

合并是快速和容易点击合并拉取请求,添加合并注释,然后单击确认合并。

你的团队应该建立谁应该合并拉取请求的规则。一些选项包括:

  • 创建拉取请求的人应该合并,因为他们需要解决合并导致的问题。
  • 在项目组中分配一个人。这确保了一致性,但可能成为一个瓶颈。
  • 创建拉取请求的人以外的任何人都可以合并。这确保了至少一次审查已经发生。

保持一切同步

合并你的pull请求之后,删除GitHub上的分支。要执行此操作,请点击请求画面底部的删除分支。

但是,在GitHub上合并和删除不会自动更新您的本地版本库。让我们回到我们的命令行应用程序,并让所有内容同步。

首先,我们需要将我们在GitHub上所做的更改导入到本地版本库中:

  1. 切换回你的默认分支: git checkout master
  2. 从GitHub中检索所有更改: git pull
    kjameson-ltm:best-repo-ever kjameson$ git checkout master 
    Switched to branch 'master' 
    Your branch is up-to-date with 'origin/master'. 
    
    kjameson-ltm:best-repo-ever kjameson$ git pull 
    remote: Counting objects: 1, done. 
    remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 
    Unpacking objects: 100% (1/1), done. 
    From https://github.com/kierenjameson/best-repo-ever 
           f464053..599f35f master -> origin/master 
    Updating f464053..599f35f 
    Fast-forward 
       README.md | 4 +++- 1 
       file changed, 3 insertions(+), 1 deletion(-)
    注意:git pull是一个组合命令,它从GitHub中检索所有更改,然后更新当前所在的分支,以包含来自远程的更改。正在运行的两个独立命令是git fetch和git merge。

版本控制(3)

学习目标

完成本单元后,您将能够:

  • 建立促进协作的组织结构。
  • 列出GitHub存储库中最常访问的部分。
  • 在GitHub中解释与您的源代码一起存在的协作功能。

GitHub Logo

参观一个GitHub仓库

在我们深入了解单元4中的Git命令之前,我们先来谈谈GitHub的结构,以及它如何帮助您与团队协作。

存储库是GitHub的最基本的元素。把它想象成一个项目文件夹是最简单的。但是,与笔记本电脑上的普通文件夹不同,GitHub存储库还提供了简单而强大的与其他人协作的工具。

存储库包含与项目相关的所有文件(包括文档),并存储每个文件的修订历史记录。无论你只是好奇,或者你是一个主要的贡献者,知道你周围存储库的方式是必不可少的!

Create a Git Repository

计划与代码和谐一致的任务

规划和代码并排,但也完全交织在一个GitHub仓库。这范围从能够搜索特定代码的存储库到能够查看先前版本的文件的能力来识别哪些提交影响了这些版本以及什么对话被关联。

设置你的GitHub组织

有效的计划从了解谁是谁开始。在GitHub上建立团队和组织可以使协作过程更顺畅。

当你注册GitHub时,你会自动获得一个用户帐户。用户帐户的权限非常简单,您可以将人员添加为特定存储库的协作者。

组织帐户可以更精确地控制存储库权限。对他们来说,你创建了一批人,然后让这些团队访问特定的存储库。权限可以在团队级别进行分配(例如,读取,写入或管理),但您也可以在逐个用户的基础上进行。

团队是GitHub的一个强大功能,可以根据公司的实际团队结构进行构建,也可以基于可能在用户指定角色之外的技能,兴趣和专业知识进行临时组织。

GitHub上的存储库中有很多信息。最初,最重要的部分是代码,问题,合并请求和项目。

在存储库中的代码

代码视图是您可以在其中找到存储库中包含的文件的位置。这些文件可能包含项目代码,文档和其他重要文件。我们也称这个观点为项目的根源。这些文件的任何更改将通过Git版本控制进行跟踪。

当您访问存储库时,代码视图会自动显示默认分支。在创建存储库时,默认分支称为主分区,但如果您更喜欢不同的东西,则可以更改它。您也可以通过在下拉列表中选择分支来显示已经推送到远程的任何分支。

Screenshot showing switching between branches in a GitHub repository.

使用问题进行项目讨论

很像一个论坛,问题是一个线程讨论,并入GitHub存储库。您可以使用它们来跟踪错误和功能请求,并且可以将它们分配给特定的团队成员。它们旨在鼓励讨论,协作甚至社区参与。

问题是简单和轻量级的,并且意在与GitHub的其他强大功能结合使用。一起使用时,问题会创建与其他问题的链接,请求,标签,里程碑和项目。

拉取请求

拉请求是两个分支之间的比较。一般来说,这个比较是在master(生产代码的分支)和另一个用于开发代码的分支之间进行的。拉取请求表示对代码的更改。这可能意味着添加,修改或删除作者希望在另一个分支(通常在生产代码中)合并的分支上创建的文件。通常,在拉取请求中完成的工作源于存储库中的问题。

Screenshot showing the changes made to a file.

拉取请求也是一个可以扩展两个分支之间变化的对话的地方。比方说,阿里介绍了一个错误修复,但意外地创建一个新的错误时解决修复。莎莉可以查看这项工作,向阿里发出一个快速评论(请求拉),以便自己解决这个问题,或者自己解决问题,或者让阿里有机会在合并之前进行快速编辑。

Screenshot showing discussion around code changes in a commit.

通过简化代码审查和显示任何自动化测试的状态,提取请求可帮助您编写更好的软件。 这是不容忽视的 – 一个拉取请求比较代码,给出一个有关该代码的对话平台,鼓励协作彻底的代码审查,并与各种集成(包括测试!)无缝工作。 拉请求是GitHub上最强大的协作方面之一。

使用项目来组织你的工作

问题和拉请求是奇妙的,并提供各种协作风格巨大的好处。 项目将它们结合起来,使跟踪和规划更大规模的工作更直观。

Screenshot of GitHub’s Kanban style project boards.

通过项目,您可以使用看板样式板来查看自己的工作 – 移动任务卡,问题以及沿自定义列拉取请求。 项目也可以在存储库或组织级别创建,因此可以将问题或来自多个存储库的请求合并到一个计划页面中。

版本控制(3)

学习目标

完成本单元后,您将能够:

  • 建立促进协作的组织结构。
  • 列出GitHub存储库中最常访问的部分。
  • 在GitHub中解释与您的源代码一起存在的协作功能。

GitHub Logo

参观一个GitHub仓库

在我们深入了解单元4中的Git命令之前,我们先来谈谈GitHub的结构,以及它如何帮助您与团队协作。

存储库是GitHub的最基本的元素。把它想象成一个项目文件夹是最简单的。但是,与笔记本电脑上的普通文件夹不同,GitHub存储库还提供了简单而强大的与其他人协作的工具。

存储库包含与项目相关的所有文件(包括文档),并存储每个文件的修订历史记录。无论你只是好奇,或者你是一个主要的贡献者,知道你周围存储库的方式是必不可少的!

Create a Git Repository

计划与代码和谐一致的任务

规划和代码并排,但也完全交织在一个GitHub仓库。这范围从能够搜索特定代码的存储库到能够查看先前版本的文件的能力来识别哪些提交影响了这些版本以及什么对话被关联。

设置你的GitHub组织

有效的计划从了解谁是谁开始。在GitHub上建立团队和组织可以使协作过程更顺畅。

当你注册GitHub时,你会自动获得一个用户帐户。用户帐户的权限非常简单,您可以将人员添加为特定存储库的协作者。

组织帐户可以更精确地控制存储库权限。对他们来说,你创建了一批人,然后让这些团队访问特定的存储库。权限可以在团队级别进行分配(例如,读取,写入或管理),但您也可以在逐个用户的基础上进行。

团队是GitHub的一个强大功能,可以根据公司的实际团队结构进行构建,也可以基于可能在用户指定角色之外的技能,兴趣和专业知识进行临时组织。

GitHub上的存储库中有很多信息。最初,最重要的部分是代码,问题,合并请求和项目。

在存储库中的代码

代码视图是您可以在其中找到存储库中包含的文件的位置。这些文件可能包含项目代码,文档和其他重要文件。我们也称这个观点为项目的根源。这些文件的任何更改将通过Git版本控制进行跟踪。

当您访问存储库时,代码视图会自动显示默认分支。在创建存储库时,默认分支称为主分区,但如果您更喜欢不同的东西,则可以更改它。您也可以通过在下拉列表中选择分支来显示已经推送到远程的任何分支。

Screenshot showing switching between branches in a GitHub repository.

使用问题进行项目讨论

很像一个论坛,问题是一个线程讨论,并入GitHub存储库。您可以使用它们来跟踪错误和功能请求,并且可以将它们分配给特定的团队成员。它们旨在鼓励讨论,协作甚至社区参与。

问题是简单和轻量级的,并且意在与GitHub的其他强大功能结合使用。一起使用时,问题会创建与其他问题的链接,请求,标签,里程碑和项目。

拉取请求

拉请求是两个分支之间的比较。一般来说,这个比较是在master(生产代码的分支)和另一个用于开发代码的分支之间进行的。拉取请求表示对代码的更改。这可能意味着添加,修改或删除作者希望在另一个分支(通常在生产代码中)合并的分支上创建的文件。通常,在拉取请求中完成的工作源于存储库中的问题。

Screenshot showing the changes made to a file.

拉取请求也是一个可以扩展两个分支之间变化的对话的地方。比方说,阿里介绍了一个错误修复,但意外地创建一个新的错误时解决修复。莎莉可以查看这项工作,向阿里发出一个快速评论(请求拉),以便自己解决这个问题,或者自己解决问题,或者让阿里有机会在合并之前进行快速编辑。

Screenshot showing discussion around code changes in a commit.

通过简化代码审查和显示任何自动化测试的状态,提取请求可帮助您编写更好的软件。 这是不容忽视的 – 一个拉取请求比较代码,给出一个有关该代码的对话平台,鼓励协作彻底的代码审查,并与各种集成(包括测试!)无缝工作。 拉请求是GitHub上最强大的协作方面之一。

使用项目来组织你的工作

问题和拉请求是奇妙的,并提供各种协作风格巨大的好处。 项目将它们结合起来,使跟踪和规划更大规模的工作更直观。

Screenshot of GitHub’s Kanban style project boards.

通过项目,您可以使用看板样式板来查看自己的工作 – 移动任务卡,问题以及沿自定义列拉取请求。 项目也可以在存储库或组织级别创建,因此可以将问题或来自多个存储库的请求合并到一个计划页面中。

版本控制(2)

学习目标

完成本单元后,您将能够:

  • 列出现代开发人员工具箱中的基本工具。
  • 解释Git和GitHub如何在SDLC中一起工作。
  • 描述GitHub在软件开发生态系统中的地位。

GitHub icon

Git和GitHub的区别

Git和GitHub共享一个名字和一个任务,但它们不是一回事。准确区分Git和GitHub之间的界限可能需要一些时间,没关系。你在前面的单元中被引入到每个单元中,但是我们来深入一点,以了解它们是如何一起工作的。

Git是版本控制应用程序

简而言之,Git是跟踪与项目变更相关的所有内容的应用程序。首先定义几个关键术语:

  • 存储库: 用于编译项目的源文件的集合。
  • 提交: 项目的快照,因为它存在于特定的时间点。您在创建项目时创建提交,以在添加和删除离散工作单元时指示点。
  • 分支: 一系列代表您的项目随着时间的变化的提交。每个存储库都有一个默认分支,其中包含代码的生产就绪版本。在您开发新功能,修复错误或对项目进行其他更改时创建其他分支。这些分支将您的实验代码与您的测试生产代码分开。
  • 合并: 两个或更多分支的合并历史。大多数情况下,您会将功能分支合并到存储库的默认或部署分支中,以便将功能转移到生产环境中。
  • 标记: 指向特定提交的指针,提供对事件的持久引用。通常,标签与语义版本控制一起使用,以在应用程序发布时代表点。

GitHub是一个协作平台

GitHub是Git仓库的主机,具有协作功能,可以让您对代码进行更改和测试。用Git的话来说,GitHub是一个远程的,为开发人员提供了一个可以共享的工作的真实来源。除了访问存储库的所有Git数据之外,GitHub还有一些关键的术语:

  • 问题: 对您的项目进行一般讨论,制定新功能计划并讨论错误。一个问题只是一个讨论,没有实际的代码改变发生在这里。
  • 拉取请求: 拉取请求是您要求合并到默认分支的一组提交。拉取请求提供了一个讨论您所提​​议的更改的地方,并邀请其他团队成员评论和完成代码评审。拉取请求还可以帮助您看到自动化测试和许多其他很酷的集成的结果。

GitHub旨在为开发人员提供高度透明和环境的环境,从而做出最佳的工作。

总之,Git处理版本控制,GitHub处理协作。

现代开发者工具包

Git和GitHub一起构成了现代开发人员工具箱的核心。与Salesforce应用程序类似,数百个集成商已经开发了一些工具来根据您的偏好定制和扩展GitHub的功能。这个不断发展的生态系统执行重要任务,如项目管理,持续集成测试,部署,代码审查等。开发者可能每天使用GitHub,但它可能不是唯一使用的工具。

注意

GitHub是一款可轻松与新的Salesforce Developer Experience(DX)集成的工具。 Salesforce DX是一套新的工具,可以简化整个开发生命周期,从改进团队开发和协作,到促进自动化测试和持续集成,使发布周期更加高效和灵活。而且这也是一个新的开发模式,将真实的来源从组织转移到您的版本控制系统(VCS)。 GitHub可以派上用场,因为它的VCS和其他功能。在使用SFDX模块的应用程序开发中阅读有关Salesforce DX的更多信息。

这里有几个值得注意的例子:

GitHub不是一个完整的开发环境。尽管它包含一个用于进行更改的简单UI,但大多数开发人员更喜欢在集成开发环境(IDE)中进行本地工作。 Sublime Text,Atom和Eclipse等现代文本编辑器(针对Salesforce开发人员使用Force.com IDE 2)变得越来越流行,因为他们应用相同的可定制方法,将简单的文本编辑与数千个用户创建的软件包结合起来,自定义工作流程。

持续集成(CI)和持续部署(CD)应用程序是可以添加到工作流程中的一些最强大的工具。借助GitHub,这些CI和CD集成可以根据存储库中存储的规范来测试,构建和部署项目。这些重要的整合减少了运行手动测试的时间和挫折,“它在我的机器上运行”的不一致性,并等待其他人执行简单的审查或测试。

与GitHub配合的其他类型的集成是无止境的,从代码质量检查工具到依赖关系管理,甚至自动安全检查。 GitHub的API和webhook可以让开发人员所需的任何方式添加和定制新的集成。

自定义工作流程以满足您的需求

看似无穷无尽的选择,您可以自定义GitHub,以满足您的项目的确切需求和偏好。无论您是喜欢GitHub内置项目之类的轻量级项目管理解决方案还是像Waffle.io或ZenHub集成之类的全功能项目,GitHub生态系统中都有丰富的选项。在下一个单元中,我们介绍基础的GitHub流程,但是从哪里获取它,取决于您!

版本控制(1)

学习目标

完成本单元后,您将能够:

  • 描述软件开发团队面临的常见挑战。
  • 解释版本控制在缓解这些挑战中的作用。
  • 描述有效开发团队共享的原则。

注意

我们Salesforce和GitHub很高兴在这个模块上进行合作,学习使用像Git这样的版本控制系统(VCS)。我们共同致力于创建成功的公司,我们认识到VCS在确保成功方面发挥的关键作用。

没有版本控制的团队可怕的故事

认识Sally,Gunjan和Ali。这三个惊人的开发人员正在开发一个令人兴奋的新的Salesforce应用程序,明天是大发布日。 Sally和Gunjan一直在开发者组织工作,阿里在沙盒组织工作。 Sally创建一个非托管包,并将其推送到沙箱组织。 Gunjan也一样。有什么可能出错?

  • 没有合并过程,所以他们可以很容易地覆盖彼此的变化。
  • 如果阿里在沙箱里所做的改变被覆盖了,那么就没有办法再去夺回他们了。

Sally,Gunjan和Ali是经验丰富的开发人员。他们有适当的流程来防止这些事情的发生,但是这需要花费很多时间和精心的计划 – 这些能量可能会花在构建新功能上。这个团队可能还不知道,但他们受到工具的限制。即使没有感觉受到限制,他们可能会意识到,他们没有团队有潜力成为有效或生产力。

版本控制到救援

存在版本控制来解决这些问题,每个开发人员都可以轻松实现。有许多高质量的免费和开源版本控制系统,但是这个课程的重点在Git上。无论您是单独工作还是与团队一起工作,改变您的工作流程可以做的不仅仅是帮助您避免刚刚发现的问题。它也可以提高你的工作的整体质量。

版本控制如何帮助团队发展

术语版本控制广义上包括帮助人们跟踪多个版本的任何系统。这可以包括上面例子中的命名约定,但是它更经常用于描述像Git这样的软件工具。

每个VCS有不同的优点和缺点,但都可以分为中央或分布式两种类型。

A diagram showing Git’s distributed naturel.Git是分布式版本控制系统最流行的例子。分布式版本控制系统为开发人员提供了灵活性和自由度,为开发者本地机器上的所有分支机构(工作线)和提交(历史中的保存点)提供了每个协作者的整个存储库副本。

合作赢取

现代版本控制系统旨在帮助解决团队在协作时遇到的问题。

解决一起工作的问题不仅仅是像Git这样的伟大的VCS,它还要求你退后一步并评估你的方法。打破孤岛,拥抱更多的观点和对话可以使您和您的团队提供更好的软件。有些工具和工作流程旨在提高沟通和整体质量。

进入GitHub – 一个建立在Git之上的协作平台。 GitHub作为一个社区空间,您可以分享您的工作,查看您的团队的工作,完成代码审查,并连接帮助您构建,测试和部署代码的集成。 GitHub是世界上最大的开源项目集合的所在地,越来越多的世界各地的进步组织认识到开放式协作开发的好处。

Visualforce-邮件模板(4)控制器

Visualforce电子邮件模板可以利用自定义控制器呈现高度自定义的内容为此,请在使用该自定义控制器的Visualforce电子邮件模板中包含自定义组件。
例如,假设您想在电子邮件模板中显示以单词“Smith”开头的所有帐户的列表。为此,首先编写一个使用SOSL调用的自定义控制器,以返回以“Smith”开头的帐户列表:

public class findSmithAccounts {
private final List<Account> accounts;
public findSmithAccounts() {
accounts = [select Name from Account where Name LIKE ‘Smith_%’];
}
public List<Account> getSmithAccounts() {
return accounts;
}
}

接下来,创建一个名为smithAccounts的自定义组件,该组件使用此控制器:

<apex:component controller=”findSmithAccounts” access=”global”>
<apex:dataTable value=”{!SmithAccounts}” var=”s_account”>
<apex:column>
<apex:facet name=”header”>Account Name</apex:facet>
{!s_account.Name}
</apex:column>
</apex:dataTable>
</apex:component>

提示:请记住,Visualforce电子邮件模板中使用的所有自定义组件必须具有全局访问级别。
最后,创建一个包含smithAccounts组件的Visualforce电子邮件模板:

<messaging:emailTemplate subject=”Embedding Apex Code” recipientType=”Contact”
relatedToType=”Opportunity”>
<messaging:htmlEmailBody>
<p>As you requested, here’s a list of all our Smith accounts:</p>
<c:smithAccounts/>
<p>Hope this helps with the {!relatedToType}.</p>
</messaging:htmlEmailBody>
</messaging:emailTemplate>

请注意,尽管emailTemplate组件需要relatedToType属性,但它对此示例没有任何影响。它具有“机会”的价值只是为了表明它可以采用与自定义组件中使用的对象不同的对象值。

注意:

如果您的电子邮件模板使用标准控制器,则强制共享设置。如果用户对象的组织范围默认设置为“私人”,并且您需要访问Visualforce电子邮件模板中的用户信息(如姓名和电子邮件地址),则可以使用带有不共享关键字的自定义组件或自定义控制器。
有关共享用户对象的信息,请参阅Salesforce联机帮助中的用户共享概述。

Visualforce-邮件模板(3)附件

您可以将附件添加到Visualforce电子邮件模板。每个附件必须封装在一个<messaging:attachment>组件中。 <messaging:attachment>中的代码可以是HTML和Visualforce标签的组合。
前面的示例显示了如何通过遍历一些数据并将其显示给电子邮件收件人来创建Visualforce电子邮件模板。此示例显示如何修改该标记以将数据显示为附件:

<messaging:emailTemplate recipientType=”Contact”
relatedToType=”Account”
subject=”Case report for Account: {!relatedTo.name}”
replyTo=”support@acme.com”>
<messaging:htmlEmailBody>
<html>
<body>
<p>Dear {!recipient.name},</p>
<p>Attached is a list of cases related to {!relatedTo.name}.</p>
<center>
<apex:outputLink value=”http://www.salesforce.com”>
For more detailed information login to Salesforce.com
</apex:outputLink>
</center>
</body>
</html>
</messaging:htmlEmailBody>
<messaging:attachment>
<apex:repeat var=”cx” value=”{!relatedTo.Cases}”>
Case Number: {!cx.CaseNumber}
Origin: {!cx.Origin}
Creator Email: {!cx.Contact.email}
Case Number: {!cx.Status}
</apex:repeat>
</messaging:attachment>
</messaging:emailTemplate>

这个标记在电子邮件中作为附加的数据文件呈现,没有任何格式。您可以使用以下选项之一以更易读的格式显示数据:

更改文件名

<messaging:attachment>标记具有一个名为filename的属性,用于定义附加文件的名称。定义一个容易识别的名字是一个好习惯,但这不是必需的。如果不确定,Salesforce会为您生成一个名称。
没有扩展名的文件名默认为文本文件。您可以将附件呈现为CSV格式:

<messaging:attachment filename=”cases.csv”>
<apex:repeat var=”cx” value=”{!relatedTo.Cases}”>
{!cx.CaseNumber}
{!cx.Origin}
{!cx.Contact.email}
{!cx.Status}
</apex:repeat>
</messaging:attachment>

您也可以将数据呈现为HTML文件:

<messaging:attachment filename=”cases.html”>
<html>
<body>
<table border=”0″ >
<tr>
<th>Case Number</th><th>Origin</th>
<th>Creator Email</th><th>Status</th>
</tr>
<apex:repeat var=”cx” value=”{!relatedTo.Cases}”>
<tr>
<td><a href =
“https://na1.salesforce.com/{!cx.id}”>{!cx.CaseNumber}
</a></td>
<td>{!cx.Origin}</td>
<td>{!cx.Contact.email}</td>
<td>{!cx.Status}</td>
</tr>
</apex:repeat>
</table>
</body>
</html>
</messaging:attachment>

尽管您只能为每个<messaging:attachment>组件定义一个文件名,但可以将多个文件附加到电子邮件中。

更改呈现属性

与其他Visualforce页面类似,在<messaging:attachment>组件上将renderAs属性设置为PDF将使该附件呈现为PDF。例如:

<messaging:attachment renderAs=”PDF” filename=”cases.pdf”>
<html>
<body>
<p>You can display your {!relatedTo.name} cases as a PDF:</p>
<table border=”0″ >
<tr>
<th>Case Number</th><th>Origin</th>
<th>Creator Email</th><th>Status</th>
</tr>
<apex:repeat var=”cx” value=”{!relatedTo.Cases}”>
<tr>
<td><a href =
“https://na1.salesforce.com/{!cx.id}”>{!cx.CaseNumber}
</a></td>
<td>{!cx.Origin}</td>
<td>{!cx.Contact.email}</td>
<td>{!cx.Status}</td>
</tr>
</apex:repeat>
</table>
</body>
</html>
</messaging:attachment>

Visualforce PDF呈现服务的限制包括以下内容。

  • PDF是唯一支持的呈现服务。
  • PDF呈现服务呈现PDF版本1.4。
  • 将Visualforce页面呈现为PDF文件适用于为打印设计和优化的页面。
  • 作为PDF文件呈现的Visualforce页面显示在浏览器中,或根据浏览器的设置进行下载。具体行为取决于浏览器,版本和用户设置,并且不受Visualforce的控制。
  • PDF呈现服务在您的页面上呈现标记和数据,但是它可能不会呈现包含在添加到页面的富文本区域内容中的格式。
  • 没有断点的长行文本(如空格或破折号)不能由PDF呈现服务打包。这通常发生在非常长的URL,注册表项等等。当这些行比页面宽时,它们会增加页面内容的宽度,超出PDF页面的边缘。这会导致内容从页面“流”,切断它。
  • 不要使用不易打印格式的标准组件,或输入或按钮等表单元素,或任何需要JavaScript格式的组件。
  • PDF渲染不支持JavaScript呈现的内容。
  • Salesforce应用程序中的页面不支持PDF呈现。
  • 页面上使用的字体必须在Visualforce PDF呈现服务上可用。 Web字体不受支持。
  • 如果PDF文件无法显示所有页面的文本,特别是多字节字符(如日文或重音国际字符),请调整CSS以使用支持它们的字体。 例如:
    <apex:page showHeader=”false” applyBodyTag=”false” renderAs=”pdf”>
    <head>
    <style>
    body { font-family: ‘Arial Unicode MS’; }
    </style>
    </head>
    <body>
    これはサンプルページです。<br/>
    This is a sample page: API version 28.0
    </body>
    </apex:page>
    “Arial Unicode MS”是包含多字节字符的扩展字符集所支持的唯一字体。
  • 如果使用内联CSS样式,请将API版本设置为28.0或更高版本。同样设置<apex:page applyBodyTag =“false”>,并在页面中添加静态的,有效的<head>和<body>标签,就像前面的例子。
  • 创建PDF文件时的最大响应大小必须小于15 MB,然后才能呈现为PDF文件。此限制是所有Visualforce请求的标准限制。
  • 生成的PDF文件的最大文件大小为60 MB。
  • 生成的PDF中包含的所有图像的最大总大小为30 MB。
  • PDF呈现不支持以数据:URI方案格式编码的图像。
  • 以PDF形式呈现时,以下组件不支持双字节字体。
    – <apex:pageBlock>
    – <apex:sectionHeader>
    不推荐将这些组件用于以PDF形式呈现的页面。
  • 如果<apex:dataTable>或<apex:pageBlockTable>没有呈现<apex:column>组件,则将该页呈现为PDF失败。 要解决此问题,请将表组件的呈现属性设置为false,如果未呈现其任何子<apex:column>组件。

添加样式和图像

附件还可以使用样式表来改变数据的呈现方式。 样式以与在Visualforce电子邮件模板中的方式相同的方式与内嵌代码或通过使用自定义组件相关联。
呈现为PDF的附件可以通过$ Resource全局变量引用静态资源。 这使您可以引用PDF正文中的图像或样式表。
例如,以下附件在PDF中包含徽标:

<messaging:attachment renderAs=”PDF” filename=”cases.pdf”>
<html>
<body>
<img src = “{!$Resource.logo}” />

</body>
</html>
</messaging:attachment>

这个附件引用了一个你保存为静态资源的样式表:

<messaging:attachment renderAs=”PDF”>
<html>
<link rel=’stylesheet’ type=’text/css’ href='{!$Resource.EMAILCSS}’ />
<body>

</body>
</html>
</messaging:attachment>

警告:引用远程服务器上的静态资源可能会增加呈现PDF附件所需的时间。 在Apex触发器中创建PDF附件时,不能引用远程资源; 这样做会导致例外。

Visualforce-邮件模板(2)样式

默认情况下,Visualforce电子邮件模板始终使用其他Salesforce组件的标准外观。但是,您可以通过定义自己的样式表来扩展或覆盖这些样式。
与其他Visualforce页面不同,Visualforce电子邮件模板不能使用引用的页面样式或静态资源。尽管CSS似乎在电子邮件模板预览窗格中呈现,但它与您的电子邮件的收件人不同。您必须在<style>标签内使用CSS定义您的样式。
以下示例将电子邮件的字体更改为Courier,将边框添加到表中,并更改表格行的颜色:

<messaging:emailTemplate recipientType=”Contact”
relatedToType=”Account”
subject=”Case report for Account: {!relatedTo.name}”
replyTo=”support@acme.com”>
<messaging:htmlEmailBody>
<html>
<style type=”text/css”>
body {font-family: Courier; size: 12pt;}
table {
border-width: 5px;
border-spacing: 5px;
border-style: dashed;
border-color: #FF0000;
background-color: #FFFFFF;
}
td {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #000000;
background-color: #FFEECC;
}
th {
color: #000000;
border-width: 1px ;
padding: 4px ;
border-style: solid ;
border-color: #000000;
background-color: #FFFFF0;
}
</style>
<body>
<p>Dear {!recipient.name},</p>
<table border=”0″ >
<tr>
<th>Case Number</th><th>Origin</th>
<th>Creator Email</th><th>Status</th>
</tr>
<apex:repeat var=”cx” value=”{!relatedTo.Cases}”>
<tr>
<td><a href =
“https://na1.salesforce.com/{!cx.id}”>{!cx.CaseNumber}
</a></td>
<td>{!cx.Origin}</td>
<td>{!cx.Contact.email}</td>
<td>{!cx.Status}</td>
</tr>
</apex:repeat>
</table>
</body>
</html>
</messaging:htmlEmailBody>
</messaging:emailTemplate>

在自定义组件中定义Visualforce样式表

虽然您无法在Visualforce电子邮件模板中引用外部样式表,但您可以将样式定义放置在可在其他位置引用的自定义组件中。例如,您可以修改前面的示例以将样式信息放入名为EmailStyle的组件中:

<apex:component access=”global”>
<style type=”text/css”>
body {font-family: Courier; size: 12pt;}
table {
border-width: 5px;
border-spacing: 5px;
border-style: dashed;
border-color: #FF0000;
background-color: #FFFFFF;
}
td {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #000000;
background-color: #FFEECC;
}
th {
color: #000000;
border-width: 1px ;
padding: 4px ;
border-style: solid ;
border-color: #000000;
background-color: #FFFFF0;
}
</style>
</apex:component>

然后,在Visualforce电子邮件模板中,您只能引用该组件:

<messaging:htmlEmailBody>
<html>
<c:EmailStyle />
<body>
<p>Dear {!recipient.name},</p>

</body>
</html>
</messaging:htmlEmailBody>

注意:在Visualforce电子邮件模板中使用的任何<apex:component>标记都必须具有全局访问级别。

Visualforce-邮件模板(2)样式

默认情况下,Visualforce电子邮件模板始终使用其他Salesforce组件的标准外观。但是,您可以通过定义自己的样式表来扩展或覆盖这些样式。
与其他Visualforce页面不同,Visualforce电子邮件模板不能使用引用的页面样式或静态资源。尽管CSS似乎在电子邮件模板预览窗格中呈现,但它与您的电子邮件的收件人不同。您必须在<style>标签内使用CSS定义您的样式。
以下示例将电子邮件的字体更改为Courier,将边框添加到表中,并更改表格行的颜色:

<messaging:emailTemplate recipientType=”Contact”
relatedToType=”Account”
subject=”Case report for Account: {!relatedTo.name}”
replyTo=”support@acme.com”>
<messaging:htmlEmailBody>
<html>
<style type=”text/css”>
body {font-family: Courier; size: 12pt;}
table {
border-width: 5px;
border-spacing: 5px;
border-style: dashed;
border-color: #FF0000;
background-color: #FFFFFF;
}
td {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #000000;
background-color: #FFEECC;
}
th {
color: #000000;
border-width: 1px ;
padding: 4px ;
border-style: solid ;
border-color: #000000;
background-color: #FFFFF0;
}
</style>
<body>
<p>Dear {!recipient.name},</p>
<table border=”0″ >
<tr>
<th>Case Number</th><th>Origin</th>
<th>Creator Email</th><th>Status</th>
</tr>
<apex:repeat var=”cx” value=”{!relatedTo.Cases}”>
<tr>
<td><a href =
“https://na1.salesforce.com/{!cx.id}”>{!cx.CaseNumber}
</a></td>
<td>{!cx.Origin}</td>
<td>{!cx.Contact.email}</td>
<td>{!cx.Status}</td>
</tr>
</apex:repeat>
</table>
</body>
</html>
</messaging:htmlEmailBody>
</messaging:emailTemplate>

在自定义组件中定义Visualforce样式表

虽然您无法在Visualforce电子邮件模板中引用外部样式表,但您可以将样式定义放置在可在其他位置引用的自定义组件中。例如,您可以修改前面的示例以将样式信息放入名为EmailStyle的组件中:

<apex:component access=”global”>
<style type=”text/css”>
body {font-family: Courier; size: 12pt;}
table {
border-width: 5px;
border-spacing: 5px;
border-style: dashed;
border-color: #FF0000;
background-color: #FFFFFF;
}
td {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #000000;
background-color: #FFEECC;
}
th {
color: #000000;
border-width: 1px ;
padding: 4px ;
border-style: solid ;
border-color: #000000;
background-color: #FFFFF0;
}
</style>
</apex:component>

然后,在Visualforce电子邮件模板中,您只能引用该组件:

<messaging:htmlEmailBody>
<html>
<c:EmailStyle />
<body>
<p>Dear {!recipient.name},</p>

</body>
</html>
</messaging:htmlEmailBody>

注意:在Visualforce电子邮件模板中使用的任何<apex:component>标记都必须具有全局访问级别。