How to Create and Apply Git Patch Files

2. November 2019 09:20 by Parakh in Git  //  Tags: , , ,   //   Comments
This post explains how to create patch files using the git format-patch command and then apply them using the apply and am commands.

Introduction

In the years during which Git evolved, there was no provision of using a central remote repository which different contributors could use to merge their changes to a feature branch or request pull requests. Sending patch file(s) via email was the name of the game. A patch file is a handy way of encapsulating the changes introduced into the repository as part of a commit, into a single file. Patches can be created on a per commit basis or a bunch of commits can be squashed into a single patch file.

Git Format-Patch

The command that is used to create a patch file is format-patch. Various options are available that can be used in conjunction with the command to modify the output as desired. Let’s take a look at the most common operations that use format-patch command.

To learn about the command let’s create a repository and track a text file with some text entries.

$ mkdir PatchFiles
$ cd PatchFiles
$ git init
$ touch file1.txt
$ printf "first line" > file1.txt
$ git add .
$ git commit -m "First commit"
$ printf "\nsecond line" >> file1.txt
$ git commit -am "Second commit"
$ git log --online master

Creation of master branch

Next let’s create a branch named develop off of the master branch and track a text file with some text entries.

$ git branch develop
$ git checkout develop
$ touch file2.txt
$ printf "first line" > file2.txt
$ git add .
$ git commit -m "First commit in develop branch"
$ printf "\nsecond line" >> file2.txt
$ git commit -am "Second commit in develop branch"
$ git log --oneline

Creation of develop branch

Now that we have laid the groundwork, let’s understand the format-patch command. The format-patch command takes in the name of the branch against which you want to compare the commits of the current branch pointed to by the INDEX pointer. By default, the format-patch creates a patch file for every commit not available in the target branch.

$ git format-patch master
$ dir

Format Patch Multiple files

In earlier times, when there was no remote repository, such patch files used to be mailed by project contributors to the project maintainer.
Next, we will discuss the two commands that can be used to apply the patch files, i.e., apply and am.

Git Apply

There could be situations that demand the application of changes in a patch to the target branch, but, not include the corresponding commit message into the commit history of the target branch. Git’s apply command is used in such situations.

Apply command helps you to test out the changes introduced by the patch locally before you formally commit them into the repository. Since the changes are applied to the working directory, you can use git diff to view the changes applied. Also, the commit message can be a bit more descriptive signifying the nature of the changes applied.

Let’s see the command in action in code.

$ git checkout master
$ git apply 0001-First-commit-in-develop-branch.patch
$ git status
$ git log --oneline
$ dir

 05 Apply command - cropped

Git AM

Git’s am (Apply Mailbox) command is used in a situation when you are assured of the commits in a patch and want them to be applied verbatim, along with their corresponding metadata consisting of author’s information and commit message.

$ git checkout master
$ git am 0001-First-commit-in-develop-branch.patch
$ git status
$ git log --oneline
$ dir

06 AM command - cropped

Word of Caution

If you are selectively applying patches, and if a patch contains an artifact that comes to life in a prior patch, then the application of such a patch will fail. This is because patch files are commit-specific and carry changes specific to the commit.
In such cases, you will be required to sequentially apply patches in chronological order, first bringing the artifact into existence, followed by any changes in the artifact.

$ git checkout master
$ git am 0002-Second-commit-in-develop-branch.patch

07 Problem with patch application - cropped

 

We were not able to successfully apply the second patch as the file2.txt came into existence in the first commit, hence first patch. It would be imperative here, to first apply the first patch and then the second patch.

Conclusion

Git provides the ability to create patches as discreet units signifying the evolution of a project and enables the sharing of this evolution in the absence of a central remote repository.

Git offers two ways to apply patches as per requirements.

Git’s apply command applies the changes locally to the working directory and gives you the freedom to further introduce changes in the artifacts before formally committing into the repository.

Git’s am command applies the patches in the repository and includes the commit messages contained in the patches. It retains vital metadata like author information, email, etc..

A short note on Git Cherry-Pick

16. October 2019 13:27 by Parakh in Git, GitHub  //  Tags: ,   //   Comments
A short note demonstrating git's cherry-pick command.

Key Takeaway

In the real world, situations arise where some urgent work is done on a codebase that is required to be released to mainstream users. This work then needs to be bought down into the feature branches to maintain parity with the master branch.

In Git, we have the command of Rebase to handle such a situation, but in some cases, a more surgical approach is required, whereby, only the commit having the changes is required to be applied to the feature branch and exclude anything else which might otherwise pollute the feature branch. This is what Git’s cherry-pick command achieves.

