Everlasting Pages

返回

Lab 4:Git 与调试

目录#

Lab 前准备#

  • **暂时不要从 skeleton 仓库拉取代码。**从 skeleton 仓库拉取会产生合并冲突,你稍后会在本 Lab 中学习怎样修复。即使你已经知道怎样解决合并冲突,我们也希望你使用一种非常具体的方式进行修复,所以请不要忍不住现在就 pull!
  • 如果已经从 skeleton 拉取过代码,请联系你的 TA,询问如何继续完成本 Lab。
  • 本 Lab 要求你已经在 GS 上的 lab1 中获得满分。如果尚未满分,请在继续之前联系 TA。
  • 本 Lab 将使用 Git。开始之前,先确保你的仓库已与 GitHub 同步。只需运行:
git push origin master
bash

如果命令成功,就可以继续!如果没有成功,你很可能看到了类似这个错误的消息。请按照链接中的说明修复错误。

简介#

由于这是期中考试周,本 Lab 会比平常短一些。你将在本 Lab 中更深入地学习 Git,并通过一个很酷的练习再次练习调试。完成本 Lab 后,你应当会对 Git 工作流更加熟练,调试技能也应当更加精细!

Git 背景知识#

这一部分需要观看一系列讲解 Git 主要概念的视频,然后亲手练习使用 Git。请观看下面全部六个视频。众所周知,Itai 说话比较慢,所以需要时可以随意提高播放速度!

具体来说,看完视频后,你应当理解以下概念:

  • 本地 Git 工作流:git addgit commit
  • 使用 git checkout 在不同提交之间移动并更新文件;
  • Detached HEAD 状态;
  • 远程仓库,例如托管在 GitHub 上的仓库;
  • 本地 Git 与远程仓库的集成:origin/masterskeleton/master
  • 怎样解决合并冲突。

如果对上述概念有任何疑问,可以参考 Sarah 的 Git 指南Git WTFS(Git Weird Technical Failure Scenarios——别想歪了!),也可以随时请 TA 解释这些概念。

我们要在这里警告你:对网上找到的 Git 信息保持警惕,因为它们并不都来自可信来源。另外,当你在 Git 上遇到困难时,务必绝不要复制网上找到的命令或朋友发给你的命令——有疑问时,一定要询问 TA。

Git 练习#

现在该练习刚刚学到的内容了。请记住,本 Lab 要求你已经在 Gradescope 的 Lab 1 Autograder 中获得满分。如果尚未完成 Lab 1,请咨询 TA。准备好后,请使用下面的命令拉取起始代码;出现合并冲突时不要惊慌!请继续阅读以了解更多信息。

git pull skeleton master
bash

我们故意在你的 lab1 目录中引入了一个合并冲突。这个练习的目标是让你练习几个 Git 概念,其中包括合并冲突、Detached HEAD 状态,以及在你的 sp21-s*** 仓库中 checkout 文件。完成这个练习后,你应当会更熟悉在命令行中沿提交树移动,从而让文件保持在期望状态。

lab1 目录中的合并冲突位于 lab1/Collatz.java 文件。你在 Lab 1 中更新过这个文件,让它打印从 n = 5 开始的 Collatz 序列。在拉取 lab4 起始代码之前,你的解法能够正确打印 Collatz 序列。skeleton 仓库后来被更新,其中包含下面这个有 bug 的 nextNumber(int n) 方法实现;该方法应当返回 Collatz 序列中 n 之后的下一个数:

/** Buggy implementation of nextNumber! */
public static int nextNumber(int n) {
    if (n  == 128) {
        return 1;
    } else if (n == 5) {
        return 3 * n + 1;
    } else {
        return n * 2;
    }
}
java

在我们走得太快之前,请注意:如果完成作业期间的任何时刻遇到困难,务必询问 TA。出现问题时越早让他们参与,越容易帮助你恢复。你的任务如下。

第 1 步#

解决合并冲突,并确保冲突解决后的结果包含这个有 bug 的方法版本。换句话说,完成合并冲突处理之后,Collatz.java 应当能够编译,并且包含有 bug 的 nextNumber 实现。

现在,如果运行 git log(它会按顺序列出当前提交之前的提交),你应该会看到类似下面的内容:

也就是说,你应当看到最近的提交是解决合并冲突后产生的合并提交,第二新的提交是 Neil 添加 Lab 4 起始文件的提交,也就是刚刚从 skeleton 拉取的那个提交。你可能需要向前翻很久,才能看到完成 Lab 1 时创建的提交。例如,我完成 Lab 1 时创建的提交消息是 “Finished Lab 1! Collatz works!”。

你的提交很可能具有不同的提交消息,也应当拥有不同的提交哈希、作者和日期。记下你完成 Lab 1 时那个提交的提交哈希。我们把这个提交称为 lab1commit,以便在之后的步骤中引用它。

第 2 步#

在 Gradescope 上提交到 “Lab 4A: Git Exercise Part A” 自动评分器。它会验证你是否正确解决了合并冲突,使 Collatz.java 包含有 bug 的 nextNumber 实现。

第 3 步#

尽管最近的提交在 lab1/Collatz.java 中包含一个 bug,幸运的是,你曾经在某个时刻提交过正确的 Collatz 实现,也就是 lab1commit!由于 Git 中的提交是文件状态的快照,lab1commit 保存了正确版本 Collatz 的快照。不相信?让我们看一看!

下一步是 checkout 到 lab1commit。如果不记得这条命令的语法,请查看上面链接的资料。到达该提交后,输入 git status。你应当会看到自己处于 Detached HEAD 状态。如果不记得这是什么,请查看上面的资料!

NeilKulkarni@Neils-MacBook-Pro sp21-s58 % git status
HEAD detached at 4050fd8
nothing to commit, working tree clean
text

