Last updated on

Below is an unedited transcript of the Git command line session that I presented in class in 2023; I have simply added commentary between the individual commands. I kept the mistakes unedited to match exactly what happened in class. The 2024 demo followed roughly the same plan.

I started at an empty command line, running GNU Bash on Ubuntu:

~/git/cs214 $

(The part before and including the $ sign is called the “prompt”. Here I use it to display the current directory.) I wanted to demo a new Scala project, so I created a directory with mkdir:

~/git/cs214 $ mkdir demo

… but immediately after I realized that I actually didn’t need that directory, because sbt new (the sbt command to make a new project) already creates a directory. So, I tried to delete the directory I had just created:

~/git/cs214 $ rm demo
rm: cannot remove 'demo': Is a directory

That didn’t work, because rm is for files. For directories the right command is rm -r, or rmdir… and I mistyped that too, forgetting the name of the directory:

~/git/cs214 $ rmdir
rmdir: missing operand
Try 'rmdir --help' for more information.

Finally I managed to get rid of demo/:

~/git/cs214 $ rmdir demo

After that, I showed how to get help on an sbt command, using sbt help:

~/git/cs214 $ sbt help init
new [--options] <template>
  Create a new sbt build based on the given template.
  sbt provides out-of-the-box support for Giter8 templates. See foundweekends.org/giter8/ for details.

Example: sbt new scala/scala-seed.g8

Running sbt new creates a directory and a fresh Scala project from a template (scala/scala3.g8 is the name of the template):

~/git/cs214 $ sbt new scala/scala3.g8
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
A template to demonstrate a minimal Scala 3 application

name [Scala 3 Project Template]: demo
Template applied in /home/cpitclaudel/git/cs214/./demo

The warning relates to a logging component, and reading the link suggests I don’t have to worry about it.

Then I checked on what sbt new created, which showed a new demo folder:

~/git/cs214 $ ls
backup  cs214-lectures  demo

So I navigated to it using cd:

~/git/cs214 $ cd demo

And I inspected its contents with ls:

~/git/cs214/demo $ ls
build.sbt  project  README.md  src

This is when the Git part of the demo began.

I wanted to track my progress with git, so I useed git init to create a repo. And with ls -a and ls .git I confirmed that a new .git hidden folder was indeed created.

~/git/cs214/demo $ git init
Initialized empty Git repository in /home/cpitclaudel/git/cs214/demo/.git/
~/git/cs214/demo $ ls -a
.  ..  build.sbt  .git  .gitignore  project  README.md  src
~/git/cs214/demo $ ls .git
branches  config  description  HEAD  hooks  info  objects  refs

Another check of the directory contents with ls:

~/git/cs214/demo $ ls
build.sbt  project  README.md  src

By typing git and pressing TAB, I got autocompletion for all available commands. If you want to learn more about any of them, for example restore, then you can use git help [command], for example git help restore.

~/git/cs214/demo $ git
add               citool            gc                mergetool         replace           stage
am                clean             gitk              mv                request-pull      stash
apply             clone             grep              notes             reset             status
archive           commit            gui               prune             restore           submodule
bisect            config            help              pull              revert            switch
blame             describe          init              push              rm                tag
branch            diff              instaweb          range-diff        send-email        whatchanged
bundle            difftool          latexdiff         rebase            shortlog          worktree
checkout          fetch             log               reflog            show
cherry            format-patch      maintenance       remote            show-branch
cherry-pick       fsck              merge             repack            sparse-checkout

The first important command is git status, which tells me what I have in my history. Here, the message indicated that I had multiple files whose history I was not recording yet (“untracked”):

~/git/cs214/demo $ git status
On branch main

No commits yet
Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore README.md build.sbt project/ src/
nothing added to commit but untracked files present (use "git add" to track)

Git has three places where it tracks information: the workspace, the history, and the “index”:

The index is a very convenient concept: it lets you collect changes to multiple files into a consistent changeset step by step, by running the git add command. So, when I want git to record a collection of changes (additions, deletions, or modifications), I use git add on each of the modified files (or even on sections of these files—I can select just a few modified lines!), and finally when I have everything ready I use git commit to give a description of the changes that are currently waiting in the index and add them to the history as a new commit.

