Exercise 1: Practice with git
on a local repository
In this exercise you will initialize a git repository on your computer and explore various git commands with it.
Part 1: Initializing and adding files
Go to your home directory by typing:
cd
Create a directory called
test-git-repo
andcd
into it:mkdir test-git-repo cd test-git-repo
At the moment,
test-git-repo
is just a normal directory, like any others. We want to turn this into a git repository and track changes to files made in this directory. To do that, run:git init
Note that you have to run this from within the directory you want to turn into a repository.
test-git-repo
is now tracked bygit
. Run:git status
to see what the status of the repo is. You should get:
On branch main No commits yet nothing to commit (create/copy files and use "git add" to track)
Let’s add some files for git to track. Use your favorite text editor to create a file called
story.txt
and add the following to it:Once upon a time, in a land far, far away...
Save and exit the file, then type
ls
to verify it’s there. Now rungit status
again. You should have:
On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) story.txt nothing added to commit but untracked files present (use "git add" to track)
git knows you have a file in the directory called
story.txt
, but it isn’t tracked by git yet!Tell git to start tracking
story.txt
by usinggit add
. Run the following:git add story.txt
Now type
git status
. You’ll see:On branch main No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: story.txt
Notice that it still says there are no commits, but that on the next commit the file
story.txt
will be added. This means thatstory.txt
is staged for the next commit.Commit the change by running:
git commit -m "add story idea"
Now run
git status
. You should getnothing to commit, working tree clean
. Congratulations, you’ve made your first commit! To verify, run:git log
to see the history of your commits. Since this is your first commit, there will only be one entry.
Part 2: Making changes to files
You’re not happy with your story opening. You don’t want to write fairy tales, you want to write SciFi! Use your favorite text editor to open
story.txt
and change the lineOnce upon a time, in a land far,
toA long time ago in a galaxy far,
so that your file now reads:A long time ago in a galaxy far, far away...
Much better. Now type
git status
; you should see: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: story.txt no changes added to commit (use "git add" and/or "git commit -a")
git recognizes that
story.txt
has changed, but your changes have not been committed yet. You can see how the file differs from the last commit you did by typing:git diff
Let’s commit your changes so you don’t lose them! We’ll stage the changes by running
git add
again:git add story.txt
(Alternatively, you can just run
git add -u
to stage changes to all tracked files that have changed.) Now we’ll commit the change, however, we’ll do it slightly differently than we did above. Just run:git commit
(i.e., don’t include the
-m
argument). This will cause a text editor to open. The text editor that is used depends on yourgit
settings. Since we set git’s globalcore.editor
tonano
in the intro, nano will open up for you. Now you can type in a commit message. Type “change opening” then save by hitting<Ctrl>+O
. When nano asks where to save to, just hit<Enter>
to save to the default. Then hit<Ctrl>+X
to exit. This will complete the commit; you will be returned to the command line. To verify that the commit worked, run:git log
You’ll now see 2 commits in your history.
Note that there are two different ways to provide a commit message:
Provide the
-m
argument along with a short message in quotes when you rungit commit
on the command line, like we did in Part 1, step 7 above; i.e, run:git commit -m "YOUR MESSAGE"
In that case, the commit will be completed on the command line; no text editor will be opened.
Do not provide the
-m
argument. In that case, a text editor will open with which you can type the commit message. The commit will be created as soon as you save and exit from the editor. Note that if you exit the editor without saving, the commit will not be created. (You will be returned to the command line with the file changes still in the staged state.)
Either way is fine. Typically the second method is used if you want to write a more elaborate commit message that spans multiple lines, which is difficult to do on the command line.
Part 3: Branches
You want to start working on titles for your stories, but you want to do it independently of your work on the story itself. To do that, let’s create a branch to specifically work on titles.
Run
git branch
. You should get back:* main
This means there is currently only one branch, called
main
. The*
next tomain
means that you are currently on the main branch. Let’s create a new branch for working on the titles. Run:git checkout -b title-dev
You’ll get
Switched to a new branch 'title-dev'
. To verify, rungit branch
again. Now you should see:main * title-dev
Notice that
story.txt
is still here (typels
) and that your git history is the same as before (typegit log
). This is because when you create a branch, the branch will have all the same history and files as the main branch.Let’s create a file to store our title idea. Use your favorite text editor to create a file called
title.txt
and put in it:STAR FIGHT!
(You can also use
echo
and the>
operator on the command line to do this.) The title’s not great, but you can’t think of anything better at the moment.Now that you’ve created the file, run
git status
; you should see thattitle.txt
is untracked. Add it to be tracked and commit:git add title.txt git commit -m "add title idea"
Now type
git log
; you’ll see you have 3 commits in your history.Satisfied with your title for now, go back to your main branch to work on your story some more. Switch back to the
main
branch by running:git checkout main
(Notice that we don’t include the
-b
flag this time. This is becausemain
already exists; we only add the-b
when we want to create a new branch.) Verify that you’re on main by runninggit branch
; you should get:* main title-dev
Note that the
*
is now next to main.Now type
ls
. Thetitle.txt
file is gone! To see why, typegit log
. Your last commit is no longer in your history; you just have the first two commits. This is because your last commit (and the file it created,title.txt
) only exists on thetitle-dev
branch. To verify that it’s still there, you check the history of thetitle-dev
branch by running:git log title-dev
In graph form, your repository currently looks like this:
Here, each dot represents a commit, and the different lines represent a different branch. Following a line illustrates the history as seen by that branch. Since the commits on title-dev live on their own branch, changes you make on
main
won’t affecttitle-dev
and vice versa (at least not until we merge the branches; more on that below).
You can git graphs like the above in your terminal if you add --graph
to the git log
command; i.e.
git log --graph
Let’s make some more changes to
story.txt
. Use your favorite text editor to openstory.txt
and add the following lines:It is period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire.
Stage the change and commit it:
git add -u git commit -m "add intro paragraph"
Now type
git log
: you should have 3 commits, none of which include the commits totitle-dev
branch.In a flash of brilliance, you get an idea for the title of your story. Quick! Switch to your
title-dev
branch by typing:git checkout title-dev
You can verify that you’re on the
title-dev
branch by typinggit branch
. Now use your favorite text editor to opentitle.txt
and changeSTAR FIGHT!
to:Star Wars A NEW HOPE by George Lucas
Nice work, George! Now save and exit. Stage the change and commit it:
git add -u git commit -m "update title"
Type
git log
to check your history. Note that there are 4 commits here: the first two frommain
(before you branched off), and the last two ontitle-dev
. In graph form, your repo looks like this:Satisfied that your title is perfect, you no longer feel the need to keep the title development on a separate branch. It’s ready to be merged on to the
main
branch. To do that, switch to the main branch:git checkout main
and merge in the changes on
title-dev
by typing:git merge title-dev
This will open up your text editor (which we set to
nano
in the intro). Don’t change the commit message, just save and exit to return to your terminal.Now type
git log
. You should see 6 commits: the initial two commits on main, the first commit on title-dev, the add paragraph commit on main, and the update title commit you made on title-dev, followed by an additional “Merge” commit. This final “merge” commit is to indicate that some of the commits in the history came from another branch (and what the name of the branch was).If you type:
ls
you’ll see that both
story.txt
andtitle.txt
are in your directory (on the main branch), and if you look in them, you’ll see the most up-to-date versions of both. In graph form, your main branch now looks like:
There is another way to merge branches called rebasing. Rebasing changes the history of the development branch to make it look like all the commits came after the last commit on main. This makes for a cleaner history when merging, but can be more challenging to do correctly. See the Appendix (below) for more information.
Now that you’ve merged your
title-dev
branch, you can delete it to save space. To do that, run:git branch -d title-dev
If you had not merged everything on title-dev
into main
, you would have gotten an error preventing you from deleting the branch. The fact that you could delete it gives you confidence that everything on the development branch has been merged.
Challenge Questions: Editing the same file on different branches
In the above example we only made changes to different files on our two branches: on main
we only edited story.txt
, while on title-dev
we only edited title.txt
. However, there is nothing stopping us from editing the same file concurrently on two different branches. The following questions will illustrate how that works.
Part 4: Resolving merge conflicts
As illustrated in the Challenge Questions, if the same lines in the same file are modified on two different branches, git
will not know how to merge the changes. This is called a merge conflict. In this case, git
will ask you to reconcile the differences. To illustrate, let’s try merging dev2
into main
after making the changes to list.txt
discussed in Question 3 above:
If you have not done so, try to merge
dev2
intomain
after making the changes to the second line oflist.txt
discussed in Question 3:git checkout main git merge dev2
You should get the following:
Auto-merging list.txt CONFLICT (content): Merge conflict in list.txt Automatic merge failed; fix conflicts and then commit the result.
To resolve the merge conflict, we must edit
list.txt
ourselves. Usenano
(or your favorite text editor) to openlist.txt
. You should see the following:delta <<<<<<< HEAD easy ======= echo >>>>>>> dev2 foxtrot
git
has added the conflicting lines from both branches to the file, along with information about the two branches. The line as it appears onmain
is prefaced with<<<<<<< HEAD
. This indicates that the following line(s) is (are) how it appears on the branch being merged into. The line(s) is (are) followed by a=======
.After the
=======
the line(s) as it/they appear(s) on the branch being merged are shown. These are followed by>>>>>>> BRANCH_NAME
(here,BRANCH_NAME
isdev2
).We are free to edit the file anyway we like to resolve the conflict. You should remove the
<<<<
,====
, and>>>>
lines thatgit
has added, along with the edit you want to make to resolve the conflict.In this case, let’s keep the edit as it appears on
dev2
, and remove the edit onmain
. Delete the appropriate lines so that the file looks like:delta echo foxtrot
Then save and exit the file.
Now that we are happy with the way
list.txt
appears, add the file:git add list.txt
Now
commit
without the-m
:git commit
This will open up your text editor. The commit message will already be set to
Merge branch 'dev2'
. While you are free to modify this, it’s usually best to leave it as-is, as it indicates the merge. Save and exit the file.The merge conflict is resolved! If you type
git log
you will see the history with all the commits in it, as if you had a merged without any issue.
Merge conflicts happen, but can be fixed fairly easy. If there were multiple areas in a file that caused a conflict, the conflicting areas would all be surrounded by the same <<<<<<<
/=======
/>>>>>>>
lines that we saw above. You’ll want to search through the file for those to make sure you got all the conflicts. If multiple files had conflicts, git
will make you resolve all of them before allowing the merge to complete.
Summary
That’s the basics of how to use git
. In the following parts we’ll see how to use this with GitHub and how to collaborate with partners using git
and GitHub.
Appendix: Rebasing
Before merging title-dev
into master in step 8 above, we could have first done a rebase. If you rebase a development branch against its parent branch (in this case main
) it will incorporate all the commits that were made on the parent after the development branch was originally created. To do so, the development branch’s history is rearranged so that the additional commits from the parent are placed before all commits that were made on the development branch.
For example, if we had run git rebase main
while on the title-dev
branch in Part 3, step 8 above, title-dev
’s history would have been rearranged from:
to:
The advantage to rebasing is that when we merge a rebased development branch back into main no “Merge” commit is created, and the history on main is much simpler. So, in our story example, if we had done the following in step 8:
git checkout title-dev
git rebase main
git checkout main
git merge title-dev
Then main’s history would have looked like:
rather than the more complicated graph above.
The disadvantage to rebasing is it can be more challenging to handle merge conflicts. When rebasing, conflicts need to be handled at the point they are created in the development branch, rather once at the end when the merge occurs. If resolving a conflict affects a later commit on the development branch, it can cause a cascading series of conflicts that can be a challenge to untangle. The other challenge with rebasing is it can cause issues when collaborating with others on a development branch. Since rebasing changes the history of the branch, it can make it a challenge for your colleagues to keep their clones in sync with yours. For these reasons, rebasing is best for more advanced users.
For more on rebasing, see this article by the Atlassian corporation: Merging vs. rebasing.