<style>body { background-color: #eeeeee!important; } </style>
:::info
:information_source: On this page you will find notes for the first day of the NWO-I Software Carpentry workshop organized on July 4.
:::
## Code of Conduct
Everyone who participates in Carpentries activities is required to conform to the [Code of Conduct](https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html). This document also outlines how to report an incident if needed.
## :timer_clock: Schedule July 4
| | **Unix Shell**|
|------|------|
| 09:30 | Navigating and working with files and directories |
| 10:30 | Morning break |
| 10:45 | Automation (pipes, filters, loops & scripts) |
| 12:30 | Lunch break |
| 13:15 | Finding things |
| 14:00 | *END* |
| | **Git** |
| 14:15 | Setting up and working with Git |
| 15:45 | Afternoon break |
| 16:00 | Collaborating via Git |
| 17:30 | *END* |
## Unix shell
### :link: Links
* Setup page: https://swcarpentry.github.io/shell-novice/setup.html
* Lesson material: https://swcarpentry.github.io/shell-novice/
* Reference page: https://swcarpentry.github.io/shell-novice/reference.html


### 1. Introducing the Shell


Open (Git Bash) shell. Prompt: $.
Commands:
`pwd`
`ls`
`ls -F`
`ls --help`
or `man ls` (depending on OS=Operating System)
`ls -j` (raises error)
`ks` (raises error)
:::success
:pencil: **Exploring More `ls` Flags**
You can also use two options at the same time. What does the command `ls` do when used with the `-l` option? What about if you use both the `-l` and the `-h` option?
Some of its output is about properties that we do not cover in this lesson (such as file permissions and ownership), but the rest should be useful nevertheless.
:::spoiler :eyes: ***Solution***
The `-l` option makes ls use a long listing format, showing not only the file/directory names but also additional information, such as the file size and the time of its last modification. If you use both the `-h` option and the `-l` option, this makes the file size ‘human readable’, i.e. displaying something like `5.3K` instead of `5369`.
:::
### 2. Navigating Files and Directories

`ls -F Desktop`
`ls -F Desktop/shell-lesson-data`
`pwd`
`cd Desktop`
`cd shell-lesson-data`
`cd exercise-data`
`pwd`
`ls -F`
`cd shell-lesson-data` (raises error)
`cd ..` (back in path one step)
`pwd`
`ls -F -a`
`cd` (home)
`pwd`
`cd Desktop/shell-lesson-data/exercise-data`
`pwd`
`cd /Users/<username>/Desktop/shell-lesson-data`
:::success
:pencil: **Absolute vs Relative Paths**
Starting from `/Users/amanda/data`, which of the following commands could Amanda use to navigate to her home directory, which is `/Users/amanda`?
1. `cd .`
1. `cd /`
1. `cd /home/amanda`
1. `cd ../..`
1. `cd ~`
1. `cd home`
1. `cd ~/data/..`
1. `cd`
1. `cd ..`
:::spoiler :eyes: ***Solution***
1. No: `.` stands for the current directory.
1. No: `/` stands for the root directory.
1. No: Amanda’s home directory is `/Users/amanda`.
1. No: this command goes up two levels, i.e. ends in `/Users`.
1. Yes: `~` stands for the user’s home directory, in this case `/Users/amanda`.
1. No: this command would navigate into a directory home in the current directory if it exists.
1. Yes: unnecessarily complicated, but correct.
1. Yes: shortcut to go back to the user’s home directory.
1. Yes: goes up one level.
:::
`cd ~/Desktop/shell-lesson-data`
`pwd`
`ls -s exercise-data`
`ls -S exercise-data`
### 3. Working With Files and Directories
`ls north-pacific-gyre`
`ls nor`[tab] (completes name)
`ls north-pacific-gyre/goo`[tab]s (options)
`ls north-pacific-gyre/goodiff.sh`
`pwd`
`cd exercise-data/writing`
`ls -F`
`mkdir thesis`
`ls -F`
`ls -F thesis`
`mkdir -p ../project/data ../project/results`
`ls -FR ../project`
`cd thesis`
`nano draft.txt`
CTRL-X (exit)
`ls`
`cd ~/Desktop/shell-lesson-data/exercise-data/writing`
`pwd`
`mv thesis/draft.txt thesis/quotes.txt`
`ls thesis`
`mv thesis/quotes.txt .`
`ls thesis`
`ls`
:::success
:pencil: **Moving Files to a new folder**
After running the following commands, Jamie realizes that she put the files `sucrose.dat` and `maltose.dat` into the wrong folder. The files should have been placed in the `raw` folder.
```
$ ls -F
analyzed/ raw/
$ ls -F analyzed
fructose.dat glucose.dat maltose.dat sucrose.dat
$ cd analyzed
```
Fill in the blanks to move these files to the `raw/` folder (i.e. the one she forgot to put them in)
```
$ mv sucrose.dat maltose.dat ____/____
```
:::spoiler :eyes: ***Solution***
```
$ mv sucrose.dat maltose.dat ../raw
```
Recall that `..` refers to the parent directory (i.e. one above the current directory) and that `.` refers to the current directory.
:::
`cp quotes.txt thesis/quotations.txt`
`ls quotes.txt thesis/quotations.txt`
`cp -r thesis thesis_backup`
`ls thesis thesis_backup`
`rm quotes.txt`
`ls quotes.txt`
`rm -i <filename>`
`rm thesis`
`rm -r thesis`
`rm -ri thesis`
`ls thesis`
:::success
:pencil: **Copy with Multiple Filenames**
For this exercise, you can test the commands in the `shell-lesson-data/exercise-data` directory.
In the example below, what does `cp` do when given several filenames and a directory name?
```
$ mkdir backup
$ cp creatures/minotaur.dat creatures/unicorn.dat backup/
```
In the example below, what does `cp` do when given three or more file names?
```
$ cd creatures
$ ls -F
basilisk.dat minotaur.dat unicorn.dat
$ cp minotaur.dat unicorn.dat basilisk.dat
```
:::spoiler :eyes: ***Solution***
If given more than one file name followed by a directory name (i.e. the destination directory must be the last argument), `cp` copies the files to the named directory.
If given three file names, `cp` throws an error such as the one below, because it is expecting a directory name as the last argument.
```
cp: target 'basilisk.dat' is not a directory
```
:::
Wildcards
`pwd`
`cd ..`
`cd alkanes`
`ls *.pdb`
`ls p*.pdb`
`ls ?ethane.pdb`
`ls ???ane.pdb`
### 4. Pipes and Filters
`pwd`
`ls`
`cd exercise-data/`
`clear`
`ls`
`cd alkanes/`
`ls`
`wc cubane.pdb`
`wc *.pdb`
`clear`
`wc -l *.pdb`
`wc -l ` [ctrl+C]
`wc -l *.pdb > lengths.txt`
`ls lengths.txt`
`clear`
`cat lengths.txt`
:::success
:pencil: **What Does `sort -n` Do?**
The file `shell-lesson-data/exercise-data/numbers.txt` contains the following lines:
```
10
2
19
22
6
```
If we run sort on this file, the output is:
```
10
19
2
22
6
```
If we run `sort -n` on the same file, we get this instead:
```
2
6
10
19
22
```
Explain why `-n` has this effect.
:::spoiler :eyes: ***Solution***
The `-n` option specifies a numerical rather than an alphanumerical sort.
:::
`cat numbers.txt`
`sort numbers.txt`
`sort -n numbers.txt`
`cd alkanes`
`ls`
`sort -n lengths.txt`
`sort -n lengths.txt > sorted-lengths.txt`
`head -n 1 sorted-lengths.txt`
`head -n 2 sorted-lengths.txt`
:::success
:pencil: **What Does `>>` Mean?**
We have seen the use of `>`, but there is a similar operator `>>` which works slightly differently. We’ll learn about the differences between these two operators by printing some strings. We can use the echo command to print strings e.g.
```
$ echo The echo command prints text
The echo command prints text
```
Now test the commands below to reveal the difference between the two operators:
```
$ echo hello > testfile01.txt
```
and:
```
$ echo hello >> testfile02.txt
```
Hint: Try executing each command twice in a row and then examining the output files.
:::spoiler :eyes: ***Solution***
In the first example with `>`, the string ‘hello’ is written to `testfile01.txt`, but the file gets overwritten each time we run the command.
We see from the second example that the `>>` operator also writes ‘hello’ to a file (in this case `testfile02.txt`), but appends the string to the file if it already exists (i.e. when we run it for the second time).
:::
`sort -n lengths.txt | head -n 1`
`wc -l *.pdb | sort -n`
`wc -l *.pdb | sort -n | head -n 1`

:::success
:pencil: **Pipe Construction**
For the file `animals.csv` from the previous exercise, consider the following command:
```
$ cut -d , -f 2 animals.csv
```
The `cut` command is used to remove or ‘cut out’ certain sections of each line in the file, and cut expects the lines to be separated into columns by a `Tab` character. A character used in this way is a called a **delimiter**. In the example above we use the `-d` option to specify the comma as our delimiter character. We have also used the `-f` option to specify that we want to extract the second field (column). This gives the following output:
```
deer
rabbit
raccoon
rabbit
deer
fox
rabbit
bear
```
The uniq command filters out adjacent matching lines in a file. How could you extend this pipeline (using uniq and another command) to find out what animals the file contains (without any duplicates in their names)?
:::spoiler :eyes: ***Solution***
```
$ cut -d , -f 2 animals.csv | sort | uniq
```
:::
`cd ../`
`ls`
`cd ../north-pacific-gyre`
`wc -l *.txt`
`wc -l *.txt | sort -n | head -n 5`
`wc -l *.txt | sort -n | tail -n 5`
`ls *Z.txt`
### 5. Loops
`cd ../exercise-data`
`cd creatures`
`ls`
`head -n 5 basilisk.dat minotaur.dat unicorn.dat`
```
for filename in basilisk.dat minotaur.dat unicorn.dat
> do
> echo $filename
> head -n 2 $filename | tail -n 1
> done
```
```
for x in basilisk.dat minotaur.dat unicorn.dat
> do
> echo $x
> head -n 2 $x | tail -n 1
> done
```
```
for filename in *.dat
> do
> echo $filename
> head -n 100 $filename | tail -n 20
> done
```
```
for filename in *.dat
> do
> $filename
> head -n 100 $filename | tail -n 20
> done
```
```
for filename in "red dragon.dat" "purple unicorn.dat"
> do
> head -n 100 "$filename" | tail -n 20
> done
```
```
for filename in "red dragon.dat" "purple unicorn.dat"
> do
> head -n 100 $filename | tail -n 20
> done
```
`ls`
`cp *.dat original-*.dat`
```
for filename in *.dat
> do
> cp $filename original-$filename
> done
```
`ls`
`cd ../..`
`cd north-pacific-gyre`
`ls`
```
for datafile in NENE*A.txt NENE*B.txt
> do
> echo $datafile
> done
```
```
for datafile in NENE*A.txt NENE*B.txt
> do
> echo $datafile stats-$datafile
> done
```
```
for datafile in NENE*A.txt NENE*B.txt; do echo $datafile stats-$datafile; done
```
```
for datafile in NENE*A.txt NENE*B.txt; do bash goostats.sh $datafile stats-$datafile; done
```
```
for datafile in NENE*A.txt NENE*B.txt; do echo $datafile; bash goostats.sh $datafile stats-$datafile; done
```
### 6. Shell Scripts
`cd ../exercise-data/alkanes`
`ls`
`nano middle.sh`
```
head -n 15 octane.pdb | tail -n 5
```
[ctrl+X] to save and exit
`ls`
`bash middle.sh`
`nano middle.sh`
```
head -n 15 "$1" | tail -n 5
```
[ctrl+X] to save and exit
`bash middle.sh octance.pdb`
`bash middle.sh pentane.pdb`
`nano middle.sh`
```
head -n "$2" "$1" | tail -n "$3"
```
[ctrl+X] to save and exit
`cat middle.sh`
`bash middle.sh pentane.pdb 15 5`
`bash middle.sh pentane.pdb 20 5`
`nano middle.sh`
```
# Select lines from the middle of the file.
# Usage: bash middle.sh filename end_line num_lines
head -n "$2" "$1" | tail -n "$3"
```
`wc -l *.pdb | sort -n`
`nano sorted.sh`
```
# Sort files by their length
# Usage: bash sorted.sh one_or_more_filenames
wc -l "$@" | sort -n
```
`bash sorted.sh *.pdb ../creatures/*.dat`
:::success
:pencil: **List Unique Species**
Leah has several hundred data files, each of which is formatted like this:
```
2013-11-05,deer,5
2013-11-05,rabbit,22
2013-11-05,raccoon,7
2013-11-06,rabbit,19
2013-11-06,deer,2
2013-11-06,fox,1
2013-11-07,rabbit,18
2013-11-07,bear,1
```
An example of this type of file is given in `shell-lesson-data/exercise-data/animal-counts/animals.csv`.
We can use the command `cut -d , -f 2 animals.csv | sort | uniq` to produce the unique species in `animals.csv`. In order to avoid having to type out this series of commands every time, a scientist may choose to write a shell script instead.
Write a shell script called species.sh that takes any number of filenames as command-line arguments, and uses a variation of the above command to print a list of the unique species appearing in each of those files separately.
:::spoiler :eyes: ***Solution***
```
# Script to find unique species in csv files where species is the second data field
# This script accepts any number of file names as command line arguments
# Loop over all files
for file in $@
do
echo "Unique species in $file:"
# Extract species names
cut -d , -f 2 $file | sort | uniq
done
```
:::
`cd ~`
`cd Desktop/shell-lesson-data/exercise-data/writing`
`cat haiku.txt`
`grep not haiku.txt`
`grep "not the" haiku.txt`
`grep -n "not the" haiku.txt`
`grep -r Yesterday .`
`grep -E "^.o" haiku.txt`
`find .`
`find . -type d`
`find . -type f`
`find . -name "*.txt"`
`wc -l $(find . -name "*.txt")`
`grep "searching" $(find . -name "*.txt")`
## Version Control with Git
### :link: Links
* Setup page: https://swcarpentry.github.io/git-novice/setup.html
* Lesson material: https://swcarpentry.github.io/git-novice/
* Reference page: https://swcarpentry.github.io/git-novice/reference.html
* The Turing Way chapter: https://the-turing-way.netlify.app/reproducible-research/vcs.html
* List of Git GUIs: https://en.wikipedia.org/wiki/Comparison_of_Git_GUIs
### 1. Automated version control




### 2. Setting Up Git
`git config --global user.name "<username>"`
`git config --global user.email "<e-mail address>"`
`git config --global core.editor "nano -w"`
`git config --global init.defaultBranch main`
`git config --global --edit` (nano editor opens config file)
`git config --list`
### 3. Creating a Repository

`cd ~`
`cd Desktop`
`mkdir planets`
`cd planets`
`git init`
`ls`
`ls -a`
`git checkout -b main`
`git status`
:::success
:pencil: **Places to create git repositories**
Along with tracking information about planets (the project we have already created), Dracula would also like to track information about moons. Despite Wolfman’s concerns, Dracula creates a moons project inside his planets project with the following sequence of commands:
```
$ cd ~/Desktop # return to Desktop directory
$ cd planets # go into planets directory, which is already a Git repository
$ ls -a # ensure the .git subdirectory is still present in the planets directory
$ mkdir moons # make a subdirectory planets/moons
$ cd moons # go into moons subdirectory
$ git init # make the moons subdirectory a Git repository
$ ls -a # ensure the .git subdirectory is present indicating we have created a new Git repository
```
Is the git init command, run inside the moons subdirectory, required for tracking files stored in the moons subdirectory?
:::spoiler :eyes: ***Solution***
No. Dracula does not need to make the moons subdirectory a Git repository because the planets repository can track any files, sub-directories, and subdirectory files under the planets directory. Thus, in order to track all information about moons, Dracula only needed to add the moons subdirectory to the planets directory.
Additionally, Git repositories can interfere with each other if they are “nested”: the outer repository will try to version-control the inner repository. Therefore, it’s best to create each new Git repository in a separate directory. To be sure that there is no conflicting repository in the directory, check the output of git status. If it looks like the following, you are good to go to create a new repository as shown above:
```
$ git status
```
```
fatal: Not a git repository (or any of the parent directories): .git
```
:::
### 4. Tracking Changes
`mkdir moons`
`cd moons`
`git init`
`ls -a`
`rm -rf .git` (Use with caution)
`cd ..`
`nano mars.txt` (type in text)
`ls mars.txt`
`cat mars.txt`
`git status`
`git add mars.txt`
`git status`
`git commit -m "Start notes on Mars as a base"`
`git status`
`git log`
`nano mars.txt` (add text)
`git status`
`git diff`
`git commit -m "Add concerns about effects of Mars' moons on Wolfman"` (oops)
`git add mars.txt`
`git commit -m "Add concerns about effects of Mars' moons on Wolfman"`

`nano mars.txt`
`cat mars.txt`
`git diff`
`git add mars.txt`
`git diff`
`git diff --staged`
`git commit -m "Discuss concerns about Mars'climate for Mummy"`
`git log`

:::success
:pencil: **Choosing a Commit Message**
Which of the following commit messages would be most appropriate for the last commit made to mars.txt?
1. “Changes”
2. “Added line ‘But the Mummy will appreciate the lack of humidity’ to mars.txt”
3. “Discuss effects of Mars’ climate on the Mummy”
:::spoiler :eyes: ***Solution***
Answer 1 is not descriptive enough, and the purpose of the commit is unclear; and answer 2 is redundant to using “git diff” to see what changed in this commit; but answer 3 is good: short, descriptive, and imperative.
:::
:::success
:pencil: **Committing Changes to Git**
Which command(s) below would save the changes of `myfile.txt` to my local Git repository?
1. `$ git commit -m "my recent changes"`
2. `$ git init myfile.txt`
`$ git commit -m "my recent changes"`
3. `$ git add myfile.txt`
`$ git commit -m "my recent changes"`
4. `$ git commit -m myfile.txt "my recent changes"`
:::spoiler :eyes: ***Solution***
1. Would only create a commit if files have already been staged.
2. Would try to create a new repository.
3. Is correct: first add the file to the staging area, then commit.
4. Would try to commit a file “my recent changes” with the message myfile.txt.
:::
:::success
:pencil: **`bio` Repository**
1. Create a new Git repository on your computer called bio.
2. Write a three-line biography for yourself in a file called `me.txt`, commit your changes
3. Modify one line, add a fourth line
4. Display the differences between its updated state and its original state.
:::
### 5. Exploring History
`nano mars.txt` (ill considered change)
`git diff HEAD mars.txt`
`git diff HEAD~1 mars.txt`
`git diff HEAD~2 mars.txt`
`git show HEAD~2 mars.txt`
`git diff d4eb5f701dbc.... mars.txt`
`git diff d4eb5f7 mars.txt`
`git status`
`git checkout HEAD mars.txt`
`cat mars.txt`
`git checkout d4eb5f701dbc.... mars.txt`
`git log`
`git status`
`git checkout HEAD mars.txt`
`cat mars.txt`
`git checkout d4eb5f7` (without filename, do not)
`git checkout main` (restore)
`git checkout main`
`git log`
`git checkout 7483987 mars.txt`
`git status`
`git checkout d4eb5f7 mars.txt`
`nano mars.txt`
`git add mars.txt`
`git checkout d4eb5f7 mars.txt`
`git status`
`git diff --staged`
`git reset HEAD mars.txt`
`git status`
`cat mars.txt`
`git status`
`cat mars.txt`


### 6. Ignoring Things
`mkdir results`
`touch a.dat b.dat c.dat results/a.out results/b.out`
`git status`
`nano .gitignore`
contains:
*.dat
results/
`git status`
`git add .gitignore`
`git commit -m Ignore data files and the results folder`
`git status`
`git add a.dat`
`git status --ignored`
:::success
:pencil: **Ignoring Nested Files**
Given a directory structure that looks like:
```
results/data
results/plots
```
How would you ignore only `results/plots` and not `results/data`?
:::spoiler :eyes: ***Solution***
If you only want to ignore the contents of `results/plots`, you can change your `.gitignore` to ignore only the `/plots/` subfolder by adding the following line to your `.gitignore`:
`results/plots/`
This line will ensure only the contents of `results/plots` is ignored, and not the contents of `results/data`.
As with most programming issues, there are a few alternative ways that one may ensure this ignore rule is followed. The “Ignoring Nested Files: Variation” exercise has a slightly different directory structure that presents an alternative solution. Further, the discussion page has more detail on ignore rules.
:::
:::success
:pencil: **Including Specific Files**
How would you ignore all `.dat` files in your root directory except for `final.dat`? Hint: Find out what `!` (the exclamation point operator) does.
:::spoiler :eyes: ***Solution***
You would add the following two lines to your .gitignore:
```
*.dat # ignore all data files
!final.dat # except final.data
```
The exclamation point operator will include a previously excluded entry.
Note also that because you’ve previously committed `.dat` files in this lesson they will not be ignored with this new rule. Only future additions of `.dat` files added to the root directory will be ignored.
:::
:::success
:pencil: **Ignoring Nested Files: Variation**
Given a directory structure that looks similar to the earlier Nested Files exercise, but with a slightly different directory structure:
```
results/data
results/images
results/plots
results/analysis
```
How would you ignore all of the contents in the `results` folder, but not `results/data`?
Hint: think a bit about how you created an exception with the `!` operator before.
:::spoiler :eyes: ***Solution***
If you want to ignore the contents of `results/` but not those of `results/data/`, you can change your `.gitignore` to ignore the contents of `results` folder, but create an exception for the contents of the `results/data` subfolder. Your `.gitignore` would look like this:
```
results/* # ignore everything in results folder
!results/data/ # do not ignore results/data/ contents
```
:::
:::success
:pencil: **Ignoring all data Files in a Directory**
Assuming you have an empty `.gitignore` file, and given a directory structure that looks like:
```
results/data/position/gps/a.dat
results/data/position/gps/b.dat
results/data/position/gps/c.dat
results/data/position/gps/info.txt
results/plots
```
What’s the shortest `.gitignore` rule you could write to ignore all `.dat` files in `results/data/position/gps`? Do not ignore the `info.txt`.
:::spoiler :eyes: ***Solution***
Appending `results/data/position/gps/*.dat` will match every file in `results/data/position/gps` that ends with `.dat`. The file `results/data/position/gps/info.txt` will not be ignored.
:::
### 7. Remotes in GitHub

Use GitHub: https://github.com
Create repository on GitHub called planets.
Public, leave the other options open.

Use the safer "SSH" option for the URL. Copy button for copying URL.
`ls -ltr`
`git remote add origin <URL>`
(URL looks something like "git@github.com:nwo-i-dcc/planets.git")
`git remote -v`
`ls -al ~/.ssh`
Make key
`ssh-keygen -t ed25519 -C "<e-mail address>"`
(Enter, default)
Overwrite? Yes
Passphrase: skip now, but recommended to use
`ls -al ~/.ssh`
Check it/try connection:
`ssh -T git@github.com` (Denied, because GitHub doesn't know the puclic key)
`cat ~/.ssh/id_ed25519.pub`
(Copy the output, paste in Profile Settings > SSH- and GPG-keys > Add New)
`ssh -T git@github.com`
(Should say something like "...succesfully authenticated...")
`git push origin main`
(Now check remotely on GitHub in the planets repository)

`git pull origin main`
### 8. Collaborating
Repository on GitHub > Access > Collaborators > invite someone
Owner-collaborator (roles) pair.
`git clone git@github.com:<collaborator-name>/planets.git ~/Desktop/collab-planets`
`cd ..`
`cd collab-planets`
`ls`
(Note that the gitignored files are not there in the cloned collab-planets)
`ls -al`

In collab-planets (collaborator):
`nano pluto.txt`
`cat pluto.txt`
`git add pluto.txt`
`git commit -m "add notes about Pluto"`
`git push origin main`
Owner looks in remote repository: should be changed (or added if it wasn't there)
Now not (yet) in owner's repo locally.
Owner in planets local repo:
`git pull origin main`
`ls`

### 9. Conflicts
Again Owner-Collaborator.
Collaborator in own repo :
`nano mars.txt` (add a line at the bottom)
`cat marst.txt`
`git add mars.txt`
`git commit -m "Add a line in our home copy" mars.txt`
`git push origin main`
The other in own repo:
`nano mars.txt` (add a different line at the bottom)
`git add mars.txt`
`git commit -m "Add a line in my copy" mars.txt`
`git push origin main` (Rejection message)

`git pull origin main`
(Tries to solve, automerge, but have to do it yourself)
`cat mars.txt`
`nano mars.txt`
`git add mars.txt`
`git status`
`git commit -m "Merge changes from GitHub"`
`git push origin main`
`git pull origin main`
The collaborator:
`git pull origin main`
`ls`
`cat mars.txt`
`nano mars.txt`
`git add mars.txt`
`git commit -m "Added one line"`
`git push origin main`
The other:
`nano mars.txt`
`git add mars.txt`
`git commit -m "New research on Mars'moons"`
`git push origin main` (rejection message)
`git pull origin main` (opens nano, adds message)
`cat mars.txt`
(Merged well, because it was on different line, without changing it ourselves)
### 10. Open Science




(Note: Atom is deprecated and gone into VisualStudio Code)
