Exercise 1: Practice with git on a local repository

Published

2025-01-24

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

  1. Go to your home directory by typing:

    cd
  2. Create a directory called test-git-repo and cd into it:

    mkdir test-git-repo
    cd test-git-repo
  3. 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.

  4. test-git-repo is now tracked by git. 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)
  5. 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 run

    git 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!

  6. Tell git to start tracking story.txt by using git 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 that story.txt is staged for the next commit.

  7. Commit the change by running:

    git commit -m "add story idea"

    Now run git status. You should get nothing 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

  1. 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 line Once upon a time, in a land far, to A 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
  2. 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 your git settings. Since we set git’s global core.editor to nano 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

Note that there are two different ways to provide a commit message:

  1. Provide the -m argument along with a short message in quotes when you run git 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.

  2. 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.

  1. Run git branch. You should get back:

    * main

    This means there is currently only one branch, called main. The * next to main 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, run git branch again. Now you should see:

      main
    * title-dev

    Notice that story.txt is still here (type ls) and that your git history is the same as before (type git log). This is because when you create a branch, the branch will have all the same history and files as the main branch.

  2. 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.

  3. Now that you’ve created the file, run git status; you should see that title.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.

  4. 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 because main already exists; we only add the -b when we want to create a new branch.) Verify that you’re on main by running git branch; you should get:

    * main
      title-dev

    Note that the * is now next to main.

    Now type ls. The title.txt file is gone! To see why, type git 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 the title-dev branch. To verify that it’s still there, you check the history of the title-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 affect title-dev and vice versa (at least not until we merge the branches; more on that below).

Tip

You can git graphs like the above in your terminal if you add --graph to the git log command; i.e.

git log --graph
  1. Let’s make some more changes to story.txt. Use your favorite text editor to open story.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 to title-dev branch.

  2. 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 typing git branch. Now use your favorite text editor to open title.txt and change STAR 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 from main (before you branched off), and the last two on title-dev. In graph form, your repo looks like this:

  3. 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 and title.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:

Note

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.

  1. 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
Note

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.

Question 1: Setup

Create a file called list.txt that initially contains the following three lines:

alpha
bravo
charlie

Now use the git commands you learned above to do the following:

  1. Add and commit list.txt to main.

  2. Create a branch called dev1.

  3. On dev1 change the first line of list.txt to delta then commit the change.

  4. Go back to main and change the third line to foxtrot, then commit it.

  1. Run:

    git add list.txt
    git commit -m "add list.txt"
  2. Run:

    git checkout -b dev1
  3. Use nano (or your favorite text editor) to edit list.txt and change the first line to delta. Then run:

    git add list.txt
    git commit -m "change the first item of list"
  4. First run:

    git checkout main

    Then use nano (or your favorite text editor) to edit list.txt and change the third line to foxtrot. To commit, run:

    git add list.txt
    git commit -m "change the last item of list"

When you are done, list.txt should look like the following on main:

alpha
bravo
foxtrot

while on dev1 it should look like:

delta
bravo
charlie

You can check the differences by running git diff main dev1.

Question 2: Editing different lines

Now suppose you merge dev1 into main. What happens to list.txt?

A. main takes precedence since it’s the branch being merged into; list.txt will look like:

alpha
bravo
foxtrot

B. dev1 takes precedence since it’s the branch being merged; list.txt will look like:

delta
bravo
charlie

C. The changes from both are adopted; list.txt will look like:

delta
bravo
foxtrot

D. An error is raised because the same file was modified.

If you’re not sure, try it yourself and see what happens!

The answer is C. Since different lines have been modified, git is able to merge the changes together without issue. To do it yourself, run:

git checkout main
git merge dev1
Question 3: Editing the same lines

After merging dev1 on to main, suppose you create another branch called dev2. On dev2 you change the second line of list.txt to be echo while on main you change the second line to easy; i.e. on main list.txt looks like:

delta
easy
foxtrot

On dev2 list.txt looks like:

delta
echo
foxtrot

What happens if you merge dev2 into main in this case?

A. main takes precedence since it’s the branch being merged into; list.txt will look like:

delta
easy
foxtrot

B. dev2 takes precedence since it’s the branch being merged; list.txt will look like:

delta
echo
foxtrot

C. The changes from both branches are adopted; list.txt will look like:

delta
easy
echo
foxtrot

D. An error is raised because the same line is modified.

The answer is D, an error is raised. Since the same line has been modified in the same file, git cannot tell how to reconcile the differences. This is known as a merge conflict. In this case, git will leave it up to the user to reconcile the differences. How to do that is discussed in the next section.

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:

  1. If you have not done so, try to merge dev2 into main after making the changes to the second line of list.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.
  2. To resolve the merge conflict, we must edit list.txt ourselves. Use nano (or your favorite text editor) to open list.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 on main 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 is dev2).

  3. We are free to edit the file anyway we like to resolve the conflict. You should remove the <<<<, ====, and >>>> lines that git 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 on main. Delete the appropriate lines so that the file looks like:

    delta
    echo
    foxtrot

    Then save and exit the file.

  4. 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.

  5. 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.

Back to top