Lab 4:Git 与调试
目录#
Lab 前准备#
- **暂时不要从 skeleton 仓库拉取代码。**从 skeleton 仓库拉取会产生合并冲突,你稍后会在本 Lab 中学习怎样修复。即使你已经知道怎样解决合并冲突,我们也希望你使用一种非常具体的方式进行修复,所以请不要忍不住现在就 pull!
- 如果已经从 skeleton 拉取过代码,请联系你的 TA,询问如何继续完成本 Lab。
- 本 Lab 要求你已经在 GS 上的
lab1中获得满分。如果尚未满分,请在继续之前联系 TA。 - 本 Lab 将使用 Git。开始之前,先确保你的仓库已与 GitHub 同步。只需运行:
git push origin masterbash如果命令成功,就可以继续!如果没有成功,你很可能看到了类似这个错误 ↗的消息。请按照链接中的说明修复错误。
简介#
由于这是期中考试周,本 Lab 会比平常短一些。你将在本 Lab 中更深入地学习 Git,并通过一个很酷的练习再次练习调试。完成本 Lab 后,你应当会对 Git 工作流更加熟练,调试技能也应当更加精细!
Git 背景知识#
这一部分需要观看一系列讲解 Git 主要概念的视频,然后亲手练习使用 Git。请观看下面全部六个视频。众所周知,Itai 说话比较慢,所以需要时可以随意提高播放速度!
- Git Intro - Part 1 ↗
- Git Intro - Part 2 ↗
- Git Intro - Part 3 ↗
- Git Intro - Part 4 ↗
- Git Intro - Part 5 ↗
- Git Intro - Part 6 ↗
具体来说,看完视频后,你应当理解以下概念:
- 本地 Git 工作流:
git add和git commit; - 使用
git checkout在不同提交之间移动并更新文件; - Detached
HEAD状态; - 远程仓库,例如托管在 GitHub ↗ 上的仓库;
- 本地 Git 与远程仓库的集成:
origin/master和skeleton/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 masterbash我们故意在你的 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(它会按顺序列出当前提交之前的提交),你应该会看到类似下面的内容:
commit 8f0deeaef048f33a209f6f2fe5927a6fb04cc6cc
Merge: 225d73e 7aa1b6f
Author: Neil Kulkarni <neil.kulkarni@berkeley.edu>
Date: Sun Feb 7 14:36:52 2021 -0700
Merge branch 'master' of https://github.com/Berkeley-CS61B/skeleton-sp21
Fixed the merge conflict in lab4 to contain buggy Collatz!
commit 7aa1b6fc79cb752e1ed844cd9cdd8c9c21e7f3d4 (HEAD -> master)
Author: Neil Kulkarni <neil.kulkarni@berkeley.edu>
Date: Sun Feb 7 14:26:58 2021 -0700
Added Lab 4 Starter Files
...
commit 4050fd80377d85aaea6c7cdb486e581d8c422534
Author: Neil Kulkarni <neil.kulkarni@berkeley.edu>
Date: Sat Jan 30 22:56:58 2021 -0800
Finished Lab 1! Collatz works!
...text也就是说,你应当看到最近的提交是解决合并冲突后产生的合并提交,第二新的提交是 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 cleantext请记住,在 Detached HEAD 状态中,你可以自由查看当前提交,但不应当进行任何更改。让我们看看 lab1/Collatz.java。你应当会看到自己的旧解法!可以用任何喜欢的方式打开这个文件,但我推荐使用终端命令 cat,它会打印文件内容:
NeilKulkarni@Neils-MacBook-Pro sp21-s58 % cat lab1/Collatz.java
/** Class that prints the Collatz sequence starting from a given number.
* @author Neil Kulkarni
*/
public class Collatz {
public static int nextNumber(int n) {
return n % 2 == 0 ? n/2 : 3*n + 1;
}
public static void main(String[] args) {
int n = 5;
System.out.print(n + " ");
while (n != 1) {
n = nextNumber(n);
System.out.print(n + " ");
}
}
}java第 4 步#
现在,checkout 到最近的提交,以离开 Detached HEAD 状态。你应当验证:由于我们已经回到最近的提交,lab1/Collatz.java 再次包含有 bug 的实现:
NeilKulkarni@Neils-MacBook-Pro sp21-s58 % cat lab1/Collatz.java
/** Class that prints the Collatz sequence starting from a given number.
* @author YOUR NAME HERE
*/
public class Collatz {
/** 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;
}
}
public static void main(String[] args) {
int n = 5;
System.out.print(n + " ");
while (n != 1) {
n = nextNumber(n);
System.out.print(n + " ");
}
System.out.println();
}
}java第 5 步#
我们已经验证 lab1commit 中包含正确的 Collatz.java 内容,也已经回到包含有 bug 的 Collatz.java 实现的最近提交。现在,使用 git checkout 把 lab1/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.javatext再执行一次 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.javabash可以自由尝试这条命令!一般来说,git checkout skeleton/master -- <file> 会把文件恢复为其骨架代码状态。请记住,你已经在 proj0 的开发过程中创建了提交,因此总是可以把 Model.java 恢复到任何曾经提交过的状态(包括项目完成时的状态)!如果想把 Model.java 恢复为最近一次提交时(也就是完成后的)状态,可以运行下面的命令:
git checkout master -- proj0/game2048/Model.javabash一般来说,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 进行调试。