Read On

One of the founding premises of Git is to commit often and commit early. This aspect of the foundation enables you, the user, to see the evolution of your work over time and enables Git to take the responsibility to manage the evolution in a discreet order via commits. By having small commits, you enable Git to recover your work in a granular manner, should the situation come to be, as desired. And dovetailing this practice of small and frequent discreet commits, revolves the power of the command of cherry-pick.

Git’s cherry-pick command helps you apply a commit in a surgical manner such that it only brings in the changes corresponding to a commit to the branch under concern. This command can be thought to be useful in scenarios whereby you have to bring in a limited number of changes that are pertinent to your work. Examples:
1. There’s a hotfix branch created from the master branch and you need to include the hotfix in your feature branch.
2. There are a couple of commits that you need to take from some other feature branch and incorporate in your branch

Let’s take a look at an example to understand the cherry-pick command.

$ mkdir cherrypick
$ cd cherrypick
$ git init
$ touch file1.txt
$ printf "first line" > file1.txt
$ git add .
$ git commit -m "First commit in master branch"
$ printf "\nsecond line" >> file1.txt
$ git commit -am "Second commit in master branch"
$ printf "\nthird line" >> file1.txt
$ git commit -am "Third commit in master branch"
$ git branch develop
$ git checkout develop
$ touch file2.txt
$ printf “first line” >> file2.txt
$ git add .
$ git commit -m “First commit in develop branch”
$ printf “\nsecond line” >> file2.txt
$ git commit -am “Second commit in develop branch”
$ printf “\nthird line” >> file2.txt
$ git commit -am “Third commit in develop branch”
$ git checkout master
$ git log --oneline develop
$ git cherry-pick SHA1 hash of the second commit from the develop branch

 

Resolve any conflicts that arise from the cherry pick here and then run a git log

$ git log --oneline master

 

In this example, we created a directory aptly named cherrypick and then inserted some text entries into a text file named file1.txt. We then created a branch called develop from the master branch. In the develop branch we created some entries in a text file named file2.txt and then moved back to the master branch. Now listed the log entries from the develop feature branch and noted down the SHA1 hash of the commit corresponding to which we want to pick the changes from the develop branch. In this case, we picked the second commit from the develop branch.

01 Creation of repoFigure 1 Creating the repository and some initial commits

02 In Develop BranchFigure 2 Creation of a branch and some initial commits

03 In Develop BranchFigure 3 Going back to the branch where a cherry-picked commit needs to be merged

Finally, we used Git’s cherry-pick command in the master branch in conjunction with the SHA1 hash noted before. This will bring in the changes from the develop branch into the master branch that were made corresponding to the commit hash. In doing so, you may be required to resolve any conflict that may arise.

04 In Master BranchFigure 4 Process of cherry picking may throw up some conflicts which would require intervention

05 In Master BranchFigure 5 Once a commit is cherry picked, its content will then become available in the target branch

This way we can literally cherry-pick the changes corresponding to specific commit(s) in a branch and bring them to the branch of our choice.

Hope this was helpful.

Git Revert vs. Reset

5. October 2019 13:20 by Parakh in Git  //  Tags: , ,   //   Comments
This post explains the differences between the git commands of revert and reset and where to use them. The post also shows the three different types of reset types.

Introduction

We all make mistakes, and, it’s my belief that we all make an honest attempt to correct that mistake. If only, life was running on Git. Git offers two ways to rollback a change made to a codebase – revert and rese. In this article we will discuss the two commands and the scenarios in which they are apt to be used.

Basic Terminology

Before we embark on discussing the differences between the commands, it is prudent to discuss some basic terminology that will be instrumental in understanding the nuances of operations offered by these commands.
HEAD: Represents the last commit and is a pointer to the current branch selected in git.
INDEX: Represents the staging area aka the contents of the proposed next commit.
Working Directory: Contains all the files available in HEAD and INDEX and the artifacts that may never go in any commit. E.g. Assembly files (.dll, .exe) which do not get committed due to .gitignore settings.

Revert command

Sometimes it happens that wrong work gets committed and is pushed into the remote repository, making it available to the rest of the team, or project contributors, as the case may be. In such circumstances, it becomes imperative, that we introduce a context under which a rollback of the unintentional changes take place.

Revert offers just such functionality. It creates an opposite commit of the commit that you want to roll back. Such a rollback commit retains a detailed history of all the commits that came before it, thus avoiding all the confusion.

Run the following commands in bash shell to simulate a revert:

