Last updated on
Good to understand (week 4)
This Good to understand document provides answers to commonly asked questions.
This document does not contain new material. We encourage you to check out the table of contents and choose the sections that interest you.
Other than that, you are not required to read the entire document, as its content is already covered in the lectures, exercises or labs.
Sections marked with 📘 are alternative explanations of course material that is already covered in lectures, exercises or labs. Sections marked with 🚀 are here to satisfy your curiosity, but you will not be graded on them.
Structure of git patches 📘🚀
What is a git patch?
Patches describe a set of changes to a codebase. Git format-patch
creates a “message” out of a git commit, which is saved in a <commit_message>.patch
file.
What is the structure of such a “patch message”?
It consists of four parts:
-
Commit metadata
From <commit-hash> Mon Sep 17 00:00:00 2001 From: <user.name> <user.email> Date: <date> Subject: [PATCH] <first paragraph of commit message>
-
Rest of commit message
Remaining paragraphs of the commit message, followed by a blank line, followed by a separator line with just the text
---
. -
Summary of changes
A short summary of changes in each file:
<name_of_file> | <number_of_changed_lines> <a_sequence_of_+_and_-_representing_each_line_change>
A short summary of all changes:
<number_of_changes> files changed, <number_of_additions> insertions(+), <number_of_deletions> deletions(-)
-
Changes for each file
This is the actual “patch” and the only part required for
git apply
to work. It has the following structure:diff --git a/<path_before_rename> b/<path_after_rename> index <hash_before_change>..<hash_after_change> <file_mode> // optional --- a/<path_before_changes> +++ b/<path_after_changes> <chunks>
When a new file is created, git will use
/dev/null
fora/<path_before_changes>
. Additionally, file mode, representing its UNIX-style permissions, will be recorded.diff --git a/<path> b/<path> index <hash_before_change>..<hash_after_change> <file_mode> // optional new file mode <file_mode> --- /dev/null +++ b/<path> <chunks>
Similarly, if a file was deleted,
/dev/null
will be used forb/<path_before_changes>
, and the file mode will also be included.Each chunk (visible as
<chunks>
above) describes a change to a file fragment. It starts with ranges that place it in the file, and continues with some number of lines of code. Each line is prepended by either “+” denoting a line added by the change, “-” denoting a line removed by the change, or " " denoting an unchanged line.@@ -<range_before_change> +<range_after_change> @@ - Removed line + Added line Unchanged line
Where a range is a pair of start and end lines separated by a comma, e.g.
4,9
.By default,
git
includes three unmodified lines before and after the changes, but this can be modified using the--unified
option.
Exercise
Setup
Let’s say I created a simple project with the following structure:
|- src/main/scala/example
|- Hello.scala
|- build.sbt
|- .gitignore
Where Hello.scala
has the following code:
package example
object Hello:
def main(args: Array[String]) =
println("Hello, world!")
2025-10-06/code/git-patch/HelloBeforePatch.scala
I committed all my changes, my working tree is clean.
Changes
Now I made some changes to my project. I created a new file in src/main/scala/example
called Greetings.scala
with the following content:
package example
object Greetings:
val helloWorld = "Hello, world!"
2025-10-06/code/git-patch/Greetings.scala
And I adjusted the Hello
file to use Greetings
, so now it looks as follows:
package example
import Greetings.helloWorld
object Hello:
def main(args: Array[String]) =
println(helloWorld)
2025-10-06/code/git-patch/Hello.scala
Can you manually create a git patch out of my changes?
If you also want to reproduce the message, here is some additional information:
- by running
git log -1
I learned that the commit hash is3bbb69a256a6f2b0ef97de5d0dd0893622cfa3cf
and I made the commit on October 2, 2025, at 13:49:43 (Swiss time); - by running
git config user.name
andgit config user.email
I learned my username to be “Small Cat” and my email to be “a_very_small_cat@inde.ed”.
Answer
diff --git a/src/main/scala/example/Greetings.scala b/src/main/scala/example/Greetings.scala
new file mode 100644
--- /dev/null
+++ b/src/main/scala/example/Greetings.scala
@@ -0,0 +1,4 @@
+package example
+
+object Greetings:
+ val helloWorld = "Hello, world!"
diff --git a/src/main/scala/example/Hello.scala b/src/main/scala/example/Hello.scala
--- a/src/main/scala/example/Hello.scala
+++ b/src/main/scala/example/Hello.scala
@@ -1,5 +1,6 @@
package example
+import Greetings.helloWorld
object Hello:
def main(args: Array[String]) =
- println("Hello, world")
+ println(helloWorld)
The actual git .patch
file created by format-patch
.
From 3bbb69a256a6f2b0ef97de5d0dd0893622cfa3cf Mon Sep 17 00:00:00 2001
From: Small Cat <a_very_small_cat@inde.ed>
Date: Thu, 2 Oct 2025 13:49:43 +0200
Subject: [PATCH] refactor: extract greeting to a new file
---
src/main/scala/example/Greetings.scala | 4 ++++
src/main/scala/example/Hello.scala | 3 ++-
2 files changed, 6 insertions(+), 1 deletion(-)
create mode 100644 src/main/scala/example/Greetings.scala
diff --git a/src/main/scala/example/Greetings.scala b/src/main/scala/example/Greetings.scala
new file mode 100644
index 0000000..2cae520
--- /dev/null
+++ b/src/main/scala/example/Greetings.scala
@@ -0,0 +1,4 @@
+package example
+
+object Greetings:
+ val helloWorld = "Hello, world!"
diff --git a/src/main/scala/example/Hello.scala b/src/main/scala/example/Hello.scala
index 71418e5..1503d26 100644
--- a/src/main/scala/example/Hello.scala
+++ b/src/main/scala/example/Hello.scala
@@ -1,5 +1,6 @@
package example
+import Greetings.helloWorld
object Hello:
def main(args: Array[String]) =
- println("Hello, world")
+ println(helloWorld)
--
2.39.3 (Apple Git-145)
Can I pass a tuple of n
elements instead of n
arguments? 🚀
The tupled
function
You can turn a function that takes n
parameters into a function that takes a tuple of length n
using the tupled
function. For example:
def myFunction(x: Int, y: String, z: Int): Int
2025-10-06/code/tupled.worksheet.sc
myFunction//: (x: Int, y: String, z: Int) => Int
myFunction(1, "aaa", 3) // myFunction takes 3 arguments
myFunction.tupled //: (t: (Int, String, Int)) => Int
val iAmATuple = (1, "aaa", 3)
myFunction.tupled(iAmATuple) // myFunction.tupled takes 1 argument that is a tuple of 3 elements
2025-10-06/code/tupled.worksheet.sc
Example
Let’s say that given a function addMod3
we want to implement addPairsModulo3
.
def addMod3(z: Int, y: Int) = (z + y) % 3
def addPairsModulo3(l: List[(Int, Int)]): List[Int] =
???
2025-10-06/code/tupled.worksheet.sc
For each pair of integers in its input list, its output list should contain their sum modulo 3.
For example, given a list with elements (1, 2)
, (0, 2)
, and (0, 0)
, its output should be the list containing 0
, 2
, and 0
.
Do you know how to do it? (You can use any usual functions on lists known from the course.)
Answer
def addPairsModulo3(l: List[(Int, Int)]): List[Int] =
l.map(addMod3.tupled)
2025-10-06/code/tupled.worksheet.sc