Version Control, Git

What is Version Control?

Tracking revisions over time, also referred to as version control, quickly becomes more complex when you are working on a large project with multiple files and multiple developers.

Version Control Systems (VCS) help a software team manage changes to source code over time.

VCS even have tools to prevent conflicts when one developer’s changes are incompatible with changes made at the same time by another developer.

One of the most popular Version Control Systems (VCS) is Git.


The Git Version Control System

One of the most popular version control systems is Git, a distributed VCS.

Git has another advantage : it is distributed.
Rather than having only one single place for the full version history of a project, every developer’s working copy of the code is also a repository that can contain the full history of all changes.


Initializing a Git Repository

The most effective way to run Git is through a command line prompt.
Once inside the appropriate folder, use the git init command to turn the directory into an empty Git repository

1
$ git init

Tracking Files

In general, files in repository can have the following statuses:

  • Not tracked
  • Staged
  • Committed

To find out the actual status of files in repository, the git status command is used.

Untracked It means that the file is not being tracked by Git for changes. We should explicitly say which files it should follow/track.

The git add ‘filename’ command tells Git to track the file. This step is called staging.

If you don’t want Git to track some specific files, you can ‘ignore’ them. That is done using the .gitignore file. Anything listed in the .gitignore file is ignored by Git and won’t be visible in the repository.


Git Commit

The git commit command saves the state of your project by adding snapshots of staged files to the repository.

The git commit can include the -m flag with a message describing what we’ve changed.

1
$ git commit -m "my commit message"

To commit modifications for every tracked file in the repository, use the git commit -a command.

1
$ git commit -a -m "my commit message"

If you want to see the history of your commits, use the git log command.

1
$ git log

Too many commits can result in a series of snapshots with minor changes that make reverting to a meaningful point difficult.


Cloning and Pushing

After committing the changes, the next step is pushing the local repository to the Git server on a remote location.

The clone command is used to download a remote repository.

1
$ git clone https://git.devx.kr/project_name.git

cloning should be done at the very start of the project

If you have already initialized a local repository, you can connect it to the remote one using the following command

1
$ git remote add origin https://git.devx.kr/project_name.git

Pushing Remotely

After making our local changes and commits, its time to push the changes to the remote repository.
The push command tells Git where to put our commits.

1
$ git push -u origin master

The name of our remote is origin and the default local branch name is master.

The -u tells Git to remember the parameters, so that next time we can simply run git push and Git will know what to do.


Pulling

A local repository may have commits pushed by other users, who work on the repository. Get the latest updates on the project, especially when you are not the only one working on the project.

We can check for changes on our Git repository and pull down any new changes by running

1
$ git pull origin master

We can check what is different from our last commit by using the git diff command.

1
$ git diff HEAD

We want the diff of our most recent commit, which we can refer to using the HEAD pointer.


Reset & Checkout

You can use diff to look at changes within files that have already been staged.
Running git diff –staged will show the changes you just staged.

A stage can also be reset using the reset command,

1
$ git reset 'filename'

This removes the file from the staged status, meaning that all the changes will still remain in the file.

To reset the file to the latest committed version, the checkout command can be used,

1
$ git checkout -- 'filename'

Branches

Branches are a very important part of git.
Branching allows to make a “copy” of your working project and change it without affecting the main branch (master branch), giving an opportunity to work on the same project with different commits.

When you want to add a new feature or fix a bug - no matter how big or how small - you create a new branch to encapsulate your changes.

After the feature is done, you can merge it with the master branch.

Creating a new branch is done using the branch command.

1
$ git branch my_new_branch

Then we need to switch to the branch using the checkout command.

1
$ git checkout my_new_branch

There is a shortcut to create and switch to a new branch

1
$ git checkout -b my_new_branch

In order to see the list of your branches, run the git branch command in the project directory.


Amend