$ mkdir Revert
$ cd Revert
$ git init
$ touch file.txt
$ printf “First commit”>file.txt
$ git add file.txt
$ git commit -m “First commit”
$ touch file2.txt
$ printf “Second commit”>file2.txt
$ git add file2.txt
$ git commit -m “Second commit”
$ printf “\n\nThird commit”>>file.txt
$ git add file.txt
$ git commit -m “Third commit”
$ dir

Note that there are two files file.txt and file2.txt in the INDEX and working directory.

$ git status
$ git log –oneline

Note there are three commits. Now let’s revert the second commit. Since the file2.txt was created as part of the second commit, it should get deleted from the working directory and INDEX

$ git revert SHA1 hash corresponding to the second commit.
$ dir
$ git log –oneline
$ git status

The result of following the aforementioned commands should look something like:

01 Revert Commit Log Cropped

As you see, now we have a total of four commits in the history, with the fourth one clearly jotted down as a revert commit, erasing the work done as part of the second commit.

Reset command

Reset is used when you have the fault available locally on your system and has not been pushed out to the remote repository. Now this command offers a further three flavors and it will be here that we will be chiefly leveraging the terminology covered earlier.
Soft Reset: This option moves the branch (that HEAD points to) to the commit mentioned, reinstating the conditions as they were when the commit was made. No changes are introduced in the INDEX (staging area) and working directory. This essentially does the same this as “git commit –amend” and offers us a chance to change what we need to.
This means that you will get a chance to amend the commit message. Note that no changes are introduced into the file(s) made prior to the rolled back commit in soft mode.
Mixed Reset: This option moves the branch (that HEAD points to) and INDEX (staging area) to the commit mentioned, reinstating the conditions as they were when the commit was made. No changes are introduced in the working directory.
Hard Reset: This option moves the branch (that HEAD points to), INDEX (staging area) and the working area to the commit mentioned, reinstating the conditions as they were when the commit was made. This is destructive in nature and should be used with caution as work is not recoverable after the issuance of this command.

NOTE: In none of the cases HEAD is moved to the mentioned commit. HEAD always point to the branch. It is the branch that is made to point to the pointer corresponding to the commit being reset.

Run the following commands in bash shell to simulate all three flavors of reset:

$ mkdir Reset
$ cd Reset
$ git init
$ touch file.txt
$ printf “First commit” > file.txt
$ git add file.txt
$ git commit -m “First commit”
$ printf “\n\nSecond commit” >> file.txt
$ git add file.txt
$ git commit -m “Second commit”
$ printf “\n\nThird commit” >> file.txt
$ git add file.txt
$ git commit -m “Third commit”
$ git status
$ git log –oneline
$ git reset --soft head~1

Now observe the changes made by the soft reset command

$ status
$ git log --oneline

02 Soft Reset

The following are the results of the soft reset:
1. The third commit has been rolled back as seen in the commit log,
2. No changes have been introduced in the INDEX i.e. the staging area. The git status helpfully suggests that file.txt is ready to be committed into the repository. The contents of the file.txt are the same as they existed prior to the rolled back commit,
3. No changes have been introduced in the working directory. All the contents are intact.

Let’s continue our example to mixed reset:

$ git add file.txt
$ git commit “Third commit”
$ git reset --mixed head~1

Now observe the changes made by the mixed reset command

03 Mixed Reset Cropped

The following are the results of the mixed reset:
1.    The third commit has been rolled back as seen in the commit log,
2.    Changes have been introduced in the staging area, as suggested by the status. It helpfully suggests that file.txt needs to be added in order for it to be committed.
3.    No changes have been introduced in the working directory.

Let’s continue our example to hard reset

$ git add file.txt
$ git commit “Third commit”
$ git reset --hard head~1

Now observe the changes made by the hard reset command

03 Hard Reset Cropped

The following are the results of the hard reset:
1. The third commit has been rolled back as seen in the commit log,
2. INDEX i.e. staging area has been put into the state as it existed after the second commit and prior to the third commit. The data corresponding to the third commit has been lost from the file.txt.
3. Working directory has been put into the state as it existed prior to the third commit. Any contents introduced between the second and the third commit would have been lost as part of the hard reset of the third commit.

Conclusion

Now that we have seen the difference between revert and reset and when the two should be used. We also saw the three modes made available by revert - soft, mixed and hard reset and how they affect the state of the working directory, INDEX and the content. Hard reset should be used with caution since the content is not recoverable after the issuance of the command.

References

1. https://git-scm.com/book/en/v2
2. https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting

Git Rebase vs. Merge

