Abstract
| Goal |
Manage automation code in version control and run Ansible Playbooks from a centrally managed automation controller. |
| Objectives |
|
| Sections |
|
| Lab |
|
Git is a distributed version control system (DVCS) that enables you to collaboratively manage changes to files.
Using Git provides many benefits:
You can review and restore earlier versions of files.
You can compare two versions of the same file to identify changes.
You can record a log of who made what changes, and when those changes were made.
Multiple users can collaboratively modify files, resolve conflicting changes, and merge the changes.
Using Git, you can start by cloning an existing shared project from a remote repository. Cloning a project creates a complete copy of the original remote repository as a local repository. This local copy has the entire history of the files in Git, and not just the latest snapshot of the project files.
You make edits to files in a working tree, which is a checkout of a single snapshot of the project. Next, you place the modified files in a staging area, ready to commit the changes to the local repository. At this point, no changes have been made to the shared remote repository.
When you are ready to share the completed changes, you commit the changes and then push the changes to the remote repository.
Likewise, when you are ready to update the local repository with the latest changes to the remote repository, you pull the changes, which fetches them from the remote repository and merges them into the local repository.
To use Git effectively, you must be aware of the three possible states of files in the working tree: modified, staged, or committed.
| State | Description |
|---|---|
| Modified | You edited the copy of the file in the working tree and it is different from the latest version in the repository. |
| Staged | You added the modified file to a list of changed files to commit as a set, but the staged files have not been committed yet. |
| Committed | You committed the modified file to the local repository. |
Because Git users often modify projects with multiple contributors, Git records the user's name and email address on each of their commits.
You can define these values at a project level, but you can also set global defaults for a user.
The git config command controls these settings.
Using git config with the --global option manages the default settings for all Git projects to which you contribute by saving the settings in your ~/.gitconfig file.
[user@host ~]$git config --global user.name 'Peter Shadowman'[user@host ~]$git config --global user.email peter@host.example.com
When you first start working on an existing project, you clone a remote repository to create a local repository in a subdirectory of the current directory.
This process creates a working tree of files ready for revisions. Because the working tree is unmodified, it is initially in a clean state. A clean state means the working tree does not contain any modified, staged, or new files.
To clone a repository from the command line, use the git clone command, followed by the URL that specifies the location of the remote repository that you want to clone.
For example, the following command clones the netconfig.git repository at git.lab.example.com by connecting using the SSH protocol and authenticating as the git user:
[user@host ~]$ git clone git@git.lab.example.com:netconfig.gitTo clone a repository in VS Code, click the icon in the left navigation menu, or click → to display the Source Control panel. Click in the Source Control panel.
![]() |
As you work, you create new files and modify existing files in the working tree. This changes the working tree to a dirty state.
The git add command stages files, preparing them to be committed.
Only files that are staged to the staging area are saved to the repository on the next commit.
To add all changed files in the current directory and subdirectories, use the git add . command.
To add all changed files in every directory in the entire working tree, use the git add -A command.
The git add . and git add -A commands can simultaneously add multiple files to the staging area.
If you are working on multiple changes at the same time, then you might want to add each file separately so that you can organize the files into different commits for better tracking of changes.
To view staged files in VS Code, click the icon in the left navigation menu.
To include a file in the next commit, click the plus (+) symbol next to the file to add the file to the Staged Changes section. Click the minus (-) symbol to remove changed files from the Staged Changes section.
![]() |
Note that a capital M character appears next to modified files.
To commit your staged changes, type a commit message in the upper text box and click . This saves your changes to the local Git repository.
Commit messages do not have to be long, but they should be meaningful so that they are useful.
Meaningful and concise commit messages are the key to maintaining a clear history for an Ansible project. Use descriptive commit messages and add references to issues, tickets, and related people where applicable.
From the command line, the git push command uploads commits you made in your local repository to the remote repository.
One common way to coordinate work with Git is for all developers to push their work to the same, shared, remote repository.
Likewise, the git pull command downloads changes to your local repository from the remote repository.
Use the git pull command to perform the following tasks:
Fetch commits from the remote repository.
Add those commits to the local directory.
Merge changes into the files in the working tree.
Run the git pull command often to stay current with the changes that others are making to the project in the remote repository.
To synchronize changes by using git pull and git push in VS Code, click the menu (…) and then click or , or choose an option in the submenu.
![]() |
You can use branches to update your project and work on new development without altering your main branch. When you are satisfied with your changes, you can merge the changes in your branch to the main development branch, integrating them.
A branch is a pointer to a particular commit in your commit tree. Each commit contains the changes it made, information about how to reference it, and what its relationship is to other commits in the commit tree.
Git version control features a branching model to track code changes.
All Git repositories have a base branch.
Many projects prefer to use main as the name of the base branch.
When you create a commit in your repository, the base branch is updated with the new commit.
Git uses the term HEAD to describe the pointer to where you are in the currently active branch.
By convention, the main or master branch in a Git repository contains the latest, stable version of the source code.
To implement a new feature or functionality, create a branch from the main branch.
This new branch, called a feature branch, contains commits corresponding to code changes for the new feature.
The main branch is not affected by commits to the feature branch.
When you use a branch for feature development, you can commit and share your code often without impacting the stability of code in the main branch.
After ensuring that the code in the feature branch is complete, tested, and reviewed, you are ready to merge the branch into another branch, such as the main branch.
Merging is the process of combining the commit histories from two separate branches into a single branch.
Git has sophisticated mechanisms to merge code changes from one branch into another branch. However, if there are changes to the same file in both branches, then a merge conflict can occur.
A merge conflict indicates that Git cannot automatically determine how to integrate changes from both branches. When this happens, Git inserts markers in each affected file to indicate the sections that contain content conflicts from both branches.

