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:

  1. 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>
    
  2. Rest of commit message

    Remaining paragraphs of the commit message, followed by a blank line, followed by a separator line with just the text ---.

  3. 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(-)
    
  4. 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 for a/<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 for b/<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:

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