请记住,在 Detached HEAD 状态中,你可以自由查看当前提交,但不应当进行任何更改。让我们看看 lab1/Collatz.java。你应当会看到自己的旧解法!可以用任何喜欢的方式打开这个文件,但我推荐使用终端命令 cat,它会打印文件内容:

第 4 步#

现在,checkout 到最近的提交,以离开 Detached HEAD 状态。你应当验证:由于我们已经回到最近的提交,lab1/Collatz.java 再次包含有 bug 的实现:

第 5 步#

我们已经验证 lab1commit 中包含正确的 Collatz.java 内容,也已经回到包含有 bug 的 Collatz.java 实现的最近提交。现在,使用 git checkoutlab1/Collatz.java 恢复为它在 lab1commit 中的状态。如果忘记怎样 checkout 文件,请复习本 Lab 开头的资料!

当你 checkout 一个文件时,Git 会自动把该文件加入暂存区。checkout 之后立刻运行 git status,应当返回类似下面的内容:

NeilKulkarni@Neils-MacBook-Pro sp21-s58 % git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   lab1/Collatz.java
text

再执行一次 cat lab1/Collatz.java。你应当会看到解法现在已经更新!接下来提交并推送更改。

第 6 步#

提交到 Lab 4B: Git Exercise Part B 自动评分器,以获得这项作业的分数。

最后说几句:我们刚刚完成的操作非常强大。我们利用了 Git 会存储文件快照这一事实,并用它恢复到更早的版本!而且在这个过程中,我们还学习了怎样解决合并冲突!

这些信息不只是理论。当你完成项目时,会发现它确实具有实际用途。正如 Hug 教授经常说的那样,在项目中从头重新开始完全没有问题(而且受到鼓励!)。现在,我们拥有了做到这一点的手段!可以使用 git checkout 命令,把项目文件 checkout 为更早的某个提交中、尚未对它们进行修改时的版本。具体来说,如果想从骨架版本重新开始某个项目文件(例如 Project 0 的 Model.java 文件),可以运行下面的命令:

git checkout skeleton/master -- proj0/game2048/Model.java
bash

可以自由尝试这条命令!一般来说,git checkout skeleton/master -- <file> 会把文件恢复为其骨架代码状态。请记住,你已经在 proj0 的开发过程中创建了提交,因此总是可以把 Model.java 恢复到任何曾经提交过的状态(包括项目完成时的状态)!如果想把 Model.java 恢复为最近一次提交时(也就是完成后的)状态,可以运行下面的命令:

git checkout master -- proj0/game2048/Model.java
bash

一般来说,git checkout master -- <file> 会把文件恢复为最近提交中的状态。现在,你已经知道怎样重新开始一个项目;如果对重新开始的结果不满意,也知道怎样把重新开始后的项目恢复到它在 master 中的状态!

一个调试谜题#

另一项需要学习的重要技能是怎样进行彻底的调试。正确进行调试时,即使你并不完全理解正在调试的代码,也应当能够迅速缩小 bug 可能位于何处的范围。请考虑下面的场景。

你的公司 Flik Enterprises 发布了一个优秀的软件库 Flik.java,它能够判断两个 Integer 是否相同。

你收到了一封来自 “Horrible Steve” 的电子邮件,其中描述了他在使用这个库时遇到的问题:

"Dear Flik Enterprises,

Your library is very bad. See the attached code. It should print out 500
but actually it's printing out 128.

(attachment: HorribleSteve.java)"
text

使用下面任意几种技术的组合,判断 bug 位于 Horrible Steve 的代码中,还是位于 Flik Enterprises 的库中:

  • 为 Flik 库编写 JUnit 测试。如果想这样做,需要在 flik 目录中新建一个文件,并导入 junit。请参考前面问题中的测试,了解怎样完成。
  • 使用 IntelliJ 调试器,特别是条件断点遇到异常时中断,这些内容在 Lab 3 中已经学过!
  • 使用打印语句。
  • 重构 Horrible Steve 的代码。重构意味着改变语法而不改变功能。由于 HS 的代码使用了很多奇怪的东西,这可能很难完成。

找到 bug 后,修复它,并把代码提交到 Lab 4: Debugging 自动评分器。本部分自动评分器使用隐藏测试,因此无法从 AG 获得任何关于 bug 的信息。如果认为已经修复 bug,却仍然无法通过 AG,请咨询 TA。

提示:JUnit 提供了 assertTrue(boolean)assertTrue(String, boolean) 方法,它们可能会很有帮助。

尝试对这个 bug 给出简短解释!由于 Lecture 中没有讲过这个确切的问题,Google 是你的朋友。和 Lab 搭档讨论,并向 TA 或 AI 确认答案是否正确(不计分)。

提交#

这份 Lab 作业有三个独立的自动评分器。Lab 4A: Git Exercise Part A 和 Lab 4B: Git Exercise Part B 用来确认你已经完成 Git 练习;Lab 4: Debugging 用来确认你找到了并修复 “A Debugging Mystery” 中的 bug。这个 AG 还会检查代码风格,因此请确保每个文件都通过风格检查!请记住,可以通过右键单击文件并选择 Check Style 来检查风格。

有关每个 AG 应当在什么时候提交的更多信息,请查看规格中对应的部分。

延期申请:共有三个自动评分器(Lab 4、Lab 4A 和 Lab 4B)。在 Beacon 上,如果为 Lab 4 提交延期申请,它会同时应用于三个自动评分器。

完整回顾#

本 Lab 介绍了:

  • Git 基础;
  • 合并冲突;
  • Detached HEAD 状态;
  • 使用 Git checkout 代码;
  • 使用 JUnit 进行调试。

原始页面:https://sp21.datastructur.es/materials/lab/lab4/lab4