Here, I used git add -A to add all untracked files at once. This is the same as git add ., which tells git to “stage” everything into the index.

~/git/cs214/demo $ git add -A

After this command, the index matched my workspace exactly, and hence git status now reported that I had new files (staged “changed to be committed”):

~/git/cs214/demo $ git status
On branch main

No commits yet
Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: .gitignore new file: README.md new file: build.sbt new file: project/build.properties new file: src/main/scala/Main.scala new file: src/test/scala/MySuite.scala

I mentioned earlier that git help is a good way to get help on Git commands. In Unix-like environments (macOS, GNU/Linux, WSL), the man (for “manual”) command can also be used to browse documentation (at this point I demoed it because I was asked about git add . vs git add -A). I said I was 60% sure of the answer, and it turned out I was wrong (but the text above is correct).

~/git/cs214/demo $ man git-add

My files are still unchanged:

~/git/cs214/demo $ ls
build.sbt  project  README.md  src
~/git/cs214/demo $ git status
On branch main

No commits yet
Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: .gitignore new file: README.md new file: build.sbt new file: project/build.properties new file: src/main/scala/Main.scala new file: src/test/scala/MySuite.scala

Now I’m ready for my first commit! Here I used the command without additional arguments, which opened a text editor in which I typed my “commit message”, which is a summary of my changes (“Create new project with SBT”).

~/git/cs214/demo $ git commit
[main (root-commit) 12a3a4a] Create new project with SBT
 6 files changed, 67 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 README.md
 create mode 100644 build.sbt
 create mode 100644 project/build.properties
 create mode 100644 src/main/scala/Main.scala
 create mode 100644 src/test/scala/MySuite.scala

After a commit, Git reports that I do not have changes (“working tree” is the same as “workspace”, and “clean” means no changes):

~/git/cs214/demo $ git status
On branch main
nothing to commit, working tree clean

At this point I opened VSCode to write some code:

~/git/cs214/demo $ code .

… and after making the changes and saving I could see that I had modified Main.scala:

~/git/cs214/demo $ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   src/main/scala/Main.scala

no changes added to commit (use "git add" and/or "git commit -a")

I then staged Main.scala:

~/git/cs214/demo $ git add src/main/scala/Main.scala

And created a new commit, but this time I used -m to give the commit message directly instead of opening a text editor.

~/git/cs214/demo $ git commit -m "Say hello to EPFL, not to the world"
[main a4a9c57] Say hello to EPFL, not to the world
 1 file changed, 1 insertion(+), 3 deletions(-)

Now that I had multiple commits, I could inspect the history of my project, using git log:

~/git/cs214/demo $ git log
commit a4a9c57b99279f66e831530f7397e98005bf1852 (HEAD -> main)
Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch>
Date:   Wed Sep 27 15:44:36 2023 +0200

Say hello to EPFL, not to the world
commit 12a3a4a9592d8c3f3f0d0b88a379e0e0125c43d6 Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch> Date: Wed Sep 27 15:42:21 2023 +0200
Create new project with SBT

By adding -p to git log I got “diffs” in addition to commit messages (a “diff” is a comparison of the contents of two files). Red means removed, and green means added:

~/git/cs214/demo $ git log -p
commit a4a9c57b99279f66e831530f7397e98005bf1852 (HEAD -> main)
Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch>
Date:   Wed Sep 27 15:44:36 2023 +0200

Say hello to EPFL, not to the world
diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index a0c585f..5319870 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -1,5 +1,3 @@ @main def hello: Unit = - println("Hello world!") - println(msg) + println("Hello EPFL")
-def msg = "I was compiled by Scala 3. :)"
commit 12a3a4a9592d8c3f3f0d0b88a379e0e0125c43d6 Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch> Date: Wed Sep 27 15:42:21 2023 +0200
Create new project with SBT

At this point I chose to compile and run my code…

