- Learn Git concepts, not commands
- Nico Riedmann
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: Mirosalva
- Proofread by: Shixi-Li, Moonliujk
Git: Learning Concepts through Commands — Part 2
Instead of listing common commands, use an interactive tutorial to teach you how Git works.
So, do you want to use Git properly?
But you don’t want to just learn commands, you want to understand the principles behind them, right?
Then this article is tailor-made for you!
Let’s get started!
The starting point for this article is based on the general concepts mentioned in Rachel M. Carmena’s how to Teach Git.
There are plenty of Git tutorials on the web that focus on methods over principles, but I found a great resource for getting both (and the inspiration for this tutorial) : the Git Book and Reference Page.
So if you’re still not satisfied with this article, just click on the two links above to find out. I really hope that the concepts introduced in this tutorial will help you understand the other Git features detailed in the other two articles
It is recommended to read this series in order:
- Git: Learning concepts through Commands — Part 1
- Git: Learning Concepts through Commands — Part 2
- Git: Learning Concepts through Commands — Part 3
- Git: Learning Concepts through Commands — Part 2
- merge
- Fast forward to merge
- Merge dissimilar branches
- Resolve the conflict
- rebase
- Resolve the conflict
- Update remote changes toLocal work environment
- Get updates
- Pull the update
- Storage changes
- Contains conflicting pulls
- merge
merge
We all work on branches in general, and we need to talk about merging to get changes from one branch to another.
We’ve just modified alice.txt on the Change_Alice branch, and I’d like to say that we’re happy with the changes we’ve made.
If you then execute git checkout master, the commit we created on the other branch will not be visible here. In order to make changes to the master branch, we need to merge the Change_Alice branch onto the master branch.
Note: you always merge a branch into the current branch.
Fast forward to merge
Now that we have checked out to switch to the master branch, we can execute git merge Change_alice merge command.
Since there are no other conflicting changes in alice.txt, we have not made any changes in the Master branch, so the merge will take place in what is called a fast-forward merge.
As we can see in the diagram below, this simply means that the master pointer is simply advanced to where the Change_Alice branch exists.
The first figure shows the state before we perform the merge, with the master pointer still in its previous commit position, while we make another commit on another branch.
The second chart shows what has changed since our merger.
Merge dissimilar branches
Let’s try something more complicated.
Add some text to the new line of the bob.txt file in the master branch and commit it.
Next, execute git checkout change_alice to change the alice.txt file and commit.
In the image below, you can see what our commit history looks like now. Both the Master and Change_Alice branches stem from the same commit, but since then they diverge, with each branch having its own additional commit.
It is not possible to perform a fast forward merge using git merge change_alice. Instead, your favorite text editor will open and allow you to change the commit information for the merge commit operation that Git is about to perform to bring the two branches back into line. You can just use the default submission information for now. The following figure shows the git history after we performed the merge.
The new commit introduces our changes on the Change_Alice branch to the Master branch.
As you recall earlier, revisions in Git aren’t just snapshots of files, they also contain some information about where they came from. Each commit contains one or more parent commit information. Our new merge commit contains the final commit of the master branch and our commit on another branch as the parent commit of the merge.
Resolve the conflict
So far, none of our changes interfere with each other.
Let’s introduce a conflict and then resolve it.
Create a new branch and check it out. You know how to do it, but you can probably save yourself some trouble by using git checkout -b.
I’ll call it Bobby_Branch.
On this branch, we will modify the bob.txt file.
The first line should still be Hi!! I’m Bob. I’m new here. Change it to Hi!! I’m Bobby. I’m new here.
After tabbing the file, commit your changes before you check out the Master branch again. On the master branch we change the same line to Hi!! I’m Bob. I’ve been here for a while now.
Now it’s time to merge the new branches into master.
If you try this, you will see the following results:
Auto-merging Bob.txt
CONFLICT (content): Merge conflict in Bob.txt
Automatic merge failed; fix conflicts and then commit the result.
Copy the code
Both branches have changed the same line, and the Git tool cannot handle this situation completely on its own.
If you run git status, you’ll get all the usual help commands for how to proceed.
First we must resolve conflicts manually.
For simple conflicts like this, your favorite text editor will suffice. For merging multiple files with lots of changes, it’s easier to use a more powerful tool, and I recommend using your favorite IDE with a version control tool and a merger-friendly interface.
If you open the bob.txt file, you’ll see something like the following (I’ve truncated everything else that might have been on the second line) :
<<<<<<< HEAD
Hi! I'm Bob. I've been here for a while now.
=======
Hi! I'm Bobby. I'M new here. >>>>>>> bobby_branch [... whatever you pass in at line 2]Copy the code
Above you can see the changes made to bob.txt on the current HEAD and below you can see the changes made to the branch we are trying to merge in.
To resolve conflicts manually, you just need to make sure that the file ends up with some reasonable content and doesn’t contain the special lines git introduced into the file.
So go ahead and change the file to something like this:
Hi! I'm Bobby. I've been here for a while now.
[...]
Copy the code
From here, what we’re going to do is apply to any changes.
After we add the file with add Bob.txt, we temporarily store the changes and then commit.
We’ve looked at change commits to resolve conflicts, which are merge commits that occur all the time during the merge process.
In fact, you should be aware that if you do not want to continue the merge process during conflict resolution, you can abort it directly by running git merge –abort.
rebase
Git has another clean way of integrating changes from two branches, called base change.
We always remember that one branch is always based on another. When you create it, fork from somewhere.
In our simple merge sample, we create a branch from a commit in the Master branch, and commit changes in master and Change_Alice.
When a branch changes relative to the branch on which it is based, if you want to integrate the latest changes into your current branch, rebasing provides a purer way to handle it than merging.
As you can see, a merge introduces a merge commit, in which the histories of both sides are consolidated.
As you can easily see, rebasing only changes the history point on which your branch depends (the commit on which the branch was created).
To try this out, we first check out the Master branch again and then create/check out a new branch based on it.
I called my new branch add_patrick, then I added a new file, patrick.txt, and submitted it with the message “Add Patrick”.
After you add a commit to that branch, go back to the master branch, make some changes and commit it. The change I made was to add more text to the alice.txt file.
As in our merge example, the two branches have a common ancestor, but the history is different, as you can see from the following figure:
Now let’s execute the checkout add_patrick command again and fetch the changes we made on master to the branch we’re working on!
When we run git rebase master, we baseline the add_patrick branch against the master branch in its current state.
The command above gives us a friendly reminder of what to do so far:
First, rewinding head to replay your work on top of it...
Applying: Add Patrick
Copy the code
We know that HEAD is the pointer to the current commit in our work environment.
Until the base operation is executed, it points to the add_patrick branch. When a base change occurs, it will first move back to the common ancestor of both branches, and then move to the current vertex of the branch we want to base.
So HEAD moves to 0CFc1d2 for this commit, and then 7639f4b for this commit, which is at the vertex of the master branch.
Then the base operation will apply every commit we make on the add_patrick branch to that vertex.
To get a better idea of what Git is doing by moving the HEAD pointer back to the common ancestor of the branch, you can store a portion of each commit you make on the branch being operated on (change differences, commit information, author, etc.). .
After doing this, your rebasing branch needs to check out the latest commit and then apply any changes saved to the previous commit as a new commit.
So in our original simple view, we thought that after rebasing, the 0cfc1d2 commit would no longer point to its historical common ancestor, but to the top of the Master branch.
In fact, the 0CFc1D2 commit disappears this time, and the add_patrick branch starts with a new commit, 0CCABa8, which takes the most recent commit from the Master branch as its common ancestor.
We make it look like the add_patrick branch is based on the current master branch, not an older version of the branch, but we’re rewriting the history of the branch.
At the end of this tutorial, we’ll learn a little more about rewriting history and when it’s appropriate and inappropriate to do so.
Rebasing is a pretty powerful tool when your own working branch is based on a shared branch, such as the Master branch.
Using the rebase operation ensures that you are constantly integrating changes and pushes that others have submitted to the Master branch, and keeps a clean linear history that can be fast-forwarded when your working text needs to be imported into the shared branch.
Keeping the history linear can also make the commit log more useful than having a messy history of merge commits (try Git Log –graph, or take a look at the branch view on GitHub or GitLab).
Resolve the conflict
Just like during merge, if you encounter two commits that change a block of content in the same place in a file, you may encounter conflicts.
However, when you encounter a conflict during the rebasing process, you don’t have to resolve it in an additional merge commit, but you can resolve it in the commit currently being executed.
Likewise, base your changes directly on the current state of the original branch.
In fact, the conflict resolution you do in the rebase process is very similar to what you do in the merge process, so go back to that section if you’re not sure how to do it.
The only difference is that since you did not introduce merge commit, you are not required to submit the conflict resolution result. Simply add the changes to the staging environment, and then execute git rebase –continue. The conflict will be resolved in the commit just performed.
When merging, you can always stop and discard everything you’ve done so far by executing git rebase –abort.
Update remote changes toLocal work environment
So far, we’ve learned how to generate and share content changes.
If you’re working alone, that’s enough. But usually we have multiple people working on a task, and we want to somehow capture their changes from the remote repository into our own working environment.
Since a while has passed, let’s take a look at git’s components:
Just like your work environment, everyone working on the same source code has their own work environment.
All of these working environments have their own ongoing and staging changes, which are committed to the local repository at some point, and eventually pushed to the remote repository.
In our example, we will use the online tools provided by GitHub to simulate changes made by others to the remote repository while we are working.
Check out your fork for this repository on github.com and open the alice.txt file.
Find the Edit button to generate and submit a change through the site.
In this repository, I have added a remote repository change to the alice.txt file on a branch called fetching_changes_SAMPLE, but in your repository version, you can of course change the file directly on the master branch.
Get updates
Remember that when you execute git push, you synchronize changes from your local repository to your remote repository.
To fetch changes from a remote repository to a local repository, you can use the git fetch command.
This operation gets any changes remotely to your local repository, including commits and branches.
Note that the changes have not yet been integrated into the local branch, let alone the workspace and staging area.
If you execute git status now, you’ll see another great example of the git command that tells you what’s going on:
git status
On branch fetching_changes_sample
Your branch is behind 'origin/fetching_changes_sample' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
Copy the code
Pull the update
Since we have no working and pending changes, we can now execute the Git pull command to pull all changes from the repository into our workspace.
A pull implicitly fetches a remote repository, but sometimes it is a good choice to perform a fetch alone. For example, if you want to synchronize any new remote branches, or if you want to perform Git rebase on branches like Origin/Master, you need to make sure your local repository is up to date.
Before we pull, let’s modify a file locally to see what happens.
Let’s modify the alice.txt file again in the workspace!
If you try git pull now, you’ll get the following error:
git pull Updating df3ad1d.. 418e6f0 error: Yourlocal changes to the following files would be overwritten by merge:
Alice.txt
Please commit your changes or stash them before you merge.
Aborting
Copy the code
You cannot pull any changes because some files in the workspace have been modified and the commit you are pulling in also has changes to those files.
One solution to this situation is to add local changes to the staging space before you finally commit them, in order to capture changes at a point where you trust them. But before you finally submit them, this is a good time to learn another great tool.
Storage changes
At any point in time if you have some local changes that you don’t want to put into a commit, or want to have somewhere to solve a problem in some other way, you can store those changes.
Git stash is basically a stack of changes where you can store any changes to your workspace.
The most common command is git Stash, which stores any changes to the workspace. There is also the git stash pop command, which takes the most recent changes stored and reapplies them to the workspace.
Just as the stack command is named, the git stash pop command removes the most recently stored changes before applying them.
If you want to keep stored changes, you can use the git Stash apply command, which does not remove the changes from the store before they are applied.
To check your current stash, you can use the git Stash list command to list individual entries. You can also use the git Stash show command to show recent changes to entries in your stash.
Another useful command is git Stash branch {branch NAME}. It creates a branch from the current HEAD, at which point you store the changes and apply them to the new branch.
Now that we know about the git stash command, let’s execute it to remove our local changes to the Alice.txt file from the workspace so we can proceed with the git pull command to pull the remote changes we made on the site.
After that, let’s execute the git stash pop command to retrieve our local changes.
Because the commit and store changes we pulled both modified the alice.txt file, you need to resolve conflicts, just as you would in a merge or rebase. Commit the change after the addition is complete.
Contains conflicting pulls
Now that we understand how to get and pull remote changes into our work environment, it’s time to create some conflicts!
Don’t push the submission to modify the alice.txt file, go back to your remote repository at github.com
Here we’ll modify the alice.txt file again and commit it.
There are actually two conflicts between our local and remote warehouses right now.
Don’t forget to run git fetch to see the change remotely, rather than pull it immediately.
If you run git status now, you’ll see that each branch has a different commit from the other.
git status
On branch fetching_changes_sample
Your branch and 'origin/fetching_changes_sample' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Copy the code
In addition, we have modified the same file in different commits above, so we need to resolve it in order to introduce the concept of conflict in merges.
When you execute git pull and there is a difference between the local and remote repositories, the same thing happens as when you merge the two branches.
In addition, you can think of the relationship between remote and local repository branches as a special case of creating one branch from another.
The local branch is based on the last state of the branch you retrieved from the remote repository.
If you think of it this way, these two options make a lot of sense to capture remote warehouse changes:
When you run git pull, the local and remote repository versions are merged. Just like merging different branches, this introduces a merge commit.
Since any local branch is based on their respective remote versions, we can also base it so that any changes we make locally appear to be based on the latest available version in the remote repository.
To do this, use the git pull –rebase command (or git pull -r for short).
As discussed in the rebase section, it is good to keep a clean, linear history of commits, which is why I strongly recommend using Git pull -r instead when you need to execute git pull.
You can also tell Git to use rebasing instead of merging as your default policy when you run git pull by setting the pull. Rebase flag with a command like git config –global pull. Rebase true.
After my introduction to the previous paragraphs, if you haven’t executed git pull yet, let me now execute Git pull -r to get remote changes, making it look like our new commit is after those remote changes.
Of course, just like a normal rebase (or merge) operation, you need to resolve the conflicts we introduced so that the Git pull command can do it.
Read on for the rest of the series:
- Git: Learning concepts through Commands — Part 1
- Git: Learning Concepts through Commands — Part 2
- Git: Learning Concepts through Commands — Part 3
If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.