The git commit –amend command is a convenient way to modify the most recent commit. It lets you combine staged changes with the previous commit instead of creating an entirely new commit.

It can also be used to simply edit the previous commit message,

1
$ git commit --amend -m "change last commit message"

Amending does not just alter the most recent commit, it replaces it entirely, meaning the amended commit will be a new entity.

But, never amend a commit which was already pushed to a remote repository someone else might have pulled / cloned from. This will severily mess with the remote repository or, if you fix it, this someone’s history. Do a new commit instead.


Stash

The git stash command temporarily caches any changes you’ve made to your working copy so you can switch to something else, and then come back and recover them later.

The git stash command takes your uncommitted changes, both staged and unstaged, and saves them for later use.

1
$ git stash

After stashing, you’re free to make any changes, create new commits, switch brances, and perform any other Git operations. Then you can come back and re-apply your satsh.

The stash stays locally on your Git repository. They will not be transferred to the server when you push.

There are two ways to re-load the stashed changes:

1
$ git stash pop

pop will remove stashed changes from the stashed state

1
$ git stash apply

apply applies the same stashed changes to multiple branches.

Git stash will not include untracked files by default. To do so, we should add the -u option (or –include-untracked).


Merging Branches

Let’s create and commit a few files on the master branch (main branch). The touch command below will create 5 text files, with names file1.txt, file2.txt, etc.

1
2
3
$ touch file{1..5}.txt
$ git add .
$ git commit -m "add files 1 to 5"

Now let’s create and switch to our new branch,

1
$ git checkout -b test

At this point, this branch is exact copy of the master branch, but every modification, addition, or removal is done in the context of this new “test” branch. Let’s add new files and remove a couple of already existing files and then commit the changes.

1
2
3
4
5
$ touch file{6..10}.txt
$ git rm file3.txt
$ git rm file4.txt
$ git add .
$ git commit -m "add new files, remove 3&4"

We could just use the rm command, but it is better to use the git rm, which not only remove the actual files from disk but will also stage the removal of the files for us.

Now, we have 2 branches, master and test. We are on the test branch which contains some new files and lacks some of the files from the master branch. Let’s switch to the master branch and finally merge them together.

1
2
$ git checkout master
$ git merge test

git merge test tells the Git to merge the test branch into the current branch (the master branch in our example).

In order to remove a branch that you don’t need anymore, use git branch -d test command.


Merge Conflicts

Let’s see what happens if we change the same file in two different branches and try to merge the branches together.

Create file1 with the following text and commit the change: “this is the original text.”

Now let’s create a new branch (“test”) and edit file1 by adding the following text after the initial one: “and it is additional text from test branch.”

Again commit your changes and switch back to the master branch.

This time add the text below in file1 and commit your changes: “also, it is additional text from master.”

Now we have file1 with two different variants in two different branches. Let’s merge them and see what will happen

1
$ git merge test

Git failed to merge them together automatically, because there were two different versions of the file. We should fix this conflict manually and commit the change.

First of all, lets do a git status. It shows which file resulted to merge conflict (it is useful when you have multiple files). It also suggests how to fix the conflict and commit or abort the merge.

When a merge conflict occurs, Git automatically adds some indicators to make it easy for us to find where the conflict occurs.
These indicators are: «««<, =======, »»»>.
Usually, the content before the ======= marker is the receiving branch and the part after the marker is the merging branch.

Besides the general rule about which order the branches will appear, after the «««< mark you can see the HEAD and test (your branch name) after the »»»>, which tells you which version is from which branch.

Now, it is your choice to keep only your master branch changes, keep only the test branch changes, or make a brand new change, which may incorporate changes from both branches.

After that, delete the conflict markers «««<, =======, »»»> and make the changes you want in the final merge. Save and exit. Finally, commit your changes.

1
2
$ git add .
$ git commit -m "solve the merge conflict"

If you check your commit history (git log), you will see that the commit from your test branch is now in your main branch.