00:00:00
This is an advanced git course taught by Tobias
Gunther. He'll teach you some more advanced
00:00:05
concepts and tools to make you more productive
and confident with Git. Hello Free Code Camp
00:00:12
friends. My name is Tobias. And I'm going to make
you a better get user today. If you're using Git
00:00:18
is your version control system, you have a lot of
powerful stuff available. And we're going to talk
00:00:23
about some of the more advanced topics today,
cherry picking, interactive rebase sub modules,
00:00:29
and much more. At the end of the session, you'll
be a lot closer to becoming an advanced git user.
00:00:36
Before we start a huge shout out to the Free
Code Camp team, thank you so much for being on
00:00:41
this mission of teaching people how to code and
thanks for letting me contribute a little bit.
00:00:46
A couple of words about my own background, I
am part of the team behind tower tower is a
00:00:52
get desktop GUI for Mac and Windows, we've been
around for more than 10 years and helped over
00:00:58
100,000 developers and designer, designers work
more easily with get become more productive with
00:01:04
Git and make fewer mistakes. For today's sessions
session, you do not need to have tower installed,
00:01:12
you can follow along on the command line,
don't worry. Okay, let's get started.
00:01:24
Interactive rebase is like the Swiss Army
knife of good commands, lots of use cases
00:01:28
and lots of possibilities. It's really a
great addition to any developers tool chain.
00:01:34
I'll start to explain what you can do with it.
And then we'll look at a couple of practical
00:01:40
examples. So in short, interactive rebase allows
you to manipulate your commit history, you can
00:01:47
make changes to your commits after the fact. So
you could change an old commits message, you can
00:01:54
delete commits, reorder them, combine multiple
commits, and even reopen a commit, edit it and
00:02:01
make new changes from its change that crazy stuff.
Keep in mind, though, that interactive rebase
00:02:08
rewrites your commit history. So the commits you
manipulate will have new hash IDs. Technically,
00:02:15
they are new commits. And there's a simple rule,
you shouldn't use interactive rebase on stuff
00:02:20
that you've already pushed to a shared repository.
Use it for cleaning up your local commit history.
00:02:27
A good example is when you're done
developing on a feature branch,
00:02:31
before you merge it back into a team branch, you
can optimize that clean up the commit structure,
00:02:37
so it's easier to understand. And that's
exactly what interactive rebase is meant to do.
00:02:44
Before we take interactive rebase for a test
drive, let's look at the general workflow.
00:02:50
This is the same no matter what exactly we're
doing deleting a commit changing a commit message
00:02:55
combining commits, the general steps are always
the same. So the first step is to determine which
00:03:01
range of commits you want to manipulate.
How far back in time do you want to go?
00:03:08
Once you have that, you can start the actual
interactive rebase session. And finally, you'll
00:03:13
have the change chance to make your edits. You
can manipulate the selected commits by reordering
00:03:19
deleting, combining and so on. Okay, let's try
this in practice. We'll do two things as examples.
00:03:27
First, we'll change an old commits message. And
secondly, we'll combine two old commits into one.
00:03:34
Alright, let's hop into the command line
and take a look at our repository. git log.
00:03:44
Okay, so let's say we want to change
this commits message here, right.
00:03:51
Just to note, as a reminder, if it were the most
recent commit, we want to change this one here,
00:03:57
we could just use git commit amend to change the
commit message. But for anything older than that,
00:04:03
for this one, this one, this one, we really need
to use interactive rebase. So the first step for
00:04:11
interactive rebase, determine our base commit
termen, how far back in history we need to go.
00:04:17
And this is at least the parent commit of the
one we want to manipulate. So we said we want to
00:04:23
change this here. So at least this one here. I
could simply copy the commit hash of that commit,
00:04:33
or I can do a little bit of counting. So this
is head currently head minus one head minus
00:04:38
two head minus three, I'll use that. So Git
rebase. Stash interactive had till the three.
00:04:50
So there is an editor window popping up
just as I promised. Let's make this bigger
00:05:00
And you can see all of the commits
that we just selected. And by selected,
00:05:05
I mean that we referred to a range of commits,
had to leave three, three behind the head,
00:05:12
from head all the way to the one we mentioned.
So we can now manipulate these commits. But
00:05:19
big but, but we don't change the commit message
right here. In this window, we only tell git,
00:05:25
what kind of manipulation we want to
perform. So we just mark up this line here.
00:05:33
With the reward action keyword, that's the
action keyword that allows us to change the
00:05:38
commit message. And don't worry, all of the action
keywords or protocols are documented here in the
00:05:44
comments. Alright, reword, then we just save and
close the window, we do not make our changes to
00:05:53
the commit message right here, right? We do not
do that we only mark it up. So save and close.
00:06:02
And that's actually it. So now we get
an editor window. Again, why is that
00:06:09
because we can now finally actually change the
commit message. So let's make a change optimize
00:06:15
the markup structure, very important. Save and
Close. And let's check git log again. And we can
00:06:28
see it's now optimized the markup structure. So
we've successfully changed an old commits message.
00:06:35
So let's do one more example of an interactive
rebase. Let's combine. Let's take a closer look.
00:06:43
Let's combine well, let's say these two,
these two commits here into one. Again,
00:06:56
first step of any interactive rebase. Correct,
we have to determine the base commit. And again,
00:07:01
we have to go at least to the parent
commit, so at least this one here,
00:07:07
so it's head on is 1234. Okay, so the git
rebase, interactive head, and tail D for
00:07:20
again, here are the commits, we just requested
for manipulation. And this time we're using the
00:07:27
squash keyword. Squash works by combining the
line we mark up this here with the one above.
00:07:37
So by marking up line number two, with squash
good, we'll combine it with line number one,
00:07:44
which is what we want, right, we want to combine
these two commits, so markup, this one with with
00:07:49
squash and combine it with the one before.
Again, we just save and close the window.
00:07:57
And a new window will pop up, right. Why is
that because by combining two old commits,
00:08:03
we are creating a new one, right. And we
can give this new commit a commit message,
00:08:09
of course, so I'll just write combine
two into one. And again, save and close.
00:08:19
And let's see what happened
here on the command line.
00:08:26
All right, now we have combined
these two old commits, you can see
00:08:31
they were present here, and they are now
combined as one under this commit here.
00:08:38
So these were just two examples of what
interactive rebase can do. If you want to
00:08:42
see the other possibilities, you should check out
our video about undoing mistakes in Git, and I'll
00:08:48
add a link in the description. Normally, when you
integrate commits, you do this on a branch level,
00:08:58
you use a command like Git merge or git
rebase and integrate all the new commits
00:09:03
from one branch into your current head branch.
And normally, that's exactly what you want.
00:09:10
But in some situations, you might only want a
specific commit not all the commits from another
00:09:18
branch. And this is where it gets cherry picking
tool comes in handy. I'll show you a practical
00:09:24
example in a minute. But before that a word of
warning. Don't get too excited about cherry pick.
00:09:30
Your main way of integration should still be on
the branch level, merge and rebase were built
00:09:36
exactly for this job. Cherry Picking should
only be used for very special situations,
00:09:41
you need to have a good reason to use it.
It's not a replacement for merge and rebase
00:09:47
a sure I'll show you a great example for when
cherry pick is actually the right tool to use.
00:09:54
Let's say you made a commit on the wrong
branch. As an example many teams don't want
00:09:58
you to commit directly The domain or master or
other long running branches, and still we forget
00:10:05
and commit there by accident, of course. And
this is a perfect example for cherry pick.
00:10:12
Let's take a look at an example situation in
practice. Okay, so currently we are on master
00:10:23
and have that checked out. And the last commits
is newsletter signup page. Okay, but we already
00:10:29
have a feature newsletter branch. So my guess
is, and I'm correct. This should have been here,
00:10:36
this should have happened on the feature
newsletter branch. So we want this commit here
00:10:42
to be on the feature branch and not on master. So
let's use cherry pick to move it over. Oh, okay.
00:10:50
So first, we switch to the feature branch, feature
newsletter in that case. And then we cherry pick
00:11:01
that commit over and we can just copy the commit
hash. So this is the one we want to the clipboard.
00:11:09
And then git cherry, pick and hash of the commit.
And Wallah, let's take a look at what happened.
00:11:19
And we can see, boom, here it is. Now, it should
have been here in the first place. And finally,
00:11:26
here it is. If you also want to clean
up the master branch, so this doesn't
00:11:31
linger here, it shouldn't be here. Then
you can also do this week and get checkout
00:11:39
back to master and then get reset to Ashutosh
hard head till the one head till the one means
00:11:46
one behind the head. So we're effectively
deleting this from the master branch. And
00:11:53
Wallah. Master is clean and newsletter, feature
newsletter now has that brand that commit sorry,
00:12:04
well as if nothing had happened. Let's
talk a bit about a very special git tool,
00:12:13
the ref lock. You can think of the ref log
as gits diary. It's a journal where git logs
00:12:19
every movement of the head pointer. So what is the
movement of the head pointer Exactly. That's when
00:12:25
you commit checkout, merge rebase, cherry pick
reset, all of the more interesting actions are
00:12:33
documented there. And this makes it perfect for
those situations where things go wrong. Let me
00:12:40
show you a perfect use case for the ref log. Let's
say we don't want these two newest commits here.
00:12:48
At least we think we don't want them anymore.
And to get rid of those, we'll perform a reset,
00:12:53
right? They've disappeared from the
commit history. And a while later,
00:12:57
we noticed that this was a horrible
mistake. We've just lost valuable commits.
00:13:03
So let's have some fun and make
this horrible mistake in practice.
00:13:10
Alright, so here we go. Let's say we want to throw
away these two commits, and get rid of them. So
00:13:18
we are making this here our latest revision on
the branch. I just copied the commit hash here
00:13:24
to the clipboard. And on the command line, I can
get reset dash dash hard and use this commit hash.
00:13:36
Alright, let's have a look what happened. And
we can see those newer commits disappeared.
00:13:44
And we're back at this rubbish in here. I'll
give you a couple of seconds to realize that
00:13:49
this was a horrible mistake, panic emoji con.
And there was invaluable data in those commits,
00:13:56
and we just deleted them by accident. So let's
see if the ref log can help us in this situation.
00:14:03
Opening the ref log is as easy as
typing get ref log on the command line.
00:14:10
And this here is the ref lock that journal where
git logs all of the important actions. And first
00:14:16
of all the entries are ordered chronologically,
which means the most recent, the newest items
00:14:21
are at the top. And if you look closely,
you'll find this catastrophic reset action
00:14:28
right at the top. We just did that 20 seconds
ago. So the journal seems to work. Good news.
00:14:35
Now if we want to undo our last action, we can
simply restore the state before, which is also
00:14:40
documented here. We can just copy the commit hash,
this is the state before. I'll copy that to the
00:14:47
command line to the clipboard. And I could use git
reset once more totally valid, but I'm going to
00:14:55
create a new branch. Should I find a little bit
more Elegant, happy ending. And I will have it
00:15:04
start at that previous revision with the correct
state. So let's use that. Let's see what happened.
00:15:13
Okay, there's a new branch. Wow, that can
that contains the seemingly lost commits.
00:15:23
We just saved our necks. Awesome. So we just
restored some deleted commits with the ref lock.
00:15:28
And here's another great use case
for the ref log, restoring branches.
00:15:34
So again, we think we can clean up
and delete something. This time,
00:15:38
it's a branch not commits. But again, as life
sometimes goes, we realize that it was a bad idea.
00:15:45
And we still need it. So let's take a look
at this scenario in practice. That is,
00:15:56
ref log, re cover, Branch. Okay, so here's a
beautiful, beautiful feature login branch. meds,
00:16:13
let's say our customer or boss or team lead, say
they don't want it anymore, it's not necessary. So
00:16:20
of course, we go ahead and delete it. Just
to make things worth worse, this commit here,
00:16:26
this one is present nowhere else. So we are
definitely going to lose data when we deleted.
00:16:33
All right, but before we can delete it, we
need to step away from that branch. It's
00:16:37
currently the head branch at the moment, and we
can't delete the head branch and get so we're
00:16:42
checking our master. And then we can delete,
we have to force deleted because it contains
00:16:49
a commit that is not merged into
any other branch. And Wallah. Okay.
00:16:56
So now let's say our customer changed their
mind. Again, the feature is necessary after also,
00:17:02
what do we do? What do we do? What do we
do? Let's take another look at the ref log.
00:17:10
Alright, and it turns out, we're lucky
the last entry was when we made that
00:17:15
checkout to master. So let's try returning to
the state before again. And I will copy the
00:17:23
so here is the checkout and I'll copy this here to
the clipboard and get put that on top. git branch
00:17:36
I think it was called featured login if I'm right,
and we want to have it start at that revision here
00:17:42
that we just copied from the ref log. And let's
see what happened. So okay, a branches back
00:17:51
Wallah. And it includes that seemingly last
commit that valuable commit another happy ending.
00:18:01
If you're using Git in a desktop GUI like tower
like you're seeing here, undoing a mistake is
00:18:06
really easy, you can simply press command Z to
undo your last action, just like in a text editor,
00:18:12
when you made a typo, and this works for almost
anything, accidentally deleting branches,
00:18:18
commits files, undoing a commit or a bad merge,
you might have pushed something too early,
00:18:24
doesn't matter what it is. So I can just go
ahead and delete this one more time, delete,
00:18:31
forced delete, I can just press Command Z. And
again, there it is very helpful. Pretty often in
00:18:42
a project, you want to include libraries or other
third party code. And you can go the manual way,
00:18:49
download the code, copy the files into your
project, and commit the new files into your
00:18:54
projects. Git repository. And this is a valid
report approach. But it's not the cleanest one.
00:19:00
If you simply throw those library files into your
project, you're inviting a couple of problems.
00:19:08
First, you're mixing external code
with your own unique project files.
00:19:12
But the library is actually a project of itself.
And it should be kept separate from your own work.
00:19:19
There's no need to keep these files in the
same version control context as your project.
00:19:25
And secondly, should the library change because
bugs were fixed or new features added? You'll have
00:19:31
a hard time updating the library code. Again,
we need to download the raw files and replace
00:19:36
the old items. These are quite common problems
in everyday project. So Git has a solution and
00:19:42
that's sub modules. A sub module is just a
standard git repository. The only specialty
00:19:49
is that it's nested inside another
parent repository. sub module is a
00:19:56
fully functional and Git repository. You can
modify files commit, pull, push from inside,
00:20:02
like with any other repository. Let's see
how sub modules work in practice. Alright, so
00:20:11
we have a little sample project here. And
let's say we want to add some third party code.
00:20:18
Creating a lib or or vendor folder is a good idea
so that it's cleanly separated from our own files.
00:20:24
Let's do that. So let's create lib, and
change into that folder. And I will add
00:20:35
a little JavaScript library from GitHub, I can do
that with a Git submodule, add command, and then
00:20:43
provide the remote URL of that repository. And you
can see that the command starts to clone the Git
00:20:51
repository responsive misspecified. And let's
see what happened in our Working Copy folder.
00:21:00
And here we go. We have a lib
folder, I just created that.
00:21:04
And here it is. Our project now contains that
third party library inside the lib folder,
00:21:11
you can see there's a dot Git sub folder here.
00:21:19
So this is indeed a fully featured git
repository. And let me emphasize this,
00:21:24
again, the actual contents of that sub module,
they're not stored in our parent repository.
00:21:31
Right? This is important to understand apparent
repo only stores the sub modules remote URL,
00:21:37
the local path inside the main
project and the checkout revision.
00:21:41
Of course, the sub modules, working files are here
in our project. Now, after all, we want to use the
00:21:47
libraries code, but the files aren't are not part
of our parent projects. Git repository, they're
00:21:53
not part of that version control context. Okay,
so let's take a closer look at what else happened.
00:22:00
So there's a new git modules file here.
So let's take a look at that. Get modules.
00:22:16
And so you can see here is the path in our
project and the remote URL documented, and as
00:22:24
also a record of that in the Git slash config file
at the bottom sub module lib to progress again.
00:22:35
And finally, the Git also keeps a copy
of each sub sub modules git repository
00:22:41
in its internal git modules folder. So
here is the Git repository. There is
00:22:49
modules, and now we have lip to progress. So
this is the the Git repository of our library,
00:22:57
stored inside of our main git repository. These
are all internal files. So please don't mess with
00:23:03
this configuration. Because as you can see, get
internal gets internal management of sub modules
00:23:10
is quite complex. There's good modules, there
is dot git config, there is dot get modules.
00:23:16
My strong recommendation is don't mess with sub
module configuration values and files manually.
00:23:23
Do yourself a favor and always use proper git
commands or get desktop GUI to manage sub modules.
00:23:31
All right, so let's have a look at our projects
status. And you can see that Git regards adding a
00:23:40
sub module as a modification like any other,
so this means we have to commit it to our
00:23:46
main repository. Let me just do that
quickly. So Git commit dash m, Add to Project
00:24:00
to progress library. Okay, we've successfully
added a sub module to our main project. Congrats.
00:24:07
Now, let's start from the other end. Let's clone
a project that already has sub modules added.
00:24:17
And let's step out of that and get clone I
will clone the Apache Airflow project for that.
00:24:30
And let's see what happens here. So I know
this project has several sub modules added
00:24:38
and we'll take a look at those
in a second. Okay. All right.
00:24:49
Here we go the airflow project. So
00:24:57
let's see. So I know that the product checked
has some sub modules in the dot GitHub
00:25:04
actions folder. So these are our sub modules
for this project. But if we take a closer look
00:25:11
after cloning, visual empty. Okay, so what's
happening here? Why, why are those sub module
00:25:18
folders empty? Well, you already know that
a project report does not contain its sub
00:25:24
modules files, right? The parent repository
only saves the sub module configurations.
00:25:30
So when we clone a project with a default
git clone command, like I just did,
00:25:35
and we only download the project itself with
the configuration, the sub module folders,
00:25:39
however, they stay empty. And we can
repair that by now calling get sub module
00:25:49
update. And we have to initialize these for the
first time. And we want to do this recursively.
00:25:58
Sorry, I have to step into the
airflow project folder. And now
00:26:05
you can see this action happening here. It's
several cloning processes start. And And
00:26:14
now, the sub module folders have been
populated, or in other words, the
00:26:18
sub module Git repositories have been cloned
can take another look at that and see, well,
00:26:24
yeah, these are all populated now.
Right as files in here. Let me see.
00:26:35
So using git clone, like we just did, and
then an extra command is a bit unnecessary,
00:26:40
I would say. So you can achieve the same if
you use a particular option with Git clone,
00:26:46
the recurse submodules option. This tells Git to
initialize all sub modules, when the cloning is
00:26:52
finished automatically. So I would have used the
command like this instead, right, we left that
00:27:00
option here out the recurse submodules. And we
would include that now, and everything works from
00:27:05
the beginning. Alright. I don't want to go into
too much detail about some modules. But one more
00:27:13
thing is important to understand the way revisions
are checked out in a sub module. A git repository
00:27:21
can have countless committed reversion revisions,
but the files from one specific revision
00:27:27
can be in your working directory at one time,
right. So in a normal git repository, you check
00:27:37
out a branch. And automatically the last commit in
that branch is your checkout revision. If you add
00:27:45
new commits to that branch, the checkout revision
is all always automatically the newest commit,
00:27:51
right, we just move the pointer forwards to
the latest commit, like like you can see here.
00:27:57
sub module repositories, on the other hand,
are always checked out on a specific commit,
00:28:03
not a branch. And it makes sense, think about it,
the contents of a branch can change over time when
00:28:10
new commits arrive. In a sub module, however,
you don't necessarily want that sub modules are
00:28:16
mostly library code. And in that situation, I
want to guarantee that I always have a specific
00:28:22
revision of the code. Even if the maintainer
of the library ships, some genius updates.
00:28:28
I don't know if those would break my code. So I
always want a specific chosen version checked out.
00:28:36
Alright, let's quickly hop into the sub modules
in this project. I can show you that. So
00:28:48
this is a target client. And when I select the
sub module here, so here you can see that this
00:28:53
sub module is not checked out on a branch, but
on a specific revision. And this is what I meant.
00:29:00
I've already mentioned it managing sub modules is
a bit complicated, I can really recommend taking
00:29:06
a look at a good desktop, Gui, like tower, for
example. So adding updating moving sub modules,
00:29:12
all of that is really simple here,
take a look and see if it's helpful.
00:29:18
Okay, we could talk for hours
about sub modules, but I think this
00:29:21
should be a good introduction. Just try them
out in your project and take it from there.
00:29:30
And git repository is a perfect log
of all your activities in a project,
00:29:35
every change is documented. And sometimes you want
to browse through this log, because you're looking
00:29:40
for something you're wanting to find a piece
of information. So let's talk a bit about how
00:29:45
searching and finding good works. Or in other
words, how you can filter your commit history.
00:29:53
You can filter by almost anything by date by
message by author by file by branch even. So
00:30:02
let's start with date and search for all commits
that happened after a specific date, for example,
00:30:09
we're doing this in the Ruby on Rails, open source
repository. So there are a couple of commits
00:30:15
present, you can use git log and then the after
flag. So now I get all of the commits that are
00:30:24
after July 1, I specifically specified in that
repository, I can also combine that with before,
00:30:38
and thereby get all of the commits
that are between July 1 And July 5.
00:30:46
Pretty easy. And if you want to search for a
certain commit message, you can also do that,
00:30:55
you can do that with the destination grep. Flag,
and let's search for anything that contains
00:31:02
refactored in its message. And you will see here
is one, here's one, and there's lots of others.
00:31:11
You can get really fancy with GREP, because it
accepts regular expressions. So actually no limits
00:31:18
to your creativity. Looking for a certain author
works exactly the same using the author flag. So
00:31:28
git log, let's search for anything
from a certain Heinemeier colleague.
00:31:39
And of course, before I forget that, you can
combine these criteria, of course, so for example,
00:31:44
you could use dash dash author and dash dash
before in combination to find all commits
00:31:50
from a certain person before a certain date. And
then you can also search for files, this can be
00:31:57
really handy to understand how a certain
file evolved over time, for example, when you
00:32:02
know that a certain file used to work fine in
the past, but it doesn't anymore. And you could
00:32:07
filter for commits with only that file. So let's
do that git log double dash, I'll explain that
00:32:15
readme dot markdown, sorry, markdown. And I get
all of the commits. Were readme dot markdown was
00:32:25
manipulated. The double dashes in that command,
if you've taken a look, there just to make sure
00:32:32
that Git doesn't confuse the file name with a
branch name, right. And finally, pretty helpful
00:32:40
way to see commits that are not in one branch,
but in another one. So very handy. If you
00:32:46
want to want to find out, for example, what
happened in the main branch, let's say,
00:32:50
after you branch off with your private feature
branch. So let's type git log, feature.
00:33:00
Login, dash dash, or sorry, dot double dot
notation, main. And this will show you all commits
00:33:09
that are in Maine, but not in feature login. And
you might then decide to do a merge so that you're
00:33:16
up to date again, or you can at least see what
happened in Maine while you were gone in your
00:33:22
future work. Alright, so much for today.
Be sure to check out my little advanced git
00:33:30
kit. It's completely free of charge. It's a
little collection of short videos about a lot of
00:33:35
advanced get topics from things like interactive
rebase, all the way to branching strategies,
00:33:41
merge conflicts, sub modules, what have
you. It's really helpful if you want to
00:33:45
become more productive with Git and version
control. And again, it's free. Alright,
00:33:51
have fun and see you soon. Here on
the Free Code Camp YouTube channel.