~/git/cs214/demo $ sbt run
[info] welcome to sbt 1.9.6 (Ubuntu Java 11.0.20.1)
[info] loading settings for project demo-build-build from metals.sbt ...
[info] loading project definition from /home/cpitclaudel/git/cs214/demo/project/project
[info] loading settings for project demo-build from metals.sbt ...
[info] loading project definition from /home/cpitclaudel/git/cs214/demo/project
[success] Generated .bloop/demo-build.json
[success] Total time: 1 s, completed Sep 27, 2023, 3:45:24 PM
[info] loading settings for project root from build.sbt ...
[info] set current project to demo (in build file:/home/cpitclaudel/git/cs214/demo/)
[info] compiling 1 Scala source to /home/cpitclaudel/git/cs214/demo/target/scala-3.3.1/classes ...
[info] running hello
Hello EPFL
[success] Total time: 3 s, completed Sep 27, 2023, 3:45:28 PM

… but I was disappointed: I forgot the “!” at the end of the message! So I went back to my existing VSCode window and modified the code further:

~/git/cs214/demo $ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   src/main/scala/Main.scala

no changes added to commit (use "git add" and/or "git commit -a")

I then used git diff to confirm the changes I had made:

~/git/cs214/demo $ git diff
diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala
index 5319870..b77e767 100644
--- a/src/main/scala/Main.scala
+++ b/src/main/scala/Main.scala
@@ -1,3 +1,3 @@
 @main def hello: Unit =
-  println("Hello EPFL")
+  println("Hello EPFL!")

git add to stage them into the index:

~/git/cs214/demo $ git add src/main/scala/Main.scala

… and git commit to create a snapshot… except that I decided, rather than create an additional entry in the history, to rewrite the previous history entry by pretending I got the “!” right from the start. That’s what the --amend is for: it combines my stages changes into the most recent changeset/commit rather than create a new one (note that I also added a “!” to the amended commit message).

~/git/cs214/demo $ git commit --amend
[main 56e8387] Say hello to EPFL, not to the world!
 Date: Wed Sep 27 15:44:36 2023 +0200
 1 file changed, 1 insertion(+), 3 deletions(-)

I then checked git log to make sure that I had indeed modified the most recent commit:

~/git/cs214/demo $ git log
commit 56e83877f6c28aeede83335d7a2deb5d59282402 (HEAD -> main)
Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch>
Date:   Wed Sep 27 15:44:36 2023 +0200

Say hello to EPFL, not to the world!
commit 12a3a4a9592d8c3f3f0d0b88a379e0e0125c43d6 Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch> Date: Wed Sep 27 15:42:21 2023 +0200
Create new project with SBT

Notice the long yellow identifiers above (hexadecimal, next to the word “commit”): these are commit hashes, used to identify specific commits in the history of your project unambiguously. Using git show, I can see a commit by its id (and since I picked the initial commit, the diff is just a long list of additions):

~/git/cs214/demo $ git show 12a3a4a9592d8c3f3f0d0b88a379e0e0125c43d6
commit 12a3a4a9592d8c3f3f0d0b88a379e0e0125c43d6
Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch>
Date:   Wed Sep 27 15:42:21 2023 +0200

Create new project with SBT
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e79245 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# macOS +.DS_Store + +# sbt specific +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ +project/local-plugins.sbt +.history +.ensime +.ensime_cache/ +.sbt-scripted/ +local.sbt + +# Bloop +.bsp + +# VS Code +.vscode/ + +# Metals +.bloop/ +.metals/ +metals.sbt + +# IDEA +.idea +.idea_modules +/.worksheet/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..102c5ca --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +## sbt project compiled with Scala 3 + +### Usage + +This is a normal sbt project. You can compile code with `sbt compile`, run it with `sbt run`, and `sbt console` will start a Scala 3 REPL. + +For more information on the sbt-dotty plugin, see the +[scala3-example-project](https://github.com/scala/scala3-example-project/blob/main/README.md). diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..630e51d --- /dev/null +++ b/build.sbt @@ -0,0 +1,12 @@ +val scala3Version = "3.3.1" + +lazy val root = project + .in(file(".")) + .settings( + name := "demo", + version := "0.1.0-SNAPSHOT", + + scalaVersion := scala3Version, + commit 12a3a4a9592d8c3f3f0d0b88a379e0e0125c43d6 Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch> Date: Wed Sep 27 15:42:21 2023 +0200
Create new project with SBT
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e79245 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# macOS +.DS_Store + +# sbt specific +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ +project/local-plugins.sbt +.history