15. September 2019 06:30 by Parakh in Git, GitHub  //  Tags: ,   //   Comments
This post explains the difference between the rebase and merge operations in git, and more importantly when to use which operation.

Introduction

Every version control system offers a core set of functionalities, of which, the ability to create branches and then merge changes into branches are offered by both central and distributed version control systems. The way different systems behave is different and hence, often, while the result will be the merging of changes, the way it accomplished and the resulting history created are different.

Consider the following scenario:
You have created a feature branch from a long-running branch. Someone in the team commits in changes into the parent long-running branch and you have to bring in the changes into the feature child branch that you are using for active development. There are two ways to do that in Git:
1.    Merge command
2.    Rebase command

Both the commands achieve the same outcome of integrating the changes from parent long-running branch into the feature child branch. Where things differ, is the resulting commit history that gets created due to the usage of the commands.

Merge command

In the aforementioned scenario, if you use the merge command, the resulting commit history will bear a merge commit. Note that the merge command does not alter the history of your feature branch in any way. On the contrary, the merge commit provides a context for the changes bought from the parent branch.
Run the following commands in bash shell to simulate a merge in git to bring in changes from a parent branch to a child branch:

$ mkdir Merge
$ cd Merge
$ git init
$ touch file.txt
$ printf “First instalment of work done in master branch” > file.txt
$ git add .
$ git commit “First commit in master branch” 
$ git branch dev
$ git checkout dev
$ printf “\n\nWork done in dev branch” >> file.txt
$ git commit -am “First commit in dev branch”
$ git status
$ git checkout master
$ printf “\n\n\n\nSecond instalment of work done in master branch” >> file.txt
$ git commit -am “Second commit in master branch”
$ git checkout dev
$ git merge master
Resolve conflicts, if any. Perform a git commit which is going to be a merge commit.
$ git log –oneline –graph

The result will be something like shown in the image below:

 

Log history after a merge. Notice the merge commit made in the end

Figure 1 Log history after a merge. Notice the merge commit made in the end

Rebase command

Rebase command, as the name suggests re-creates the base for the child branch while bringing in the changes from the parent branch. This results in a cleaner, unidirectional history, but the context under which the changes were bought in, gets lost. It appears that the child feature branch always worked with the changes bought from the parent long-running since the beginning of its creation.

Run the following commands in bash shell to simulate a rebase in git to bring in changes from a parent branch to a child branch:

$ mkdir Rebase
$ cd Rebase
$ git init
$ touch file.txt
$ printf “Work done in master branch” > file.txt
$ git add .
$ git commit -m “First commit in master branch”
$ git branch dev
$ git checkout dev
$ printf “\n\nWork done in dev branch” >> file.txt
$ git commit -am “First commit in dev branch”
$ git checkout master
$ printf “\n\n\n\nSecond instalment of work done in master branch” >> file.txt
$ git commit -am “Second commit in master branch”
$ git checkout dev
$ git rebase master
Resolve conflicts, if any. Perform a git add . followed by git rebase –continue
$ git log –oneline –graph

 

The result will be something like shown in the image below:

Log history after a rebase. Notice the second commit from master inserted as a base commit for dev branch

Figure 2 Log history after a rebase. Notice the second commit from master inserted as a base commit for dev branch

Conclusion

The net outcome of both the rebase and merge command as seen above is the same i.e. integration of changes from one to another branch, here parent to child branch.
 
Now, naturally, the question arises as to when to use what.
 
Merge is a non-destructive operation that preserves the chronological order of commits verbatim. The merge command creates a merge commit which brings in a convergence point into the commit history, thereby, bringing in the context under which the integration of changes occurred. This is essential when you are working on a public project and want every developer to have a shared context.
 
Rebasing a feature branch with changes bought from the long-running parent branch creates a clean linear history in the feature branch. But this eliminates the context under which the activity of rebasing was done. It is preferred when you are working as part of a small team and, it is relatively easy to collaborate and communicate with all the developers about the changes done to the feature branch.

References

1.    https://git-scm.com/book/en/v2
2.    https://www.atlassian.com/git/tutorials/merging-vs-rebasing
3.    https://medium.com/datadriveninvestor/git-rebase-vs-merge-cc5199edd77c

Understanding Patch in Git via Interactive Staging

14. August 2019 12:23 by Parakh in Git  //  Tags: , ,   //   Comments
In this article, we get to understand the functionality of patch in git via interactive staging. Patch functinality is useful when you want to send selective changes in a file in a particular commit.

