Have you ever been working on a Python project when it suddenly stopped working after you made a change somewhere or a PEP-8 cleanup somewhere else, and you weren’t exactly sure how to get it back up and running? Version control systems can assist you in finding solutions to this issue as well as others that are similar. Git is rapidly becoming one of the most widely used version control systems available today.
In this article, I will explain what Git is, how to use it for your own personal projects, and how to use it in conjunction with GitHub to collaborate on larger projects with other people. In this lesson, you will learn how to set up a repository, how to add new files as well as modify existing ones, and how to traverse through the history of your project so that you may “return” to a time when everything was functioning properly.
This post will proceed under the assumption that you already have Git set up on your computer. If you don’t already know how, the fantastic book Pro Git offers a chapter that will teach you.
that.
What Is Git?
Git is a version control system that operates on a distributed basis. (DVCS). First, let’s take a step back and examine just what it is.
means.
Version Control
A version control system (VCS) is a collection of programs that monitor the changes that have been made to a group of files over time. This indicates that you can instruct your version control system (in our case, Git) to store the current state of your files at any time. After that, you are free to continue editing the files and save them in their current state. Creating a backup copy of your working directory is analogous to saving the state of the application. When working with Git, this process of storing the current state is referred to as making a commit.
When you create a commit in Git, you are prompted to add a commit message. This message provides a high-level explanation of the changes that were made in this commit. Git has the ability to display the full history of all commits together with the commit messages for each one. This offers an informative history of the work that you have done and can be of great assistance in determining when an issue first appeared in the system.
Git not only provides you with a log of the changes you’ve made to the project, but it also gives you the ability to compare files from different commits. As I indicated before, Git also gives you the ability to revert any file or all files to a commit that was made previously with relatively little effort.
effort.
Distributed Version Control
So, let me get this straight—that is a version control system. What exactly is the part that is distributed? To provide a satisfactory response to that inquiry, one should usually begin with some background information. Earlier versions of version control systems were able to function by keeping all of those commits locally on the user’s hard disk. We refer to this assemblage of changes made in the past as a repository. While this solved the problem of “I need to get back to where I was,” it did not scale very well for a team that was working on the same codebase.
As more people began to collaborate in larger groups (and as the use of networks became more widespread), version control systems evolved to store the repository on a centralized server that was accessible by a number of different developers. Despite the fact that this resolved a large number of issues, it also brought forth new complications, such as the locking of files.
Git deviated from that concept, following the example set by a small number of other products. Git does not have a centralized server that stores the version of the repository that is considered canonical. Every user has a complete copy of the repository at their disposal. This implies that it is often difficult to get all of the developers back on the same page, but it also means that developers can work offline most of the time, only connecting to other repositories when they need to share their work. This allows developers to work more efficiently.
Because there are a large number of developers that utilize GitHub as a central repository, from which everyone is required to pull, the final sentence in this paragraph may at first appear to be a little bit perplexing. Although this is the case, Git does not require users to do so. When certain conditions are met, having a centralized location to share the code is simply more convenient. Even if you use a remote repository, the complete repository is kept on all of your local repositories.
GitHub.
Basic Usage
Now that we’ve gone over the fundamentals of what Git is, let’s take a look at how it works by going through a specific example. To get started, we will only use Git on our local system and nothing else. As soon as we have it under our belts, we’ll integrate GitHub and walk you through how to interface with it.
it.
Creating a New Repo
Before you can begin working with Git, you will first need to identify yourself to it. Through the git config, you are able to set your username.
command:
$ git config --global user.name "your name goes here"
After it has been set up, you will require a repository in which to carry out your work. It’s not hard to set up a repository. In a terminal, run the git init command.
directory:
$ mkdir example
$ cd example
$ git init
Initialized empty Git repository in /home/jima/tmp/example/.git/
When you already have a repository, you can inquire about it using Git. The git status command is going to be the one you make the most use of in Git. Give it a go.
now:
$ git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
This displays a handful of pieces of information to you, including the fact that you do not have anything to commit and the branch that you are now on, which is the master branch (we will discuss branches later). This final step indicates that the directory in question does not contain any files that Git is unaware of having existed. That’s great, because we recently finished making the
directory.
Adding a New File
Now you need to make a file that Git is not familiar with. Create the file hello.py using your preferred text editor. It should include nothing more than a print statement.
it.
# hello.py
print('hello Git!')
If you run git status once more, you will notice that the output has changed.
result:
$ git status
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
hello.py
nothing added to commit but untracked files present (use "git add" to track)
Git has now detected the newly added file and will inform you that it is untracked. This is simply Git’s way of indicating that the file in question is not a part of the repository and is not being controlled by versioning software. Adding the file to Git is the solution to this problem. To create that, you can use the git add command.
happen:
$ git add hello.py
$ git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello.py
Now that Git is aware of hello.py, it includes it in the list of modifications that need to be committed. Adding the file to Git places it in the staging area, which will be covered in the following section, and enables us to commit it to the repository.
repo.
Committing Changes
When you commit changes to a repository, you are instructing Git to take a snapshot of the repository in its current state. Execute the git commit command now to complete the task. Git is instructed to use the commit message that follows when it is given the -m option. If you do not use the -m option, Git will open an editor for you and prompt you to type in the commit message. Your commit messages should, in general, represent what has changed in the code.
commit:
$ git commit -m "creating hello.py"
[master (root-commit) 25b09b9] creating hello.py
1 file changed, 3 insertions(+)
create mode 100755 hello.py
$ git status
On branch master
nothing to commit, working directory clean
You can see that the commit command gave a lot of information, much of which isn’t very helpful; but, it does inform you that there was just one file that was modified. (which makes sense as we added one file). In addition to that, it gives you the SHA of the commit. ( 25b09b9 ). A brief digression on SHA will follow a little while later.
After running the git status command once more, we can see that our working directory is clean, which indicates that all of our recent modifications have been committed to Git.
At this juncture, it is necessary for us to pause the tutorial and have a little discussion regarding the staging.
area.
Aside: The Staging Area
Git, in contrast to many other version control systems, has a staging section. (often referred to as the index ). Git maintains a record of the modifications that you intend to include in your next commit in the staging region of the repository. When we used the command git add in the previous step, we informed Git that we desired to transfer the newly created file hello.py to the staging area. The git status has been updated to reflect this change. After being in the untracked area for some time, the file was moved to the output’s section for files to be committed.
Take note that the contents of the file that were present when you executed git add are mirrored exactly in the staging area. Should you choose to alter it once more, the file will show up in the staged as well as the unstaged portions of the status output.
When dealing with a file in Git, there are three possible versions of the file that you can work with at any given time (presuming that the file has previously been committed once).
with:
- the version on your hard drive that you are editing
- a different version that Git has stored in your staging area
- the latest version checked in to the repo
It’s possible that each of these three is a distinct version of the file. By first making changes in the staging area and then committing those changes, you may bring all of these different versions back into sync.
When I was first getting started with Git, I found the staging area to be a little bit unpleasant and a little bit unclear. It seems as though additional steps will be added to the process without contributing any further benefits. That is actually correct when you are initially becoming familiar with Git. After some time has passed, you will experience circumstances in which you will learn to genuinely enjoy having that functionality available. Unfortunately, this tutorial will not cover those kinds of situations because they go beyond its scope.
I strongly suggest that you read this blog article if you are interested in learning more specific information regarding the staging area.
.
You’ll discover that the status command comes in quite handy, and you’ll end up utilizing it quite a bit. On the other hand, there may be instances when you discover that there is a collection of files that appear in the untracked region that you would prefer Git to ignore completely. To address this issue, the.gitignore file was created.
Let’s look at an example to see how this works. Make a new Python file with the name myname.py and save it in the same directory.
.
# myname.py
def get_name():
return "Jim"
Then update your hello.py file to include myname and refer to it using its new name.
function:
# hello.py
import myname
name = myname.get_name()
print("hello {}".format(name))
Python will automatically convert a local module to bytecode and save the resulting file to your disk whenever you import a local module. It will leave a file in Python 2 with the name myname.pyc, but for the sake of this discussion, we’ll pretend you’re using Python 3. In that circumstance, it will generate a __pycache__ directory and save a pyc file in that location. This is what the evidence suggests.
below:
$ ls
hello.py myname.py
$ ./hello.py
hello Jim!
$ ls
hello.py myname.py __pycache__
Now, if you type git status into your terminal, you’ll notice that particular directory listed under the untracked section. Note that your newly created myname.py file is not being tracked, but the modifications you made to hello.py have been moved to a new section that has been titled “Changes not staged for commit.” This merely indicates that those modifications have not yet been brought into the staging area for consideration. Try it out with me.
out:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: hello.py
Untracked files:
(use "git add <file>..." to include in what will be committed)
__pycache__/
myname.py
no changes added to commit (use "git add" and/or "git commit -a")
Let’s do a little bit of cleaning up of the mess we’ve produced before we go on to the gitignore file and see how that goes. First, in the same way that we did before, we will add the myname.py and hello.py files.
earlier:
$ git add myname.py hello.py
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.py
new file: myname.py
Untracked files:
(use "git add <file>..." to include in what will be committed)
__pycache__/
Let’s put those modifications into effect and wrap up our clean up.
up:
$ git commit -m "added myname module"
[master 946b99b] added myname module
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 myname.py
When we now run status, all that appears on the screen is __pycache__.
directory:
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
__pycache__/
nothing added to commit but untracked files present (use "git add" to track)
We are going to add a.gitignore file to our repository so that any and all __pycache__ folders, as well as the contents of those files, will be ignored. This is not nearly as complicated as it may sound. Make changes to the document in the program of your choice (don’t forget to put a period in front of the file name!).
editor.
# .gitignore
__pycache__
The __pycache__ directory is no longer displayed for us when we execute the git status command. On the other hand, we can see the new.gitignore file! Take a
look:
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
nothing added to commit but untracked files present (use "git add" to track)
That file is nothing more than a standard text file, and as such, it can be uploaded to your repository in exactly the same way as any other file. Carry it out
now:
$ git add .gitignore
$ git commit -m "created .gitignore"
[master 1cada8f] created .gitignore
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
The directory in which you keep your virtual environments is another common entry in the.gitignore file. You may find more information about virtualenvs on this page, although the directory for a virtualenv is typically referred to as env or venv. You can include these in the.gitignore file that you use. It does not matter if the files or folders are there because they won’t be used. If this is not the case, then nothing will take place.
It is also feasible to keep a copy of a global.gitignore file in the home directory of your user account. If your editor loves to store temporary or backup files in the local directory, this comes in quite helpful.
This is an example of a straightforward Python.gitignore file.
file:
# .gitignore
__pycache__
venv
env
.pytest_cache
.coverage
Look at this link for a more comprehensive example; alternatively, if you wish to make your own, git help gitignore will give you all the information you need.
need.
What NOT to Add to a Git Repo
When you begin using any tool for version control for the first time, but especially Git, you could find yourself inclined to add everything to the repository right away. This is a mistake made by most people. Because of Git’s restrictions as well as the security considerations that it raises, you are need to restrict the kinds of information that you add to the repository.
To begin, let’s go through the most fundamental principle that applies to all version control systems.
You should never put produced files into version control; only source files should ever be checked in.
In the context of this discussion, a “source file” refers to any file that you generate, most commonly through the use of an editor and the typing method. A file that is generated is one that is created by the computer, typically as a result of the computer processing another file known as a source file. For instance, the file named hello.py is considered a source file, whereas the file named hello.pyc is considered a produced file.
There are two main reasons why why created files are not included in the repository. The first reason is that doing so is a pointless expenditure of both time and room. The files that were generated can be regenerated at any point, and it is possible that a new format will need to be used. If another person is using Jython or IronPython while you are utilizing the Cython interpreter, the.pyc files may look very different from one another. It’s possible that committing a certain flavor of files will generate a conflict.
The second argument for not retaining generated files is that these files are frequently larger than the original source files. This is the reason why generated files should not be stored. Because they are now part of the repository, everyone needs to download and save copies of the created files, even if they do not plan to use the files themselves.
This second point leads to another general rule about Git repositories, which is to commit binary files with caution and to avoid committing huge files as much as possible. This rule plays a significant role in the operation of Git.
Git does not keep a complete copy of every version of every file that you commit to the repository. Instead, it makes use of a complex algorithm that is derived from the changes that occur between successive versions of a file in order to drastically reduce the amount of storage space that it requires. Since binary files, such as JPGs or MP3s, don’t really have appropriate diff capabilities, Git will frequently just need to keep the entire file each time it is committed. This is because binary files can’t really be divided.
When dealing with Git, and particularly when working with GitHub, you should never put confidential material into a repository, and this is especially true for a repository that you might share publicly. Caution: Never place confidential material into a public repository on GitHub. This is so critical that I feel the need to repeat it. It is not recommended to commit sensitive information like passwords or API keys to a repository. Someone will eventually find them.
eventually.
Aside: What is a SHA
When Git keeps items (files, directories, commits, and so on) in your repository, it does so in a convoluted manner that involves a hash function. It is not necessary to dive into the specifics at this time, but a hash function is a process that takes something and generates a unique identifier for that thing that is significantly shorter. (20 bytes, in our case). In Git, this identifier is referred to as a “SHA.” It is not a guarantee that it is one of a kind, but for the vast majority of uses, it is.
Git indexes everything in your repository with the help of its hashing technique. The contents of each file are reflected in the SHA that is associated with that file. Hashes are generated for each directory in turn. The SHA of the directory will be updated automatically if one of the files within it is modified.
Along with other information, the SHA hash value (SHA) of the directory that is the root of your repository is included in each commit. That is how the whole status of your repository may be described by a single number that is 20 bytes long.
It’s possible that you’ve noticed that Git will occasionally display a message to you using the full 20-character value.
SHA:
commit 25b09b9ccfe9110aed2d09444f1b50fa2b4c979c
It will occasionally show you a shorter version.
version:
[master (root-commit) 25b09b9] creating hello.py
You will typically be presented with the complete series of characters, although it is not always necessary for you to make use of this information. Git’s rule is that the only information you need to provide is the minimum number of characters necessary to make the repository’s SHA string unrepeatable by other users. In most cases, a length of seven characters is more than adequate.
Git generates a new hashing algorithm (SHA) to describe the state of the repository each time you submit changes to it. In the following section, we will discuss the practical applications of SHAs.
sections.
Git Log
Another Git command that is used quite frequently is known as git log. The Git log provides a history of the commits that you have made up to this point in time.
point:
$ git log
commit 1cada8f59b43254f621d1984a9ffa0f4b1107a3b
Author: Jim Anderson <jima@example.com>
Date: Sat Mar 3 13:23:07 2018 -0700
created .gitignore
commit 946b99bfe1641102d39f95616ceaab5c3dc960f9
Author: Jim Anderson <jima@example.com>
Date: Sat Mar 3 13:22:27 2018 -0700
added myname module
commit 25b09b9ccfe9110aed2d09444f1b50fa2b4c979c
Author: Jim Anderson <jima@example.com>
Date: Sat Mar 3 13:10:12 2018 -0700
creating hello.py
As you can see in the listing that is displayed above, each of the commit messages for our repository are presented in the correct sequence. The term “commit” is always placed at the beginning of each commit, immediately followed by the commit’s respective SHA. The output of git log provides the history of each of the
SHAs.
Going Back In Time: Checking Out a Particular Version of Your Code
You have the ability to instruct Git to go back to any of your previous commits and examine the repository in the state it was in at that time since Git remembers each commit you’ve made along with its SHA. The following diagram illustrates what Git considers to be present in our repository:
There is no need to be concerned with the meaning of the terms master and HEAD in the diagram. These will be explained with a moment’s notice.
We will use the git checkout command to notify Git which SHA we want to look at in order to modify the point in our history at which we are now located. Let’s try
that:
$ git checkout 946b99bfe1641102d39f95616ceaab5c3dc960f9
Note: checking out '946b99bfe1641102d39f95616ceaab5c3dc960f9'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 946b99b... added myname module
In this article, there is a HUGE amount of material that is unclear. OK. First things first, let’s get some of these words defined. Let’s begin with the head first.
Git gives the moniker HEAD to the SHA that is currently being viewed by the user at any given time. What is NOT meant by this is whatever is on your filesystem or whatever is in the staging area. It indicates what Git believes you have checked out of the repository. If you make changes to a file, then the version that is stored on your disk will differ from the version that is stored in HEAD. (and yes, HEAD is in ALL CAPS).
The next item on our list is branch. One of the simplest ways to understand what a branch is and what it does is to think of it as a label on a SHA. Think of a branch as a SHA label for the time being, even though it possesses a few additional features that can be handy. Note: Those of you who have experience working with other version control systems, namely Subversion, will most likely have a completely different conception of what a branch is. Git takes a different approach to branches, which is a feature that should be encouraged.
When we consider all of this information together, we can see that having a detached HEAD indicates that your HEAD is referring to a SHA that is not coupled with a branch (or label). Git will provide you with clear and concise instructions on how to remedy the situation. There will be occasions when you will want to fix it, and there will be others when you will be able to function just fine in that detached HEAD condition. There will be times when you will want to fix it.
Let’s get back to our demo. If you take a look at the current status of the system, you’ll notice that the.gitignore file has been removed and is no longer there. We rolled everything back to the way it was before we made those changes to the system. The diagram of our repo in its current condition may be found down below. Take note of the fact that the HEAD pointer and the master pointer are pointing in separate directions:
Alright. How do we get back to the point were we were before? There are two different approaches, one of which you ought to be familiar with by now: git checkout 1cada8f. This will bring you back to the SHA that you were at before you began traveling about the area. Note: Despite the fact that you have returned to the SHA that is associated with a branch, Git will still warn you that the HEAD is detached from the repository. At least, this is the case in my installation of Git.
Check out the branch you were previously on, which is the alternative and more common method of getting back. Git will always begin working on your repository with the “master” branch selected by default. However, for the time being, we are just going to continue working on the master branch. In the future, we will learn how to create other branches.
You can easily return to your previous state by typing “git checkout master” into the terminal. This will bring you back to the most recent SHA that was committed to the master branch, which in our case bears the message “created.gitignore” in the commit message. To put it another way, when you type git checkout master, you are instructing Git to change the HEAD variable so that it points to the SHA that is associated with the label, or branch, master.
Take note that there are other options available for specifying a certain commit. The SHA is perhaps the simplest to comprehend of the three. When describing how to go to a particular commit from a given location, such as HEAD, the alternative approaches make use of a variety of distinct symbols and names. I won’t be getting into that level of detail in this tutorial; but, if you’re interested in further information, you can find it here.
.
Branching Basics
Let’s continue our discussion about branches for a moment. You may keep distinct lines of development distinct by using branches, which is a useful feature. When you’re part of a team, having this knowledge can be quite helpful, but when you’re working alone, it’s more of a convenience.
Imagine that I’m part of a small team that’s working on a project, and I have a feature that I want to add to it. I don’t want to add my modifications to master while I’m still working on it because it still doesn’t operate correctly and it could cause confusion for the other members of my team.
It is possible for me to hold off on committing the changes until I have finished everything, but this strategy is not very secure and is not always feasible. Instead of continuing to work on the master branch, I’m going to start a new branch.
branch:
$ git checkout -b my_new_feature
Switched to a new branch 'my_new_feature'
$ git status
On branch my_new_feature
nothing to commit, working directory clean
We informed Git that we wanted it to create a new branch by utilizing the -b option that is included on the checkout command. The output of executing git status on our branch, which can be seen above, reveals that the name of the branch has, in fact, been altered. Consider the following:
log:
$ git log
commit 1cada8f59b43254f621d1984a9ffa0f4b1107a3b
Author: Jim Anderson <jima@example.com>
Date: Thu Mar 8 20:57:42 2018 -0700
created .gitignore
commit 946b99bfe1641102d39f95616ceaab5c3dc960f9
Author: Jim Anderson <jima@example.com>
Date: Thu Mar 8 20:56:50 2018 -0700
added myname module
commit 25b09b9ccfe9110aed2d09444f1b50fa2b4c979c
Author: Jim Anderson <jima@example.com>
Date: Thu Mar 8 20:53:59 2018 -0700
creating hello.py
The log continues to have the exact same appearance, as you had hopefully anticipated it would. When you create a new branch, it will start from the location that you were at before you created the new branch. In this particular instance, we were at the very top of the master branch, which is indicated by the commit hash 1cada8f59b43254f621d1984a9ffa0f4b1107a3b; hence, that is where the new branch begins.
Let’s get to work on that new feature now. You should edit the hello.py file and then commit your changes. I’ll walk you through the commands to help refresh your memory, but I won’t show you the results of the commands for items that you’ve already seen.
seen:
$ git add hello.py
$ git commit -m "added code for feature x"
You may verify that our most recent commit was successfully added by using the command “git log.” My repository has a SHA hash value of 4a4f4492ded256aa7b29bf5176a17f9ed66efbb, however it is quite likely that your repository will have a different value.
SHA:
$ git log
commit 4a4f4492ded256aa7b29bf5176a17f9eda66efbb
Author: Jim Anderson <jima@example.com>
Date: Thu Mar 8 21:03:09 2018 -0700
added code for feature x
commit 1cada8f59b43254f621d1984a9ffa0f4b1107a3b
... the rest of the output truncated ...
Now, navigate your way back to the master branch, and have a look at the
log:
git checkout master
git log
Is the most recent commit labeled “added code for feature x” present?
You won’t have to put in as much effort because Git provides a method that is built in to compare the state of two branches. It’s the show-branch command, if you were wondering. The following is how it appears:
like:
$ git show-branch my_new_feature master
* [my_new_feature] added code for feature x
! [master] created .gitignore
--
* [my_new_feature] added code for feature x
*+ [master] created .gitignore
Because the chart that it generates can be interpreted in a few different ways at first glance, let’s go over it step by step. To begin, you have to invoke the command by providing it with the names of two different branches. In our particular circumstance, that was master and my_new_feature.
The remaining text in the output can only be deciphered by using the first two lines of the output. On every line, the first character that is not a space is either a * or a! first comes the name of the branch, then the commit message for the most recent change made to that branch, and finally the name of the parent branch. The symbol * is used to signal that the branch is currently checked out, whereas the! character is used to indicate that the branch has been deleted. is utilized by each and every other division. The character can be found in the column in the following table that corresponds to matching commits.
A divider is represented by the third line.
There are commits that begin on the fourth line that are only present in one of the two branches and not the other. In the circumstances we find ourselves in, doing so is not too difficult. One of the changes made to my new feature was not included in the master branch. On the fourth line, you’ll be able to see it. Take note of the fact that the line in question begins with a star in the first column. It is important that you specify which branch this commit is in using this.
In conclusion, the very final line of the report displays the very first commit that is shared between the two branches.
This is a relatively straightforward example. In order to provide a clearer illustration, I have made it more interesting by committing a few extra changes to the master branch as well as my_new_feature. Because of this, the output appears to be
like:
$ git show-branch my_new_feature master
* [my_new_feature] commit 4
! [master] commit 3
--
* [my_new_feature] commit 4
* [my_new_feature^] commit 1
* [my_new_feature~2] added code for feature x
+ [master] commit 3
+ [master^] commit 2
*+ [my_new_feature~3] created .gitignore
You are able to observe right now that each branch contains its own unique set of commits. It is important to take note that one of the commit selection techniques that I discussed before is the string [my_new_feature2]. You may make it show the SHAs if that is what you would prefer to view by using the –sha1-name option to the command.
command:
$ git show-branch --sha1-name my_new_feature master
* [my_new_feature] commit 4
! [master] commit 3
--
* [6b6a607] commit 4
* [12795d2] commit 1
* [4a4f449] added code for feature x
+ [de7195a] commit 3
+ [580e206] commit 2
*+ [1cada8f] created .gitignore
You now have a branch that contains a variety of commits from multiple different people. What steps do you take once you have successfully completed that function and are prepared to share it with the other members of your team?
Merging, rebasing, and cherry-picking are the three primary approaches that can be utilized to move commits from one branch to another. In the next paragraphs, we will discuss each of these in turn.
sections.
Merging
The process of merging is the easiest of the three to comprehend and put into practice. Git will generate a new commit when you perform a merge, and if necessary, it will combine the top SHAs of the two branches it is combining. If all of the commits in the other branch are ahead of the top of the current branch, it will simply do a fast-forward merge and place those new commits on this branch. This is because the top of the current branch is used as a reference point.
Let’s go all the way back to the point where our show-branch output appeared to be.
this:
$ git show-branch --sha1-name my_new_feature master
* [my_new_feature] added code for feature x
! [master] created .gitignore
--
* [4a4f449] added code for feature x
*+ [1cada8f] created .gitignore
Now, we want to make sure that the commit with the ID 4a4f449 is applied to the master branch. Start by switching to the master branch, then execute the git merge command.
there:
$ git checkout master
Switched to branch 'master'
$ git merge my_new_feature
Updating 1cada8f..4a4f449
Fast-forward
hello.py | 1 +
1 file changed, 1 insertion(+)
Since we were already working on the master branch, we merged the my_new_feature branch into our working copy. You are able to observe that this is a forward merge and identify the files that have been modified. Take a peek at the log, shall we?
now:
commit 4a4f4492ded256aa7b29bf5176a17f9eda66efbb
Author: Jim Anderson <jima@example.com>
Date: Thu Mar 8 21:03:09 2018 -0700
added code for feature x
commit 1cada8f59b43254f621d1984a9ffa0f4b1107a3b
Author: Jim Anderson <jima@example.com>
Date: Thu Mar 8 20:57:42 2018 -0700
created .gitignore
[rest of log truncated]
Git would have generated a new commit that comprised the mix of the changes from the two branches if we had made modifications to master before we merged the branches.
Git has a number of strengths, one of which is its ability to automatically merge changes and comprehend the common ancestors shared by many branches. Git is unable to determine the appropriate action to take if the identical part of code has been updated in both branches. When this occurs, it will interrupt the merging in the middle of its process and provide you with guidance on how to correct the problem. The term for this type of conflict is a merge conflict.
.
Rebasing
The process of rebasing is analogous to that of merging, but it operates somewhat differently. In the event that both branches have been modified, a new merge commit will be generated during the merge process. Git will take the commits from one branch and replay them, one at a time, on the top of the other branch when you do a rebasing operation.
Because it is difficult to build up a demo to explain this in text and because there is an outstanding website that covers the topic very well and that I will reference at the end of this, I will not do a comprehensive example of rebasing here. I will reference this website at the end of this article.
section.
Cherry-Picking
In the process of shifting commits from one branch to another, cherry picking is an alternative option. In contrast to merging and rebasing, cherry-picking requires you to be specific about which commits you intend to use. Simply naming one value is the most straightforward approach to taking this action.
SHA:
$ git cherry-pick 4a4f4492ded256aa7b29bf5176a17f9eda66efbb
Git is instructed to include all of the modifications that were made in the 4a4f449 branch into the one that is now active.
When you only want a single modification and not the entire branch that it was done on, this functionality can come in quite helpful. A Handy Hint Concerning Branching Before we part ways here, I just want to point you in the direction of a fantastic resource that can teach you all about Git branches.
Learn Git Branching provides its users with a series of exercises that use graphical representations of commits and branches in order to demonstrate the distinctions between merging, rebasing, and cherry-picking in an understandable manner. I
I cannot stress enough the need of devoting some time to working through issues.
exercises.
Working with Remote Repos
All of the commands that we have gone over up to this point only work with the local repository that you have. They do not communicate with a server or send any data over the network at any time. It has been discovered that the only significant Git commands that can communicate with remote servers are the following:
repos:
-
clone
-
fetch
-
pull
-
push
That sums it up nicely. Everything else is handled on the machine that is local to you. (It is true that there are other instructions that can be used to communicate with remotes; however, these commands are not considered to be fundamental.)
Allow me to walk you through each of these commands in
turn.
When you already know the address of a repository and you want to create a local duplicate of the repository’s contents, the Git clone command is what you use. Let’s use a little repository that I have on GitHub called github-playground as an example of how to use the service.
This location serves as the GitHub page for that repository. You will discover a “Clone or Download” button on that page. Clicking that button will provide you the URI that you need to input into the git clone command. Once you have that copied, you will be able to clone the repository.
with:
git clone git@github.com:jima80525/github-playground.git
You should now have a full repository of that project on the workstation you’re working on locally. This incorporates each and every commit along with each and every branch that has ever been made on it. ( Note: This repo was used by some friends while they were learning Git. I copied or forked it from someone else.)
If you wish to experiment with the other remote commands, you will need to establish a new repository on GitHub and proceed through the same processes as before. You are free to make a copy of the github-playground repository on your own account and use that instead. To fork a repository on GitHub, click the “fork” button that is located in the
UI.
It is necessary to take a step back and discuss how Git manages the interaction between your local repository and a remote repository in order to provide a thorough explanation of the fetch command. The next section contains background information; nonetheless, despite the fact that it is not something you would use on a regular basis, it will help you better understand the distinction between fetch and pull.
Git does not simply save one version of each file contained within a project when it clones a new repository for you to work with. It makes a copy of the repository in its entirety and then utilizes it to create a new repository on your local computer.
Git will not create any local branches for you other from the master branch. On the other hand, it does maintain a record of the branches that were stored on the server. Git will accomplish this by generating a number of branches, each of which will begin with the prefix remotes/origin/branch_name>.
Even though you will visit these remotes and origin branches only infrequently (nearly never), it is useful to be aware that they are there. Keep in mind that any branch that was present on the remote repository at the time that you cloned the repository will have a corresponding branch in the remotes/origin directory.
Git will mark your local branch as a tracking branch that is associated with a remote branch whenever you create a new branch and the name of the branch matches an existing branch on the server. When it comes time to pull, we’ll see how useful that information is.
It should not be too difficult for you to learn how to use git fetch now that you are familiar with the remotes and origin branches. The only thing that fetch does is bring all of the remotes and origin branches up to date. It will only edit the branches that are saved in remotes/origin, and none of your local branches will be affected.
branches.
Git pull is not a standalone command; rather, it is the result of combining two other operations. It begins by running git fetch to bring the remotes and origin branches up to date. Then, if the branch you are now working on is tracking a remote branch, it will perform a git merge of the branch that corresponds to the remote or origin location with your branch.
Consider the following scenario: you were working on the my_new_feature branch of the project, and a colleague had just added some code to the branch that was hosted on the server. Git will update ALL of the remotes/origin branches when you perform a git pull, and then it will perform a git merge remotes/origin/my_new_feature, which will get the new commit onto the branch that you are currently working on!
There are bound to be some restrictions imposed on this. If you have files on your local system that have been modified, Git will not even allow you attempt to perform a git pull. That can lead to an unacceptable amount of chaos.
If you have commits on your local branch, and the remote branch also contains new commits (which means that “the branches have diverged”), then the git merge phase of the pull will create a merge commit, just as we explained previously.
Those of you who have been paying attention will have noticed that in addition to doing a merging, you may instruct Git to perform a rebase instead by using the command git pull -r.
.
The git push command does the exact opposite of the git pull command, as you may have guessed. To a large extent, the opposite is true. When you use the push command, information about the branch you are pushing is sent to the remote, along with a question about whether or not it would like to update its own copy of that branch to match yours.
In most cases, this will include uploading the latest modifications that you have made to the server. What what is meant by the term “fast-forward commit” is a topic that is fraught with a great deal of nuance and specificity.
This place has a wonderful article that you should check out. The most important takeaway is that using git push causes your most recent commits to be visible on the remote.
server.
Putting It All Together: Simple Git Workflow
We’ve gone through a few of the most fundamental Git commands and how you might put them to use at this stage. I’ll conclude this up by providing a brief overview of one of the available workflows in Git. This approach is based on the assumption that you are working on your own local repository and that you have access to a remote repository to which you will push changes. It does not matter whether you use GitHub or another remote repository; the functionality will not change. It makes the assumption that you have already cloned the
repo.
-
git status
– Make sure your current area is clean. -
git pull
– Get the latest version from the remote. This saves merging issues later. -
Edit your files and make your changes. Remember to run your linter and do
unit tests
! -
git status
– Find all files that are changed. Make sure to watch untracked files too! -
git add [files]
– Add the changed files to the staging area. -
git commit -m "message"
– Make your new commit. -
git push origin [branch-name]
– Push your changes up to the remote.
This is one of the more fundamental movements that takes place throughout the system. You’ve only scratched the surface with your knowledge of Git after going through this course because there are so many different ways to use it. Check out these instructions if you use Vim or Sublime as your editor; they’ll show you how to get plugins that integrate Git into your workspace. You might wish to do so in order to save yourself some time.
editor:
-
VIM and Python – A Match Made in Heaven
-
Setting Up Sublime Text 3 for Full Stack Python Development
-
Emacs – The Best Python Editor?
I would recommend looking into these if you are interested in delving further into Git.
books:
-
The free, online
Pro Git
is a very handy reference. -
For those of you who like to read on paper, there’s a print version of
Pro Git
and I found O’Reilly’s
Version Control with Git
to be useful.
GitHub Copilot is a tool that can help you make the most of the potential of artificial intelligence as you progress along your coding path. If this is something that interests you, take some time to experiment with it.