For each conflict in a file, replace the content between the merge conflict markers (<<<<<<<< and >>>>>>>> inclusive) with the correct content from one branch or the other, or an appropriate combination of content from both branches.
You can also click and resolve the merge conflict there.

After you reconcile the content for each conflict in a file, you need to save, stage, and commit the file changes. You can then synchronize with the remote repository.
For more information on merge conflicts, see Basic Merge Conflicts at https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging
Different branches in Git enable different work streams to evolve in parallel in the same Git repository. Commits for each work stream append only to that branch.
From the command line, you can use the git branch command to create a branch from the current HEAD commit.
This command creates a reference for the new branch, but it does not set the current HEAD to this branch.
Use the git checkout command to move the HEAD to the appropriate branch.
To create a branch in VS Code, click the icon in the left navigation menu, or click → to display the Source Control panel. Click the icon (…) and then click → .

Some Git servers, such as GitHub, GitLab, and Bitbucket, allow you to protect branches from unwanted changes.
As a best practice you might protect certain branches on the remote repository, such as the main branch, to avoid merging unwanted changes to them from other branches.
These servers also often implement a feature called pull requests or merge requests. This provides a mechanism that you or other code reviewers can use to review and approve requests before the branch can be merged with the protected branch on the remote server.
Part of the point of a version control system is to track a history of commits. A commit hash identifies each commit.
The git log command displays the commit log messages with the associated ID hashes for each commit.
The git show command shows what was in the change set for a particular commit hash.
You do not need to use the entire hash with this command; only enough of it to uniquely identify a particular commit in the repository.
These hashes can also be used to revert to earlier commits or otherwise explore the version control system's history.commit-hash
Table 3.1. Git Quick Reference
| Command | Description |
|---|---|
git status
| Display the status of files in the working tree. |
git log
| Display the commit log messages. |
git show
| Show what was in the change set for a particular commit hash. |
git revert
| Create a commit, undoing the changes in the referenced commit. You can use the commit hash that identifies the commit, although there are other ways to reference a commit. |
This is a simplified introduction to Git. It has made some assumptions and avoided discussion of other important topics related to branches and merging. Some of these assumptions include:
You cloned the local repository from a remote repository.
You configured the local branch to pull from and push to a branch on the original remote repository.
You provided write access to the remote repository, such that git push works.
Each Ansible project should have its own Git repository.
The structure of the files in that repository should follow the directory layout recommended at https://docs.ansible.com/ansible/6/user_guide/sample_setup.html#sample-directory-layout.
For example, the directory structure might appear as follows:
site.yml # main playbook
ios.yml # playbook for IOS managed nodes
junos.yml # playbook for Junos managed nodes
collections/ # holds Ansible Content Collections in directories
requirements.yml # specifies collections needed by this project
roles/
requirements.yml # specifies roles needed by this project
...additional roles...Ansible Roles and Ansible Content Collections are ways that you can reuse code from other Ansible projects.
You can include roles and Ansible Content Collections in your Ansible project's Git repository.
In this case, each collection is generally stored in subdirectories of the collections/ directory, and each role is stored in subdirectories of the roles/ directory.
There might also be a requirements.yml file in either or both of those directories that specifies collections or roles that are used by this project.
A role or collection can have its own Git repository. Then instead of keeping a copy of the role or collection in your Ansible project's repository, automation controller can retrieve it directly from the relevant role or collection repository when it runs playbooks that belong to the project.
To set this up, create requirements.yml files in your roles/ and collections/ directories.
Then use the ansible-galaxy command to populate this directory with the latest version of roles from automation hub, Ansible Galaxy, or their Git repositories before you run the project's playbooks.
Automation controller automatically updates the project with roles and collections based on the roles/requirements.yml and collections/requirements.yml files included in the Ansible project.
Not every file and directory in your Ansible project directory should be tracked by Git. You can configure Git to ignore files and directories that should not be tracked or committed.
To configure Git to ignore specific files and paths in your project, add a .gitignore file at the top of your Ansible project directory.
Each line in a .gitignore file represents a pattern to match, and that determines whether Git ignores a file in the repository.
Generally, lines that match file names indicate files that Git ignores.
Lines that start with an exclamation mark (!) indicate files that Git should not ignore, even if they were matched by a previous pattern.
You can use blank lines to improve readability, and any line that starts with a number sign (#) is a comment.
Details on the pattern matching format for this file are documented in the gitignore(5) man page under "PATTERN FORMAT".
Pay particular attention to the distinction between single asterisks (*) and double asterisks (**) in these pattern matching lines.
(Double asterisks can match multiple directory levels.)
For example, if you use requirements.yml files with the ansible-galaxy command to populate the project's roles and collections directories, then you should configure Git to ignore the populated content.
A sample .gitignore file might contain the following content:
roles/** !roles/requirements.yml collections/ansible_collections
gittutorial(7), git(1), and gitignore(5) man pages
Sample Ansible Setup: Sample Directory Layout - Ansible Documentation