Git is a super flexible version control system and offers capabilities that you may have felt the need for when working with other version control systems, but were not available. One such capability that is available is the ability to assign parts of a file in a commit, thereby, creating a logical sequence to the work done in a file over time.

Let’s understand the patch operation in Git via a simple example.

Consider a text file that you have created over time and you want the work to go in two separate commits representing a logical sequence to work:

$ mkdir PatchDemo
$ cd PatchDemo
$ git init
$ touch file.txt
$ notepad file.txt

 

Key in the following lines inside the file as shown in the image below:

Line 1 - Work done in first commit


Line 3 - Work done in first commit


Line 5 - Work done in first commit


Line 7 - Work done in first commit


Line 9 - Work done in first commit


Line 11 - Work done in first commit


Line 13 - Work done in first commit


Line 15 - Work done in first commit


Line 17 - Work done in first commit


Line 19 - Work done in first commit


Line 21 - Work done in first commit


Line 23 - Work done in first commit


Line 25 - Work done in first commit

The reason why we are introducing text in this fashion is to allow sufficient room for Git to differentiate between the work done in different logical sequences. In Git terminology, the work that needs to go in a certain patch is called a hunk. A patch can contain several hunks.
 

For text to be considered in different hunks, there needs to be a large enough difference between pre-existing work and new work meant to go in a hunk. Hence alternate lines in the original file would constitute a somewhat large enough body of pre-existing work.

Now let’s commit the work done:

$ git add file.txt
$ git commit -m “First commit consisting of work done in file.txt”

Now let’s modify the file:
 
Line 1 - Work done in first commit
Line 2 – Work done in second commit
Line 3 - Work done in first commit


Line 5 - Work done in first commit


Line 7 - Work done in first commit


Line 9 - Work done in first commit


Line 11 - Work done in first commit
Line 12 – Work done in third commit
Line 13 - Work done in first commit


Line 15 - Work done in first commit


Line 17 - Work done in first commit


Line 19 - Work done in first commit


Line 21 - Work done in first commit


Line 23 - Work done in first commit
Line 24 – Work done in second commit
Line 25 - Work done in first commit

Now let’s do the interactive staging of the file.txt and use the patch functionality to stage file.txt for two separate commits:

$ git add -i

This will start the interactive staging and it will look somewhat like in the following figure:

Patch using Interactive Staging

 
Once you enter interactive staging, you will be asked to choose from several options viz. status, update, revert, add untracked, patch, diff, quit and help. You need to choose “p” or number 5 to perform a patch.

Once that is selected, git presents you with a list of files that are available in the repository. Note, that in this case we only have one file and it shows the status of the file as consisting of 3 unstaged changes. You need to enter the serial number of the file you want to patch, thus, in this case, that would be 1.

Git will show an asterisk next to the selected file and will ask for permission to move further. You need to hit enter once to permit Git to move ahead.

Once we permit to parse the file, Git will present you hunks i.e. changes that the file contains that are not available in its last committed version. The lines in green show the changes, while the ones in white show the pre-existing work. Just below the hunk is the query posted by Git, and presented with one letter acceptable answers. If you want to know more about the meaning of those letters, press “?”. It will present you with the meaning of all those options like in the following figure:

Patch Options

In our case, we want to stage the first and the third hunks. Git then moves forward and presents you the next hunk. In the second hunk, we have the line added “Line 12 – Work done in the third commit”, and accordingly we would preserve it for the third commit. So you need to press “y” for the first and third hunks and n for the second hunk.

Once you are done marking the hunks that need to go into a commit, you will be re-directed to the initial menu. This time, press “s” for getting the status and you would be presented something like shown in the following figure:

Status on Patch

We can see in the image that now we have staged two changes and one remains unstaged. The staged changes are the two hunks that we staged in our previous steps of selecting hunks, while the one unstaged change is the hunk that we did not selected to go into our patch.

If you run a diff, then you will be able to see the changes that will go into the next commit, appearing in green and the existing work appearing in white, as shown below:

Diff of staged changes

Now let’s quit this interactive staging utility and run git status. This would give us something like shown in the following figure:

Git Status

The reason Git is showing the same file in the staged and unstaged form is, because we have staged only part of the file in the form of the selected two hunks.

Now we can go about our business as usual, and make two commits – The first commit consisting the two selected hunks and the second commit having the rest of the changes, as shown in the figure below:

Final Commits

As you can see the first commit carried two changes and the second commit carried a single change. Once we are done with the two commits, there were no changes to be committed any further.

Month List

ParakhSinghal@Twitter.com

Note: For Customization and Configuration, CheckOut Recent Tweets Documentation