
This lecture walks through the full Git and GitHub for DevOps Engineers curriculum so you know what you'll learn. It covers:
version control basics and installing Git on Windows and Linux
working with Git: repositories, the stages of Git, and comparing changes
working with GitHub: creating a repository, cloning, pushing, resolving push failures, and SSH authentication
Git commits and Git branches, including branching strategies and merge conflicts
team workflows: forks, pull requests, private repositories, and protected branches
reverting changes and using a .gitignore file
a final real-time project that takes code from the dev branch through UAT to production
A few housekeeping points before the hands-on labs begin.
Prerequisites: Basic Linux commands are used throughout. If you're new to Linux, you can follow along, or take the Linux for Cloud and DevOps Engineers course for the fundamentals. The project uses multiple AWS EC2 instances, so some EC2 knowledge helps.
Resources you'll need: An AWS account, with a separate lecture at the end of the course on how to create one. You don't need a GitHub account beforehand, because it's created during the course.
Course materials: All documents used in the course are available at the GitHub URL provided.
Meet J.R. Shenker, a DevOps consultant with ten plus years of Linux experience in the IoT sector, teaching on Udemy and creating Galaxy Technologies content on YouTube.
When several developers work on the same project, sharing code by copying it between them quickly becomes unmanageable. This lecture explains the problem and how version control solves it. A version control system records every change to your code, shows who made it and when, makes collaboration easier, and lets you revert to an earlier version. It then compares the three types, local, centralized, and distributed, and why the distributed model is the most widely used. Each developer has a full copy of the code, so work continues even if the central repository is unavailable. Basic terms like push and pull are introduced, and the lecture closes with why this course uses Git.
This lecture sets up the Git environment for the course, which uses two developers: Developer 1 on Windows (the presenter's laptop) and Developer 2 on an Amazon Linux EC2 instance, so you see Git on both platforms. It walks through downloading the 64-bit Git installer from the official site, installing it with the default settings (admin rights required), and launching Git Bash. Git Bash lets you run Linux commands such as ls, cd, and cat on Windows. The next lecture covers Git on Linux.
Developer 2 needs a Linux system, so this lecture uses AWS to create one. It shows how to launch an Amazon Linux EC2 instance (t2.micro, free tier) in the Mumbai region, tag it as the Developer 2 system, and create a security group with SSH port 22 open. It also covers creating and downloading a key pair. You then connect to the instance with MobaXterm using the ec2-user account and the key pair, switch to the root user, install Git with yum install git, and verify it with git --version. If you don't have an AWS account yet, or want a fuller EC2 walkthrough, the course has separate lectures at the end.
Before you start committing, Git needs to know who you are, so every change can be traced to the right developer. This lecture shows how to set your username and email with git config --global user.name and git config --global user.email. It's a one-time setup that you can change later. You set it up on the Linux system (Developer 2), check the existing values on the Windows system (Developer 1), and see how to create a desktop shortcut for Git Bash. You can view your settings by running the same commands without a value, or list everything with git config --list.
This lecture covers a few Git basics and then creates your first repository. All Git commands start with git, most must be run inside a repository, and you should configure your identity first so commits show your real name. You then create a demo repo folder on Windows, open Git Bash inside it, and turn it into a repository with git init .. The hidden .git directory is what marks a folder as a repository, and you can see it with ls -a. Finally, you add two files to the repository, one from Git Bash and one from Windows. The next lecture shows how to track these changes.
This lecture explains the stages a file goes through before it becomes part of a Git repository. Creating a file in the working area only starts tracking it. To add it to the repository, you first move it to the staging area (also called the index area) with git add. Then git commit -m commits it to the local repository. The lecture also shows other ways to add files: git add . and git add --all. Along the way, git status shows where your files are: red for the working area, green for the staging area. git log shows the commit history, including the commit ID, author, and date. The course uses Git Bash rather than Git GUI.
This lecture digs deeper into Git stages. File1 and file2 are already in the local repository. You create file3 and file4, add only file3 to the staging area, and leave file4 in the working directory. You then make changes to file2, file3, and file4 and use git status to see where each file is recognized: working directory, staging area, or local repository. Next, git add . moves all changes to the staging area, and git commit -m commits them to the local repository. Finally, git log lists the commits, and there are now two.
This lecture shows how to compare your changes across the working directory, the staging area, and the local repository. With four files in the repository, you modify file1 and file2 (renaming file2.txt to file2), add the changes to the staging area with git add --all, and then modify them again. Then you compare with these commands:
git diff compares the working directory with the staging area.
git diff --staged compares the staging area with the local repository.
git diff --staged HEAD gives the same output as git diff --staged.
git diff HEAD compares the working directory with the local repository.
Added lines show in green and deleted lines in red. Because of the rename, Git treats file2.txt as deleted and file2 as a new file.
This lecture shows how to compare the changes between commits in the local repository. You remove file1, add all changes with git add ., and commit with the message "updated files". git log now shows three commits, and HEAD points to the latest one. You then run git diff with two commit IDs (the first six or seven characters are enough) to compare the latest commit with the previous one, and then with the first commit. The output shows which files were added or removed in each commit, and /dev/null marks a file that doesn't exist in that commit. So far the work is only on the local system, and the next section covers sharing code with Developer 2.
So far all the work has been on the Windows laptop, and the repository exists only on the local system. That doesn't work when you're working with a team, because sharing code by copying the entire repository each time is difficult, and the system may not always be up. GitHub solves this. It is a code hosting platform where repositories are hosted, so other developers can pull the code and make changes to it. This is also how the distributed version control system works, with GitHub as the main server repository. The goal is to push the code from Developer 1's laptop to GitHub so Developer 2, on the Linux system on AWS, can pull it. To push, you need a GitHub account, which the next lecture covers.
To store your code or share it with a team, you need a GitHub account. This lecture creates one from github.com: choose a unique username, enter an email ID and password, verify the account by solving a puzzle, join the free plan, and answer a few questions about your work, your programming experience, and how you plan to use GitHub. You then need to validate your email ID to activate the account. Next, you log in to an existing account to see the landing page, your profile, the overview page, and your list of repositories. Unlike the previous section, where the repository was created locally with git init, here you create a repository on GitHub with the New option, which is covered in the next video.
This lecture creates a repository on GitHub, which you can then pull onto your local system. You log in, choose the New option (or the plus symbol), and give the repository a name and an optional description. It explains the difference between public and private repositories. Anyone on the internet can see a public repository, so use it when you're fine sharing your code with the community. In a private repository you choose who can see and commit, so use it for proprietary content. You then initialize the repository with a README file, which is written in markdown and explains what the repository does and how to use it. The lecture also notes the .gitignore and license options, but .gitignore is covered later. After creating it, you see the README.md file, the master branch, and the initial commit. You copy the HTTPS clone URL (SSH is covered later), and you get a quick look at the other tabs: Issues, Pull requests, Actions, Projects, Wiki, Insights, and Settings. The next lecture clones this repository onto the developer system.
This lecture explains how the local and remote repositories work together. git add moves files from the working area to the staging area, and git commit moves them from the staging area to the local repository. The repository created on GitHub in the previous lecture is now cloned onto the developer system with git clone. You copy the HTTPS URL from the Code option on GitHub and run git clone in the projects folder. The cloned folder contains a .git directory, which shows it is a repository, and the README file with the content from the remote repository. The next lecture covers adding changes and pushing them to the remote repository.
This lecture updates the local repository and pushes the changes to GitHub, so Developer 2 can get the latest code. The repository is already cloned on the local system, so you create two new files, index.html and contact.html, and add them with git add .. Then you commit them with git commit -m. git status now says the local branch is ahead of origin master by one commit, and git log shows two commits while GitHub still shows one. You run git push origin master to update the remote repository. Here origin is the centralized repository on GitHub and master is the branch. After the push, the new commit and both files appear on GitHub. The next lecture pulls these changes onto the Linux system for Developer 2.
Developer 1 didn't add an email ID to contact.html, so Developer 2 has to make that change. The code on GitHub is treated as the deployable code, so Developer 2 pulls it instead of editing it on GitHub directly. You log in to the Developer 2 Linux system on AWS through MobaXterm using the key pair and the ec2-user account, then switch to root and change the hostname to make the system easy to recognize. You clone the repository with git clone, add the email line to contact.html, and stage it with git add .. You commit with git commit -m, and git status and git log show the local repository is one commit ahead of the remote one. git push origin master asks for credentials on the Linux system, unlike on Windows where they were already stored. The lecture explains why. Cloning a public repository is open to everyone, but changing the code needs the owner's permission. The credentials are shared with Developer 2 for now only. After the push, GitHub shows the new commit, the updated file, and two contributors.
This lecture explains the difference between git clone and git pull. The Developer 2 system and the remote repository have three commits, while the Developer 1 system has only two, because the third commit was made by Developer 2 and pushed to the remote repository. Running git pull on Developer 1 brings in the missing changes: contact.html is updated with the email, and git log now shows three commits, including the one by Developer 2 with the description "added email to contact". Use git clone when the repository doesn't exist on your system yet, as when Developer 1 and Developer 2 first got the repository. Use git pull when the repository already exists but you want your local repository and the remote repository to have the same commits. The next lecture looks at what happens if Developer 2 makes changes and tries to push without pulling the latest code first.
This lecture covers a common issue developers face when pushing to the remote repository. Developer 1 and Developer 2 both clone the same repository (a Java project with 12 commits) with git clone, so both are at the same commit ID as the remote repository. Each makes a change and commits it locally: Developer 1 updates app.java and Developer 2 updates pom.xml.
Developer 1 pushes first with git push origin master without any problem. When Developer 2 pushes, it fails with the message that the updates were rejected because the remote contains work they do not have locally, since the remote repository is no longer at the commit Developer 2 started from. To resolve it, Developer 2 runs git pull, which merges the remote changes and adds an extra merge commit to the local repository, and then pushes again. The remote repository now shows 15 commits, including the merge commit. The best practice is to pull before pushing your changes. The lecture also says that using your own branch instead of the one you pulled is the way to avoid these conflicts, and branches are covered later in the course.
So far the repositories have been cloned with HTTPS links, which use a username and password. This lecture sets up SSH key-based authentication instead, which is more secure than a password, following the "Connecting to GitHub with SSH" page in the GitHub documentation.
On the Developer 2 Linux system, you:
generate the keys with ssh-keygen, which creates id_rsa and id_rsa.pub under /root/.ssh
start the SSH agent and add the private key to it
Cloning a test repository with the SSH link first fails with "permission denied". You then copy the public key into GitHub under Settings > SSH and GPG keys (named "developer 2"), and the clone succeeds. After adding a file, committing, and running git push origin master, the push no longer asks for credentials.
The same steps are repeated on the Developer 1 Windows system, where the keys already exist: you start the agent, add the key, add the public key to GitHub as the Developer 1 key, and the clone now works. This way, even 10 developers on the same repository don't need to share credentials.
So far the repository was created on GitHub first and then cloned. This lecture works in the opposite direction: a repository created locally with git init (demo repo, with three commits) is updated to GitHub so Developer 2 can pull it.
Running git push origin master first fails, because Git doesn't know which remote repository to push to. Inside the .git directory, the config file has no remote origin entry, unlike a repository that is already linked to GitHub. You then create a repository with the same name on GitHub without initializing a README, which shows the instructions for an existing local repository. Running git remote add origin with the repository URL adds the remote origin entry to the config file, and git push origin master now works with the SSH link and no credentials. GitHub shows the three files and three commits, and Developer 2 can clone the repository with either the SSH or the HTTPS link.
This lecture shows how a developer sets up a system and pushes code to a GitHub account, using Developer 1 as a Java developer. It lists the tools installed: Apache Maven (a build tool), Eclipse (an IDE), and Tomcat 9 for testing the code locally before pushing. In Eclipse you create a new Maven project from the web app archetype, which generates the default directory structure in the workspace. Then you turn the project folder into a repository with git init ., add everything with git add ., and commit it as the initial commit. You create a repository on GitHub without a README, so no extra commit is created, and link it to the local repository with git remote add origin. Pulling before you push is the best practice. Since this is the first push, you set the upstream, meaning which local branch is associated with which remote branch, and push with git push -u origin master. The code now appears on GitHub, and other developers can take it and work on the same project.
This lecture revisits Git commits in more detail, using a Docker-related repository with 29 commits. git log shows the commit history: each commit's ID, author, email, and message, and several contributors' changes, some made directly through GitHub rather than from a local Git client. HEAD always points to the latest commit and the files matching it, and switching to an earlier commit changes both.
You compare commits with git diff, using either full or short (seven-character) commit IDs. Lines in red belong to the latest commit, lines in green belonged to the previous commit and are no longer present, and unchanged lines appear in white. You can also compare using HEAD and a relative reference, such as git diff HEAD HEAD~1 for the previous commit, HEAD~2 for two commits back, and so on, using a tilde (~) rather than a minus sign.
This lecture shows how to see what changed in one specific commit, rather than comparing two. git log gives the commit SHA (also called the commit ID), where HEAD points to the latest local commit and origin/master points to the same commit on the remote repository. git show followed by the SHA, or git show HEAD, displays what was added and removed in that commit. You can also check earlier commits with HEAD~2 for two commits back, and so on.
To see who changed each line of a specific file, use git annotate followed by the file name. It lists every line with the author, the commit ID, and when the change was made, which makes it much easier to trace a bug to the commit that introduced it, compared with checking each commit individually with git log. The next lecture looks at how commits appear on GitHub.
This lecture finds the same commit information on GitHub that earlier lectures found with Git commands. GitHub's commit list matches git log, and clicking a commit shows the same information as git show: which file changed, with additions in green and deletions in red. There's no direct GitHub equivalent to git diff, but viewing a specific commit's code shows what it looked like at that point, similar to using git checkout with a commit ID locally.
To see who changed each line of a file, GitHub's Blame view is the equivalent of git annotate. It shows each line, the commit that last changed it, and the commit author, and clicking further back shows earlier versions of that line and what changed in each commit. The next lecture covers committing changes directly on GitHub rather than from the local Git client.
One thing to check: the transcript briefly mentions "master" as representing the latest commit while browsing an older commit on GitHub, and switching commits with git checkout locally. Since git checkout moving HEAD isn't covered in earlier lectures, I kept the description at the same level of detail as the transcript.
This lecture shows how to create, edit, and upload files directly on GitHub, without using the local Git client. From the repository page, Add file lets you create a new file or upload one, alongside the Code option used for cloning.
You create a readme.md file with a heading (using # in markdown) and preview how it renders before committing, with a commit message and the choice to commit directly to the master branch, similar to git commit -m. Editing an existing file works the same way, with a Preview changes option before committing. You can also upload a file by dragging it in or choosing it from your system.
Since these changes were made only on GitHub, they aren't yet on the local system. Running git pull brings them in. After that, a file uploaded during the demo is removed locally, committed, and pushed back to GitHub with git push origin master.
This lecture explains why branches matter, using a simplified DevOps workflow: code goes into the SCM (GitHub), gets built and tested, produces artifacts, and is deployed to a server. By default, every repository has a master branch, and you can create other branches or even remove master since it's not mandatory.
If you commit and deploy directly from master every time, an untested or buggy commit can break the application already running in production. Instead, you push only working code to a separate branch (for example, a production branch), and only that branch triggers the build and deployment. Some commits on master may not work and stay out of that branch, while working commits get checked into it and deployed, keeping the live application stable and customers unaffected. The next lecture covers branching strategies, which explain how to know whether code is actually working before it gets checked into that branch.
This lecture walks through one branching strategy, using test, QA, and production branches with matching test, QA, and production systems. Code that works locally isn't pushed straight to production. Instead, it goes to the test branch first and is deployed to the test system. If it fails, it's fixed and pushed again, and once a version works there, it's considered stable. That stable version is then pulled into the QA branch and deployed to the QA system to confirm it again, while development continues on the test branch with newer changes that may or may not work. Once a version is verified on QA, it moves to production. Meanwhile, later versions can be at different stages: an early stable version running in production while newer versions are still being validated on QA or test. Branch names can vary (for example, dev instead of test, or pre-prod instead of QA), and the number of branches can be two, three, four, or more, but the strategy stays the same. A second branching strategy is covered later in the section, and the next lecture moves to hands-on practice.
This lecture puts the branching strategy into practice on the demo repo, which so far has three files, three commits, and everything done on the master branch. To try new features without risking the working code, or without confusing collaborators on the same branch, you create a new branch from master called "test". This creates a snapshot of the master branch's code at that point.
You add a new file, file5, on the test branch and commit it. The test branch now shows four commits, including this one, and file5 appears there but not on master, since the change was made only on the test branch. Once code on a branch like test is working (for example, after being deployed and verified on a test system), it can be brought into master through a pull request, which is covered later. The next lecture does the same branching activity from Git itself rather than the GitHub GUI.
This lecture repeats the branch creation from the local Git CLI. The test branch from the previous lecture is deleted on GitHub first, leaving only master with three commits, which matches the local repository.
git branch lists your branches and shows the active one with an asterisk; at this point there's only master. git branch test creates a new test branch pointing to the same commit as master, which git log confirms.
You can move HEAD to an earlier commit with git checkout <commit-id>, which shows the repository as it was at that point, for example without file1 and with file2.txt instead of file2. git switch - returns HEAD to where it was before. To move between branches, git checkout test switches HEAD to the test branch, and any changes you make from here are tracked only on that branch, leaving master untouched. The next lecture covers seeing those branch-specific changes.
This lecture creates separate commits on the test and master branches to show how they stay independent. On the test branch, you add file1, commit it, and see that git log shows test and HEAD at the new commit while master, origin/master, and origin/HEAD remain at the previous one. Switching back to master with git checkout master hides file1, since it exists only on the test branch.
The lecture uses a diagram to show how a new branch starts at the same commit as master, and each branch can then move ahead independently as commits are added to it. You then add file5 and commit it on the master branch, so master is now one commit ahead of origin/master, while the test branch's commit is separate. git push origin master pushes only the master branch changes. To push the test branch as well, you use git push origin test, which creates the test branch on the remote repository with its own commits. As a result, file1 exists only on the test branch, and file5 exists only on master. Adding two different commits together across branches needs git merge, covered in the next lecture.
This lecture merges the test branch, which has file1, into master, which has file5. git merge must be run from the destination branch, so you switch to master with git checkout master and then run git merge test. This brings file1 into master, and Git creates a separate merge commit for it, so master ends up with file1 through file5. git status now shows master ahead of origin/master by two commits, one for the file5 commit and one for the merge, and git push origin master pushes both to GitHub. On GitHub, master now shows the merge commit along with the individual commits from both branches.
A merge conflict happens when two developers change the same line of the same file and Git can't automatically decide which change to keep. This lecture recreates that using Developer 1 and Developer 2.
Developer 2 pulls the latest code, updates file2 with new content, commits it, and pushes it to the remote repository without any issue, since their local copy matched the remote before the change. Meanwhile, Developer 1, without pulling that update, creates a new branch called dev with git checkout -b dev and edits the same line of file2 differently, based on the old content. After committing this change on the dev branch, Developer 1 switches to master, pulls the latest changes (which now include Developer 2's update), and runs git merge dev.
This produces a merge conflict, since master and dev each expect a different version of that line. Git marks the conflicting section in the file with both versions. Resolving it means the developers involved discuss the change and decide what the file should contain, then edit the file directly to keep the intended content, here both lines. After editing, you stage and commit the resolved file with git add . and git commit -m, and then push it with git push origin master. GitHub then shows the merge conflict resolution as its own commit, with both lines present in file2.
Forking copies a repository from one GitHub account to another, unlike cloning, which copies a repository to your local system. It's useful when you find a public repository that fits your needs, such as an existing travel-related project, and want your own independent copy to work on. Forking creates a snapshot with the full commit history, and changes made afterward don't affect the original.
The lecture demonstrates this by searching GitHub for existing "cab booking" projects and forking one to show the general idea. It then forks the presenter's own Docker repository (from the "ravdy" account) into a second account ("aynkills") using the Fork button, so the second account gets its own independent copy. The next lecture covers making changes on the forked copy and submitting them back to the original repository through a pull request.
This lecture takes the forked repository from the previous lecture, updates it, and sends the changes back to the original repository. From the second GitHub account , you clone the forked repository with git clone over HTTPS, since SSH is only set up for the original account.
You reorganize a few Docker files into a Dockerfile directory and edit a file. git status shows the moved files as deletions and additions until git add . is run from the repository root, after which it's recognized as a rename. You commit with git commit -m and push with git push origin master, entering the second account's credentials. This account's repository is now one commit ahead of the original.
You then create a pull request from the second account to the original one, choosing the source and target branches and reviewing the changed files before creating it. On the original account, the pull request shows up with the changes and commit details, but only the account owner can merge it, not the person who created the pull request. The owner reviews it, merges it, and can leave a comment, which the other account can then see on the closed pull request.
The lecture also notes the difference between merge and pull request: merge combines two branches within the same repository, while a pull request moves changes from one account's repository to another's.
Unlike the public repositories used so far, this lecture creates and works with a private repository. When creating a new repository on GitHub, choosing Private instead of Public means it won't be visible to other users and won't appear in another account's repository list.
Cloning a private repository with HTTPS asks for credentials, since only authorized users can access it, while a public repository doesn't. Cloning it with SSH, using keys already authorized for that account, works without asking for credentials. On GitHub, private repositories are marked with a lock icon, while public ones have no such marker.
After cloning, you create a file, commit it, and push it with git push origin master over SSH, again without needing credentials. This is how private repositories keep your code secure and accessible only to people you've given access to, whether through account permissions or authorized SSH keys.
When you trust another developer to work closely on a repository, adding them as a collaborator avoids needing a pull request for every one of their commits. In this lecture, "ravdy" invites "aynkils" as a collaborator on the Docker repository through Settings > Manage access. The collaborator must have already cloned the repository. The invitation goes as an email that waynkils accepts.
Once accepted, waynkils clones the repository, makes a change, commits it, and pushes it with git push origin master over HTTPS using the waynkils account credentials. Because waynkils is now a collaborator, the push goes directly onto the repository without a pull request. The same is shown by editing a file directly on GitHub while logged in as waynkils, which also commits directly rather than proposing a change.
To show the difference, the lecture tries the same thing on a separate Java demo project where waynkils is not a collaborator: editing a file there prompts "Propose changes" and requires a pull request instead, as before.
Even with a trusted collaborator, pushing directly to master risks breaking working code. This lecture protects the master branch so no one, including the owner, can commit to it directly. A separate dev branch stays open for everyday commits from either user.
In Settings > Branches, a branch protection rule is added for master, using the "require pull request review before merging" option, with one required approval since there are only two collaborators. After this, trying to edit a file directly on master (as either user) is blocked and instead creates a new branch or asks for a pull request.
Working changes are made on the dev branch, which isn't protected, and then merged into master through a pull request that the other collaborator has to approve before it can be merged. An example where the reviewer requests changes and the pull request is closed without merging is also shown, alongside a successful merge, to demonstrate how protected branches keep only reviewed, working code on the protected branch.
A tag is a user-friendly pointer to a specific commit, so you don't need to remember its SHA. It's commonly used to mark a version release, and unlike a branch, a tag doesn't move as new commits are added.
On the Docker repository, git tag version1.0 tags the current commit as version1.0. git tag and git log show it attached. The tag is pushed to the remote repository with git push origin version1.0. If later commits break something, you can find version1.0 on GitHub's Tags page and go back to that exact commit without needing its SHA.
After pulling more commits, a newer working commit is tagged as version1.1 using git tag -a version1.1 with a commit ID, since it's an annotated tag rather than a lightweight one. This tag is also pushed with git push origin version1.1. GitHub now shows both tags, each pointing to its own commit, so you can always fall back to a known-working version.
There are three scenarios for reverting changes: changes only in the working directory, changes staged, and changes already committed to the local repository. This lecture covers the first one.
To discard changes made to a tracked file in the working directory, use git restore <file> or git checkout -- <file>. Both undo the file back to its last committed state. Neither works for a newly created file, since Git doesn't track it yet, so a new file is simply deleted with rm instead.
The lecture demonstrates this by editing index.html (changing <h1> to <h2>) and creating file1. git status shows both as untracked or modified changes in the working directory. file1 is removed directly, while index.html's change is undone with git restore and then again with git checkout -- index.html, both restoring the file to its previous content, confirmed with git status showing a clean working directory. The next lecture covers reverting changes from the staging area and the local repository.
This lecture covers the other two revert scenarios: staged changes and committed changes.
To discard a change already staged, first move it back to the working directory with git restore --staged <file>, then discard it from the working directory with git restore <file> (or git checkout -- <file>), as covered in the previous lecture. Both steps are needed, since staged changes still need a second command to clear them from the working directory.
To undo a commit that hasn't been pushed to the remote repository, use git reset HEAD^ (or git reset HEAD~1), which moves HEAD back one commit while keeping the changes in the working directory. From there, the same working-directory commands discard them if needed. The lecture demonstrates this on index.html: staging a change, unstaging it with git restore --staged, discarding it from the working directory, then committing a different change and reverting it with git reset HEAD^ followed by git checkout -- index.html.
Once a commit is pushed to the remote repository, it's not backed out, so reverting isn't as simple. This kind of revert is needed rarely, and the lecture recommends being careful before pushing.
Some files shouldn't be committed to a repository, and instead of manually removing or moving them each time, a .gitignore file tells Git to skip them automatically.
The lecture creates a .gitignore file in the repository and adds improve.txt to it, so git status no longer shows that file. After committing and pushing the .gitignore file, it also demonstrates ignoring multiple files by listing each one, then using a wildcard pattern like *.txt to cover all files with that extension in one line instead of listing them individually. Only changes to .gitignore itself get committed; the ignored files never show up in the repository.
A reference document with more .gitignore patterns and combinations is mentioned as being added to the course resources.
git rebase is used to squeeze multiple commits into a single one, so the commit history doesn't show every small step. This lecture creates a new repository and adds five commits to the same file, number.txt, each adding a line ("number one" through "number five"). The commands git commit -a -m is used for later commits, since it stages and commits already-tracked file changes in one step.
To squeeze the last four commits into one, git rebase -i HEAD~4 opens the interactive rebase list. Each commit is marked pick or squeeze: pick keeps that commit, and squeeze folds a commit into the one above it. Here, the first of the four listed commits is picked and the other three are squeezed into it, then a single combined commit message is written for all the changes. After saving, git log shows two commits total instead of five, though the file itself still contains all the added lines, since the content is unchanged, only the commit history is condensed.
One thing to check: Git's actual rebase option is squash, not "squeeze." The transcript consistently says "squeeze," so please confirm which term to use in the video or on-screen text, since students following along will need to type the correct one.
git pull does two things at once: it syncs the local repository with the remote repository's commits and updates the working directory with those changes. git fetch only syncs the commit history with the remote repository, without touching the working directory; a separate git merge is needed to bring those changes into your files. In short, git pull = git fetch + git merge.
The lecture demonstrates this on number.txt. After committing and pushing local changes, more commits are added directly on GitHub. Running git pull origin master (or just git pull) brings both the commit history and the file content up to date locally. More commits are then added on GitHub again, and this time git fetch origin master (or git fetch) updates the local commit history, confirmed with git log, but number.txt still shows the old content. Running git merge afterward brings the file up to date with the fetched changes. Use git fetch when you want to see what's changed remotely without immediately updating your working directory.
This lecture introduces the final real-time project, a car rental Java application, to show how the earlier Git and GitHub topics come together in a real workflow. As a DevOps engineer setting up a repository for a client, the plan is:
Create a private repository.
Delete the default master branch and create three branches instead: prod, uat, and dev.
Add four developers as collaborators, each with their own GitHub account.
Enable SSH-based, passwordless authentication for all of them.
Protect the prod and uat branches so developers can push freely to dev, but changes to uat need one approval and changes to prod need two.
Require a successful build and deployment on dev before code moves to uat, and on uat before it moves to prod, so only working code advances.
For the demonstration, four systems represent the four developers: the Windows laptop (Developer 1) and three separate systems (Developer 2, 3, and 4), though one person plays all four roles. Developer accounts named "aynkil" and "Valaxy" are used so they can approve pull requests into uat and prod. The next lecture sets up this environment.
This lecture sets up the environment from the previous one for the taxi booking project.
Create a private repository named "taxi-booking" with a README, which comes with a default master branch.
Create three branches from master: dev, uat, and prod.
Change the default branch to dev (since a default branch can't be deleted), then delete the master branch, leaving only dev, uat, and prod.
Protect the uat and prod branches under Settings > Branches with "require pull request reviews before merging": one required reviewer for uat, two for prod. This branch protection option needs a paid GitHub account for private repositories. Administrators aren't included in the rule here, so they can push freely if needed.
Add collaborators under Settings > Manage access: "Vyankil" (representing Developer 2) and "Veloxy Infotech" (representing another team member), reusing existing accounts for the demonstration rather than creating new ones for each developer.
Remaining setup, SSH authentication for the developers and enforcing successful builds before code moves to uat or prod, is covered in later lectures.
This lecture finishes the SSH setup and gets each developer's system ready to work on the taxi booking repository.
For Developer 3 and Developer 4 (Developer 1 and Developer 2 were already set up earlier in the course), you generate SSH keys with ssh-keygen, add them to the SSH agent, and add each public key to the GitHub account under Settings > SSH and GPG keys. With this done, all four developers can push to unprotected branches without needing credentials.
Tasks are assigned: Developer 1 works on the landing page, Developer 2 on the services section, Developer 3 on the cars and driver/garage information, and Developer 4 on car availability and customer contact details, using an existing taxi-rental style application as source material rather than writing new code from scratch.
Each developer then clones the taxi-booking repository using the SSH URL (so no credentials are needed) on their own system. At this point each clone contains only the README file and the single initial commit. The next lecture covers the developers starting to write and push their code.
Developer 1 adds the home page code to the taxi-booking repository under webapp/src/main/webapp, commits it, and pushes it with git push origin dev, since the repository has no master branch. Pushing doesn't ask for credentials, since SSH is already set up.
Before this code can move toward UAT and production, it needs to be built and deployed to a test system so it can be checked. A Jenkins job ("taxibooking_dev") is set up for this: it pulls from the dev branch using the HTTPS URL with stored credentials, builds the Maven project with the goals clean install package, and deploys the resulting WAR file to a dev Tomcat server (port 8080), also using stored credentials. After a successful build and deploy, the home page shows a couple of bugs (missing car images and prices not shown in rupees), which would normally go back to the developer to fix.
Meanwhile, Developer 2 pulls the latest dev branch code with git pull, adds the services page under the same webapp path, commits, and pushes to dev. Rebuilding and redeploying through the same Jenkins job shows the services page isn't displaying, so a fix is committed and pushed, and the next build and deploy confirms it now works.
With the home page and services page both verified on the dev environment, the next step is getting the project manager's approval to move this code to UAT, which the next lecture covers.
With the home page and services page working on the dev environment, this lecture moves that code to UAT through the approval process. First, "Vyankils" accepts the collaborator invitation. Then a pull request is created from the dev branch to the UAT branch, titled "Adding home and services pages," which requires review from Vyankils and Veloxy Infotech before it can be merged.
The account that raised the pull request can't approve its own request, so Vyankils reviews the changes and approves them, since the working code (the fix that resolved the earlier bug) is what's moving forward. After approval, the pull request is merged into the UAT branch, and UAT now has the same code that was on dev. The next lecture covers deploying this code to the UAT environment using Jenkins, pointing to the UAT system instead of the dev system.
This lecture completes the workflow, taking the taxi booking application from dev through UAT and into production.
A new Jenkins job, "taxibooking_uat", is created by copying the dev job and updating it to build from the UAT branch and deploy to the UAT server. After deployment, only the home page and services page are live there, matching what was merged earlier. That commit is tagged as version 1.0 with a GitHub release.
Development continues on the dev branch: Developer 3's code (cars, drivers, garage) is added, followed by Developer 4's code (the remaining pages), each pulled, copied in, committed, and pushed to dev, then rebuilt and checked on the dev environment until everything works without bugs.
Once the full application works on dev, a pull request merges dev into UAT (one approval required), and the UAT Jenkins job deploys the complete application there. After confirming it works on UAT, another pull request merges UAT into production (here approved directly as an admin), and a new Jenkins job, "taxibooking_prod", deploys it to the production Tomcat server, which is started for the first time. The final check confirms the complete application is live on production.
This shows how Git and GitHub, together with Jenkins, support a full DevOps release process, from development through UAT to production, and how a UAT or dev environment URL can be shared to show ongoing progress to a customer. This looks like the final lecture in the technical content, with a closing lecture to follow.
Not sure where to start your DevOps journey? or
Want to know what kind of activities a DevOps Engineer does on Git and GitHub in the real world? or
Would you like to set up a production-ready Git environment for your developers? Then this course is for you. I have created this course from the perspective of a DevOps Engineer who is not writing application code much. I have taken a real-world project to explain from creating a repository to releasing code onto the production environment. This gives a complete understanding of the power of Git and GitHub. I hope you will enjoy this course.
We have covered various concepts like
What is a version control system
installing git on windows, Linux, and mac
working with git bash
creating repositories
git stages
git workflows
creating GitHub account
cloning repository
push code onto the remote repository
git clone vs. git pull
git remote add
working with commits on git
git branches
branching strategies
committing changes on git branches
resolving merge conflicts
Fork a repository
creating a pull request
working with private repositories
adding a collaborator
creating protected branches
tagging a commit
reverted changes
using .gitignore file
git rebase
git fetch vs. git pull
how the git project does work
setup git repository and branches for a new project
allowing developers to check in code
Enabling DevOps workflow on the Dev branch
pull request (PR) to merge code from Dev to production
Release code onto production