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 workspace is the folder that contains your files. That’s where you make your changes.
- The history is where git stores snapshots (“commits”, or “changesets”)
- The index is where you temporarily gather changes before taking a snapshot.
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:
-
I picked a random line (500 at first, then 1000 since the code didn’t look too interesting around line 500), found two very similar functions next to one that was a bit different (
final def implicitMembers
), and explored the history of that one. It turned out that it used to be similar to the other two, but diverged (that is, was changed) recently. -
I used
git blame
to annotate every line of the filecompiler/src/dotty/tools/dotc/core/Types.scala
with authorship information, showing how much editing had happened in this file after its creation.
~/git/cs214/backup/dotty $
This is the end of part 2!