Intermediate Git Tutorial
Intro
[0:00 Video timestamp]
- In this tutorial we will cover intermediate level Git operations like resolving merge conflicts, tags, refs, undoing/redoing prior commits, and temporarily stashing code.
- You should already be familiar with making commits, using branches, and using the git status, diff, log, and show commands.
- We will be working with a simulated coding project named git-demo with a local git repository. It is the same project from the Beginning Git tutorial.
- You can continue with that project for this tutorial.
- The finished code from this tutorial series can be found at github.com/LearnByCheating/git-demo
- Clone the project on your computer:
- git clone https://github.com/LearnByCheating/git-demo.git git-demo-master-copy
- Then do a hard reset to the the end of the Beginning Git tutorial:
- git reset --hard v1-beginning-git
Read it, watch it, do it, review it:
- There are both a written and accompanying video version of this tutorial. The written version has timestamps under the headings that align with the video version of the topic.
- Read it: For each topic heading, first read the topic.
- Watch it: Then watch the associated section of the video.
- Do it: Then follow the instructions to replicate the steps on your computer.
- The CheatSheet has a major category that aligns with this tutorial.
- Review it: When you are done with the whole tutorial, do the two Intermediate Git exercises. They repeat the steps from this tutorial without the explanations so you can do them quickly. One exercise uses only Git commands, the other uses the VS Code Source Control sidebar's graphical user interface.
- Open the CheatSheet and review the Intermediate Git category which includes all the topics from this tutorial. Make sure you understand everything, and can refer back to the CheatSheet when you are working on your own projects in the future.
VS Code Source Control GUI
- If you are using VS Code as your text editor, VS Code has a Source Control feature that allows you to execute many Git actions with toolbars and menus rather than typing in git commands.
- In this tutorial we will use both Git commands and the GUI. If you aren't using VS Code, just focus on the Git commands.
- Open the Source Control sidebar by clicking the Source Control toolbar on the left side:
- At the top right of the Source Control sidebar are three dots
⋯
. Click that to access a drop-down menu of more actions. We'll refer to that as More Actions.
Tags
[1:15 Video timestamp]
- You can create tags for important commits like a new version release, or a feature addition, to distinguish them from other commits.
- We'll add a tag to the last commit from the beginning Git tutorial.
- First let's see a list of our commits with git log --oneline.
Create/delete tags using the VS Code Source Control sidebar
Add a tag:In VS Code, click the Source Control toolbar on the Activity Bar on the left.
⋯ > Tags > Create Tag > Enter tag name > Optionally enter tag message
- At the top of the Source Control sidebar, click the More Actions drop-down menu
⋯
> select Tags > Create Tag > Type in the tag name. Call it Beginning Git > And optionally add a message: "Beginning Git demo". - This adds the tag to the last commit.
- View the log again: git log --oneline
- You should see after the commitId is the tag name.
Delete the tag:
⋯ > Tags > Delete Tag > Select tag name
- To delete the tag click the Source Control More Actions drop-down > Tags > Delete tag > and select the tag name: "Beginning Git"
- The log will show that the tag name is gone: git log --oneline
CLI Create/delete tags using the command line
We just created and deleted tags using the VS Code Source Control sidebar. Now let's do it using Git Commands.
Add a tag:
Add a tag:
- Open the Unix shell (e.g., Terminal app).
- git tag v1-beginning-git
- Add the tag back using the command: git tag and the tag name.
- By default, this will be applied to the last commit.
- You can apply it to a different commit by appending the commit id to the command. Just enter the first 4 or more characters from the id.
git tag <tagname> <commit>
- Recheck the log: git log --oneline. You should see the tag added to the last commit.
- git tag -l will list all the tags in alphabetical order. We just have the one.
Delete a tag:
- git tag -d v1-beginning-git To delete a tag, enter git tag -d then the tag name.
Add tag with a message:
- Add the tag one more time. This time annotate a message with the -am option and the message in quotes followed by the tag name.
git tag -am 'Beginning Git' v1-beginning-git
- git tag -ln will list all tags including the message. If no tag message was added, the commit message would be shown. The log lists the tag after the commitId
Merge Conflict
[3:26 Video timestamp]
Let's cover how to resolve a merge conflict. - Merge conflicts occur when there are changes to the same line in competing commits. The conflict must be resolved before the file containing a conflict can be merged. Let's create an example where we create a merge conflict, then resolve it.
- We'll do this in five steps:
1. Check out a new branch called feature3:
git checkout -b feature3
2. Make changes in the feature 3 branch and commit them:
Add file named feature3.txt and save it:
feature3.txt
Some text.
Modify the app.txt file by changing line 3, and adding a line at the bottom of the file. Save it.
app.txt
This is a demo app to learn Git.
Second line modified.
Third line modified in feature3 branch.
Feature 1.
Feature 2. Feature 3.
Stage and commit the changes:
git add .
git commit -m 'Add feature 3'
3. Switch back to the main branch, modify app.txt line 3, then commit the changes.
Modify app.txt line 3 and save the file:
app.txt
This is a demo app to learn Git.
Second line modified.
Third line modified in main branch.
Feature 1.
Feature 2.
Stage and commit it:
git commit -am 'Modify app.txt'
That will give us changes to the same file on the same line in different branches.
View the log: git log --oneline
Our branches are now out of sync by one commit that includes conflicting changes.
4. Check out the feature3 branch again, then get the branch back in sync with the new commit in main by merging it in.
git checkout feature3
Logging the current branch's commits will confirm that we don't have the 'Modify app.txt' commit we just made to main.
git log --oneline
5. Let's get our feature3 branch up-to-date by merging main into it:
git merge main
Logging the current branch's commits will confirm that we don't have the 'Modify app.txt' commit we just made to main.
git log --oneline
5. Let's get our feature3 branch up-to-date by merging main into it:
git merge main
It lets us know there is a conflict on line 3. In green is the version from our feature3 branch. In blue is the version from the main branch we are merging in.
There are some buttons we can click to select one version or the other.
Or we can can click the
Resolve in Merge Editor
button. Let's click that.This will take us to the Merge Editor
- The two versions are shown side by side. You can accept one or the other. Click "Accept combination" to combine them. And customize the line to read:
Third line modified in main branch and modified in feature3 branch.
- Then click the
Complete Merge
button. - This will stage the changed file, which we then need to commit. You can do that by either:
- Review the message in the
commit message box
at the top of the Source Control sidebar. Leave it as or or change it. Then click the✓Commit
button. - Or go to the Terminal and enter the commit from the command line:
git commit -m 'Resolve app.txt merge conflict'
- Enter the git log command with the graph option which will indent commits that are inside a branch:
git log --graph --oneline
- Going from bottom to top, it shows the commits from the main branch.
- Then the feature3 commit we made in our feature3 branch.
- Then the 'Modify app.txt' commit we just merged in that caused our conflict.
- At the top is the commit we just made that resolved the merge conflict.
- Now that the feature3 branch is in sync, we'll wrap up by merging it back into main.
- Check out the main branch again: git checkout main
- And merge feature3 in: git merge feature3
- You can delete the feature3 branch:
git branch -d feature3
Log aliases
- Create an alias called glog for the log with the graph and oneline options:
- Git config and aliases were covered in the Beginning git video.
- git config --global alias.glog 'log --graph --oneline '
- Entering the alias git glog shows we have the same commits as before but we are now on the main branch.
- In the Beginning Git tutorial we created an alias for logging commits on one line with: git olog
- If you don't have that as an alias yet, add it now:
- git config --global alias.olog 'log --oneline '
Refs
[7:21 Video timestamp]
- Refs are like aliases you can use for referencing commits instead of using the commitId.
- The branch name is a ref for the the head commit of the branch.
git glog main will log the commits up to the head commit (i.e., the latest commit) for the main branch.
- Tag names are also refs.
git glog v1-beginning-git prints the commits up to the commit for that tag.
- There are also refs for stash indexes and remote names. Stashes are covered later in this tutorial and remotes are covered in a separate GitHub tutorial.
- If you open the project directory in the Mac Finder app or Windows File Explorer,
- Make sure you are displaying hidden files. On a Mac press Shift+Cmd+.
- The .git folder contains your project's local Git repository.
- It contains a directory called refs which in turn has a folder called heads.
- Heads contains a file for each branch. We only have the one branch, main.
- If you open the file, it contains the ID of the branch's head, or latest, commit.
- There is also a folder called tags and a file for each tag name that contains the tag id.
Rename | Delete files
[8:44 Video timestamp]
- Create a branch called practice: git checkout -b practice
- Rename file3 to file3b and stage it with one command: git mv file3.txt file3b.txt
- You can see in the Explorer sidebar that the file name has changed.
- You can delete a file, such as file4, and stage it with one command git rm file4.txt
- And file4 will disappear when you press enter.
- git status will show these two changes are staged and ready to commit.
Git Undo/Redo
Unstage Changes
[9:23 Video timestamp]
Unstaging changed files puts them back in the working directory
We have two staged changes, renaming a file and deleting a file. We can unstage them using either the Source Control sidebar or the command line.
Alternatively, from the More Actions menu:
Unstaging changed files puts them back in the working directory
We have two staged changes, renaming a file and deleting a file. We can unstage them using either the Source Control sidebar or the command line.
Unstage changes from the Source Control sidebar
Click the Source Control toolbar.
-
In the Staged Changes group click the minus sign. Now they are in the Working Directory.- You can unstage individual files by clicking the minus sign to the right if the filename.
+
To stage them again you can Click the plus sign to the right of Changes for all files, or to the right of individual file names for individual files.
Alternatively, from the More Actions menu:
⋯ > Changes > Unstage All Changes
You can also unstage the file by clicking the More Actions drop-down menu⋯
> Changes > Unstage All Changes⋯ > Changes > Stage All Changes
Restage them by clicking the drop-down again⋯
> Changes > Stage All Changes.
CLI Unstage changes from the command line
- You can also unstage files from the command line:
git restore --staged . Git restore --staged then the file name, or a dot for all files starting at the current directory.git restore -S . The short option name is dash capital S.
- Now both files are unstaged.
- Stage them again with: git add .
- You can also unstage the files with the git reset command: git reset
Discard changes from the Working Directory - restore command
[10:39 Video timestamp]
- Now our changes since the last commit are unstaged and sitting in the working directory.
- git status shows that you have two tracked files with changes in the working directory. Unstaged files are color coded red. The change is that the files are being deleted. We renamed file3 to file3b so Git's tracking process treats this as deleting file3 and creating a new untracked file named file3b.
To discard changes in the working directory you can use either the Source Control sidebar or the command line.
To discard changes to all files using the Source Control sidebar you can either:
Click the "Discard All Changes" toolbar to the right of the Changes group.
⋯ > Changes > Discard All Changes
Or click the Source Control dropdown⋯
> Changes > And Discard All Changes.- CLI To discard changes using Git commands, enter git restore then the file name, or a dot for all files in the working directory.
- git restore .
- Be careful with this, because once you discard the changes they are gone.
- git status will show that the deletion of file4 is gone (it will not be deleted). File3b will still there. The restore command won't delete untracked files.
Delete untracked files from the Working Directory - clean command
[11:50 Video timestamp]- We can delete untracked files like file3b manually from the Explorer sidebar.
- Or, to delete untracked files from the command line, use the clean command. To see what files would be deleted without actually deleting them you can enter git clean then the -n option:
git clean -n
It tells us that file3b would be deleted.
- To actually delete it, enter git clean then the force long or short option:
git clean --force | git clean -f
- And now the working directory and staging areas are clean.
Amend the last commit
[12:25 Video timestamp]
- Add a file called practice.txt with some text.
practice.txt
Some text.
- Stage and commit the changes:
git add .
git commit -m 'Add practice.txt'
git commit -m 'Add practice.txt'
- Now let's say we forgot to add a line to the file. So, add it now and save it:
practice.txt
Some text. Second line.
- Add this change to the staging area:
- Log the last 3 commits:
- Let's amend the last commit to include this change in it, rather than having to create a new commit.
- You can do that with the Source Control sidebar or the command line:
Open the Source Control sidebar then click the drop-down menu at the top
⋯
⋯ > Commit > Commit Staged (Amend)
- CLI Or in the Terminal, enter the commit command with the amend option:
- git commit --amend -m 'Add practice feature' Changes the commit message.
- git commit --amend --no-edit Keeps the original message.
- Now the change has been committed, and if you enter git olog -3 again you will see that the last commitId has changed. What happened is:
- The prior commit was removed.
- The changes from that commit were combined with the new changes, and a new commit was made.
Commit identifiers: commit id, refs (branch, tag, remote name), head
[13:34 Video timestamp]
- git glog We talked about refs which are text names, like branch or tag names, that can be used to reference a commit instead of its commitId.
- Head can also be used as a commit identifier for the latest commit in the current branch.
git show head will get the details of your last commit in the practice branch that's open now.
- git show head^ Adding the hat symbol after head will get the second to last commit.
- git show head^^ | git show head~2 For third to last you can use 2 hat symbols, or enter a tilde and the number of commits back you want to go.
Revert - Reverse a commit with a new commit
[14:09 Video timestamp]
- In our last commit we added the file practice.txt with a couple lines of text in it.
- Now let's say we want to undo all the changes from the prior commit.
- One way to do that is to make a new commit that reverses the changes from the previous commit.
- You can do that with the revert command:
- git revert head reverses the last commit. If you are reversing a different commit, you can enter that commit id instead of head.
- The commit message is at the top of the auto-generated COMMIT_EDITMSG file. You can modify it or leave it as is. Closing the file will apply the message and commit the revert changes.
- git olog -3 Shows that a new commit was made, and the prior commit remains in the commit history.
- git show will show the details of the last commit. You can see the practice.txt file was deleted.
Reset - Undo commits
[15:07 Video timestamp]
- git olog -3 We just looked at the revert command. It leaves the original commit in the commit history and adds a new commit to reverse it. If you want to just remove the original commit like it never happened you can use the reset command.
- With reset all the commits after the commit you specify will be removed from the commit history and the changes will be moved to the working directory by default.
- If you add the --soft option the changes will be moved to the staging area.
- If you add the --hard option the changes will be discarded, so be careful with that option.
- Let's undo the last commit. That was the revert commit that deleted the practice.txt file.
- Enter: git reset --hard head^
- Use the --hard option to just discard all changes from that commit.
- The commit we specify will be the last commit that we keep.
- To just undo the last commit use head followed by the hat symbol which means one commit before the head commit. We keep that commit and undo the commit after it (i.e., our last commit).
- Logging the last 2 remaining commits confirms that the previous last commit has been removed:
- git olog -2
- Because we did a hard reset, all changes for that last commit were discarded and the Source Control sidebar will show there are no changes in the working directory.
- Now let's undo the next commit with git reset head hat symbol, and no options:
git reset head^
That will undo the last commit, but the changes from that commit will be placed in the working directory. The practice.txt file will now be under the "Changes" heading with a U meaning the file is untracked.
- git olog -1 Logging just the last commit will show the "Add practice.txt" commit is gone.
- Add the file to the staging area and commit it again.
git add .git commit -m 'Add practice.txt'
- git olog -2 Logging the last two commits will show that the Add practice.txt commit is back, with a different commit number.
- Enter the reset command again, this time with the --soft option:
git reset --soft head^
That will undo the commits after the one specified, so the last commit, and put the changes from that commit in the staging area.
- git olog -1 The log will show the Add practices.txt commit is gone again.
- Commit it again: git commit -m 'Add practice.txt'
- git olog -2 The log will show the commit is back, with a different commit Id.
We just did a soft reset on the last commit from the Terminal. You can also do that with Source control sidebar.
⋯ > Commit > Undo Last Commit
- git olog -1 The log will show the last commit is gone.
- The change from that commit is staged. To commit it again, you can optionally change the commit message, and click the check symbol:
Keep or change commit message box > ✓
- git olog -2 The log will show the commit is back, with a new commit id.
Rebase - Change prior commits: reorder, combine, change message, edit, delete
[18:08 Video timestamp]
- We've seen how to reverse a prior commit, amend the last commit, and undo a commit.
- Now let's see how to change a prior commit with the rebase command.
- Before we get into it, just a warning. Be careful about changing prior commits. Especially when collaborating. Rebase your own branch but don't rebase branches others may have checked out because they will have the old version.
- git olog Right now we have one commit in the practice branch. Let's add a couple more
- Add a practice2 file, with some text. Stage it and commit it:
practice2.txt
Some text.
- git add .
- git commit -m 'Add practice2'
- And a practice3 file, with some text. Stage it and commit it.
practice3.txt
Some text.
- git add .
- git commit -m 'Add practice3'
- Clear the screen and log the last four commits. The main branch ended 4 commits ago and we have three commits in the practice branch.
- clear | Cmd+K
- git olog -4
- Use the rebase command to change the order of the commits:
- git rebase -i main
- The i option puts us in an interactive mode where we use the text editor to make our changes.
- The last argument is the commit before our changes. We want to rebase the all the commits in our current practice branch so you need to type the commit that comes right before it.
- You can enter first 4 characters of the commitId.
- Or you can use the number of commits before the head commit. So head~3.
- You can also use a ref. In this case we are using the name of the parent branch which is main.
- Now our text editor opens with a list of our commits at the top. Pick means use that commit as is.
- Notice all the commented out text. That contains the instructions on how to use this interactive file.
- To reorder the commits just cut and paste them in the order you want. Note that the order is oldest first. That's the opposite of how git log lists them.
- Move the Add practice2 commit after Add practice3.
- Save the file and close it.
- git olog -4 will show that the order has changed.
- Use rebase to change the commit message of a prior commit.
- git rebase -i main Enter git rebase with the interactive -i option then the commit. Main will give us all the commits after the main branch, which is all our practice branch commits.
- Let's change the message on the last commit. Change "pick" to "reword" or just "r" then save the file and close it.
- Then another file called commit edit message will open with the current commit message. You can leave it as is or change it. Let's change it to "Add practice2 reworded". Then save and close it.
- git olog -4 will show the commit message has changed.
- You can also combine two commits.
- Enter: git rebase -i main
- Let's combine the last two commits. On the bottom commit change pick to squash or just s. Save and close it.
- It will open the commit edit message file. Change the commit message to "Add practice2 and practice3".
- Save and close it.
- git olog -3 will show the last two commits were combined into one.
- If you are following along with the video tutorial, at this point we added a practice4.txt file and committed it. It's unnecessary so we'll skip doing that here.
- Use rebase to edit a prior commit.
- git rebase -i main
- Let's edit the first commit in the branch. Change pick to edit or just e. Save and close the file.
- In the practice.txt file, add a "Third line" and save it.
practice.txt
Some text. Second line. Third line.
- Notice some instructions were printed in the Terminal after our rebase command.
- Let's add the change to the staging area: git add practice.txt
- And per the instructions enter: git commit --amend
- You can optionally modify the commit message. Change it to "Add practice.txt amended". Save the file. And close it.
- Once you are done with your changes run git rebase --continue
- And we got a message saying our rebase was successful.
- Now lets use rebase to delete a previous commit:
- git rebase -i main
- Delete the first commit from the practice branch. Change pick to drop or just d. Or just delete the whole line. Then save the file and close it.
- The file disappears, and we got a success message in the Terminal.
- git olog -2 Logging your latest commits will show the Add practice.txt commit is gone.
- You may run into some unexpected problems when trying to do your rebase. And you may just want to abort it.
- To demonstrate, do a rebase of the practice branch that includes a commit from the main branch. If you add the hat symbol after main it will go one commit before the end of the main branch, which is the "Modify app.txt" commit:
- git rebase -i main^
- Try to reword it, changing pick to r, then save and close the file, you will get a message in the Terminal saying there is a merge conflict. And it gives some options of what to do. The last suggestion it gives you is to just abort the rebase.
- You can do that by opening the Source Control sidebar > Clicking the 3 dots > Commit > Abort Rebase.
- Or you can do it with a git command.
- git rebase --abort
- And now the rebase is cancelled.
Stash code
[24:10 Video timestamp]
- If you want to play around with some ideas without formally creating a branch,
- or if you aren't ready to make a commit but need to temporarily put your changes aside while you go to another branch, you can use the git stash command.
- We are currently in the practice branch. You can run the commit log to see our commits: git glog
- Let's go back to the main branch: git checkout main
- git glog prints the log for the main branch. The practice branch commits are gone.
- clear | Cmd+K to clear the terminal.
- Add a line to the app.txt file. Then save it.
app.txt
This is a demo app to learn Git.
Second line modified.
Third line modified in main branch and modified in feature3 branch.
Feature 1.
Feature 2.
Feature 3.
Work in progress.
- The git stash command will remove the code from the working directory and stash it to be retrieved later:
- git stash
- The working directory is now clean so I am free to checkout another branch, complete some task, and come back to resume what I was doing.
- git stash list will list the stash. It shows the branch, the commitID and commit message when the code was stashed.
- To put the stashed changes back in your working directory, run: git stash pop
- Pop removes the latest stash from the list. If you run git stash list again it returns nothing because there are no stashes.
- You can see in the File Explorer sidebar that the app.txt file is color coded yellow with an M after it, meaning it is a modified file. And the text line we added is in the file.
- You can discard the changes from to the app.txt file with the git restore command: git restore app.txt
- You can see the line was removed and app.txt is color coded white, meaning no modifications since the last commit.
- Put the "
Work in progress.
" line back into the app.txt file. And save the file. - Stash it again. This time use the message option.
- git stash -m 'Add line to app.txt'
- Let's say you want to experiment with a new feature called feature 4, without creating a branch yet. Add file feature4.txt with Some text.
feature4.txt
Some text.
- Stash it. feature4 is a new file so it is not yet in the git index. So I need to add the untracked option. Git stash -u and the message option m with message 'Feature 4':
- git stash -um 'Feature 4'
- If you list the stashes again you you see you now have two:
- git stash list
- Stashes are identified by their index number inside the curly braces. The most recent stash has index 0. The second most recent has index 1.
- To get information on the stash use the:
- git stash show command for the latest stash.
- git stash show 1 To specify a different stash, add the index number. In this case 1.
- git stash show 1 -p If you add the -p option it will show the specific changes.
- There may be cases where you want to retrieve stashed code without deleting the stash. In that case, instead of using git stash pop, use git stash apply. I want the feature 4 stash at index 0, which is the default:
- git stash apply 0
- That moves feature4.txt back to the working directory.
- If you run git stash list you can see the stash is still there.
- To delete a stash you can run git stash drop then the index number.
- To delete all stashes run:
- git stash clear
- Now if you run git stash list it returns nothing.
- Right now the feature4 file is in the working directory. Let's stash it, this time using the VS Code source control sidebar:
Click the Code Source toolbar.
⋯ > Stash > Stash (include untracked)
Since feature4 is a new file so it is untracked.- Then enter a stash message in the message box: "Feature 4"
- git stash list shows we now have the Feature 4 stash.
- To put the stash into our working directory and delete it you could:
-
⋯ > Stash > Pop Latest Stash
- or
⋯ > Stash > Pop Stash
Then select which stash you want to pop. - But instead, put the stash into your working directory without deleting the stash:
⋯ > Stash > Apply Latest Stash
- git stash list would show that the stash is still there.
- To delete this stash:
⋯ > Stash > Drop Stash > select the stash
- or
⋯ > Stash > Drop All Stashes
- git stash list shows there are no more stashes.
- We are done with stash.
- git status Shows the feature4 file is unstaged in the working directory.
- Discard it by clicking the discard changes toolbar to the right of the file name or Changes group:
- Let's add a tag to the last commit with a tag message.
- git tag -am 'Intermediate Git' v2-intermediate-git
- git tag -l will list all our tags. We have two. Beginning Git and Intermediate Git which align with the tutorials.
- git branch will list all our branches. We have the main branch. The asterisk means that is our current branch. And we have the practice branch.
- And finally, do a graphical log of all the commits in our current branch:
- git glog
- You can see we are on the main branch, and two of our commits are tagged.
- And that concludes this tutorial on Intermediate Git.