I felt happy with my code, so I “tagged” it, which is a way to give a human-readable name to a specific commit (so people can refer to v1.0 instead of 56e83877f6c28aeede83335d7a2deb5d59282402):

~/git/cs214/demo $ git tag -m "Release my beautiful new software" v1.0

… and now we see the tag in the log (look for v1.0).

~/git/cs214/demo $ git log
commit 56e83877f6c28aeede83335d7a2deb5d59282402 (HEAD -> main, tag: v1.0)
Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch>
Date:   Wed Sep 27 15:44:36 2023 +0200

Say hello to EPFL, not to the world!
commit 12a3a4a9592d8c3f3f0d0b88a379e0e0125c43d6 Author: Clément Pit-Claudel <clement.pit-claudel@epfl.ch> Date: Wed Sep 27 15:42:21 2023 +0200
Create new project with SBT

Finally, I demonstrated how to make a patch. A patch is a self-contained textual representation of a changeset. Traditional patches only include position markers and +/- pairs; Git patches additionally have a header that includes the author’s name, the authoring date, and the commit message. Here, I used VSCode again to make one more change, then staged it and committed it:

~/git/cs214/demo $ code .
~/git/cs214/demo $ git add src/main/scala/Main.scala
~/git/cs214/demo $ git commit -m "Say hi to just CS214 folks"
[main 12d6eef] Say hi to just CS214 folks
 1 file changed, 1 insertion(+), 2 deletions(-)

And after that, I exported the commit to a patch using the git format-patch HEAD~1 command. HEAD is the commit that my repository is currently focused on, and HEAD~1 is the previous commit, so the command below asks Git to export only the latest changeset/commit (from HEAD~1 to HEAD) to a textual patch. Git prints the name of the result, which I then opened in vscode to look at it.

~/git/cs214/demo $ git format-patch HEAD~1
0001-Say-hi-to-just-CS214-folks.patch
~/git/cs214/demo $ code 0001-Say-hi-to-just-CS214-folks.patch

That was the end of the first part, on Git basics. In the second part, I showed some advanced history-browsing techniques. Try to explore them on your own to familiarize yourself with your tools!


For this part, to save time, I used Git repositories that I had already cloned. As a result I didn’t get to demonstrate the git clone command (which you all have been using for the labs):

~/git/cs214/demo $ ls
0001-Say-hi-to-just-CS214-folks.patch  build.sbt  project  README.md  src  target
~/git/cs214/demo $ cd ..

Here I pressed tab to show completions, then navigated to the vscode repo:

~/git/cs214 $ cd
backup/         cs214-lectures/ demo/
~/git/cs214 $ cd backup/
~/git/cs214/backup $ ls
demo  dotty  find  find-2  vscode
~/git/cs214/backup $ cd vscode

The git shortlog command shows me a summary of changes, and I can pair it with the --since flag to filter by date. Below I’ve truncated the list, since it was quite long:

~/git/cs214/backup/vscode $ git shortlog --since=2023-09-20
Aaron Munger (14):
      dont remove decorations added by other entries
      Merge pull request #193622 from microsoft/aamunger/symbolsDecorations
      skip flaky test
      Merge pull request #193729 from microsoft/aamunger/flakyTest
      Merge pull request #193810 from microsoft/revert-192602-ai-codefixes
      anchor to focused cell setting
      fix anchor range check
      spelling
      Merge pull request #193833 from microsoft/aamunger/anchorFocusedCell
      resolve cell text models so that we get the symbols even for cells outside the view
      update test
      Merge pull request #193854 from microsoft/aamunger/notebookSymbols
      highlight symbol row in notebooks (#193845)
      revert default behavior back to previous
…

Since the list was so long, I chose to focus on just one subdirectory, cli/:

~/git/cs214/backup/vscode $ git shortlog --since=2023-09-20 -- cli/
Bhavya U (1):
      Update license info from OSS Tool (#194025)

Connor Peet (3): cli: add more details if untar fails cli: fix renamed self-update name, cleanup old binaries cli: support setting the --parent-process-id in command shell (#193735)

Changing the date showed me more commits:

~/git/cs214/backup/vscode $ git shortlog --since=2023-09-01 -- cli/
Bhavya U (1):
      Update license info from OSS Tool (#194025)

Connor Peet (9): forwarding: fix log format again (#191941) tunnels: fix command prompt windows show up on windows machine (#192016) cli: fix delegated http requests not working (#192620) cli: propagate server closing (#192824) cli: fix panic if it can't bind a port (#193019) cli: allow command-shell to listen on a prescribed port (#193028) cli: add more details if untar fails cli: fix renamed self-update name, cleanup old binaries cli: support setting the --parent-process-id in command shell (#193735)

I find this command is very helpful for catching up after a few weeks of absence from an active project.

Then, I demoed how Git could help understand a complex, evolving codebase. I navigated to the directory in which I has already cloned the Scala 3 compiler:

~/git/cs214/backup/vscode $ cd ../
~/git/cs214/backup $ ls
demo  dotty  find  find-2  vscode
~/git/cs214/backup $ cd dotty/
~/git/cs214/backup/dotty $ ls
bench        community-build  language-server  presentation-compiler  sbt-test            stdlib-bootstrapped-tasty-tests
bench-micro  compiler         library          project                scaladoc            tasty
bench-run    CONTRIBUTING.md  library-js       README.md              scaladoc-js         tasty-inspector
bin          dist             LICENSE          sandbox                scaladoc-testcases  tests
build.sbt    docs             MAINTENANCE.md   sbt-bridge             sjs-compiler-tests
changelogs   interfaces       NOTICE.md        sbt-community-build    staging

… and, since I wasn’t sure which file to go look at, I wrote a small shell script to find the most commonly modified files (the shell is environment in which I type my command line programs, and a script is a small program). To do this I asked Git to print, for each commit, the list of all modified files. Below I used head to show just a few (in the original demo I used less to page through, so I changed this part):

~/git/cs214/backup/dotty $ git log --format= --name-only | head
tests/run/StringConcat.scala
compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala
compiler/src/dotty/tools/dotc/util/Signatures.scala
presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala
bench-run/src/main/scala/dotty/tools/benchmarks/Main.scala
bench/scripts/collection-vector.sh
bench/scripts/compiler-cold.sh
bench/scripts/compiler.sh
bench/scripts/library-cold.sh
bench/scripts/library.sh

I constructed a more complex script by adding a phase to sort the results alphabetically and count them:

~/git/cs214/backup/dotty $ git log --format= --name-only | sort | uniq -c | head
     10 .appveyor.yml
     17 AUTHORS.md
      2 bench-micro/results_isStable.json
      1 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/ContendedInitialization.scala
      3 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala
      3 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala
      1 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala
      2 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessMultiple.scala
      4 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala
      3 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala

And finally I sorted the results again, this time by number of changes:

~/git/cs214/backup/dotty $ git log --format= --name-only | sort | uniq -c | sort -rn | head
   1322 compiler/src/dotty/tools/dotc/typer/Typer.scala
   1173 project/Build.scala
    992 compiler/src/dotty/tools/dotc/core/Types.scala
    812 compiler/src/dotty/tools/dotc/parsing/Parsers.scala
    715 compiler/src/dotty/tools/dotc/core/Definitions.scala
    633 src/dotty/tools/dotc/core/Types.scala
    550 compiler/src/dotty/tools/dotc/core/TypeComparer.scala
    529 compiler/src/dotty/tools/dotc/typer/Applications.scala
    521 compiler/src/dotty/tools/dotc/ast/Desugar.scala
    516 compiler/src/dotty/tools/dotc/typer/Implicits.scala

(I didn’t mention this in the lecture, but the process library in Scala has very nice operators to build pipelines of commands like the above)

I picked Types.scala for no particular reason, except that I hoped it might be a bit simpler than Typer.scala. I opened it in an editor called Emacs, which has great support for Git through a plug-in called magit (I don’t know VSCode as well, and it’s a great exercise for you to figure it out!):

~/git/cs214/backup/dotty $ emacs compiler/src/dotty/tools/dotc/core/Types.scala

Of course since the rest happened in Emacs the command line transcript does not show the details of this part, but I also don’t expect you to learn Emacs for this class, so I will instead mention what I did:

~/git/cs214/backup/dotty $

This is the end of part 2!