Git Tip of the Day – rebasing is fun

Rebasing means re-ordering the commits in history. It always changes history. It means rebasing is good for your local branches, but not for branches that many people work on. Because in that case changed history means a lot of annoyed programmers.

Rebasing feature branch to master

The most common use-case is probably with feature branches. You split your feature branch from master. You develop a feature, appending a few commits. Now, you would like to send a patch to the project. But in the meantime the master branch moved. Project developers politely ask you to send the patch based on the current master. What to do? Rebase against master.

$ git checkout feature
$ git rebase master

This will find the common ancestor of feature and master (the split point). Then it will apply all changes from master. And then it will apply all changes you have done on feature since the split point. See the images.

Your code will then seem as if you based it on the current master. You can then do a simple patch git diff master feature and send it.

Rebasing a single branch

Rebasing can also be used on a single branch. It can re-order the commits in history, delete some commits, squash some commits, and more.

Let’s say I have realized I would like to have my last three commits in the feature branch in a different order:

$ git rebase --interactive HEAD~3
pick bb6f59e Update minimum python version requirement to 2.6 in the spec file.
pick 4e929b8 Improve docs in autoqa package.
pick 7796850 changed NEWS

# Rebase d03c059..7796850 onto d03c059
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

HEAD~3 denotes the fourth most recent commit. That is the base, the last commit that stays intact. It opens an editor with the last three commits in their commit order, most recent being last. You can now easily change commit order by re-ordering the lines. You can also delete commit by deleting the line.

If you change the keyword in the first column, you can edit commit, reword it, squash several commits into one, and many more. Play with it, it’s fun. If you choose to edit some commit, the rebasing process will stop at that point and allow you to do the changes. After you’re done, just execute git rebase --continue to continue with the process. If git find some conflict it can’t solve, it will again stop, you resolve the conflict and use the same command for continuing. You can abort the whole process anytime, if you wish, by issuing git rebase --abort.

Of course, always back-up your code by keeping a back-up branch before performing a difficult rebase process.

Enjoy!

Flattr this

Git Tip of the Day – changing last commit

Very often you realize you want to change something right after you’ve committed it. At least I do. How to change the last commit?

It is extremely easy to do. Let’s say you want to change just the commit message. All you need is to run:

$ git commit --amend

That’s all. An editor will open up for you with the last commit message and you can edit it. Piece of cake.

Now something more advanced! You realized you forgot to add some file/remove some file/change some file. You need to change the file contents of the last commit. How to do that?

It’s easy. First, do all the changes you need:

$ vim file_to_be_changed
$ touch file_to_be_added

Now put the desired changes into the staging area:

$ git add file_to_be_changed
$ git add file_to_be_added
$ git rm file_to_be_removed

And now run the same magic command as before:

$ git commit --amend

That’s all. Not only git will allow you to change the commit message, but it will also add the changes in the staging area to the last commit. Work done. Ask for pay raise.

Important warning at the end: Changing last commit (even just the commit message) changes SHA1 checksum of the commit. Never change commits that you already pushed to some remote location if there is a chance somebody else already based his/her work on it. You would cause such person a lot of difficulties. Change commits for your local development only.

Enjoy!

Flattr this

Git Tip of the Day – splitting changes into several patches

Sometimes you change the code and then you realize you would like to commit it as several separate patches. For example you fixed two bugs at once but then you decided you want to have the commits separated into two logical units – first bug fix and second bug fix.

As long as the changes don’t involve the same file, it’s easy to do. You just git add first file, commit, git add second file, commit. But what will you do if the two changes involved the same file?

There is a simple solution: git add --patch

Let’s see an example. I have made two changes in my README file – deleted one line and added one line. I want to stage it as two separate patches.

$ git add --patch README
diff --git a/README b/README
index 05ad773..2768fc1 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-Welcome to autoqa!

 Everything here is under heavy development so don't expect anything to
 stay stable - modules, APIs, whatever.  If it's expected to stay stable we
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
@@ -8,6 +7,7 @@ but if you have questions please feel free to contact us.
 == Architecture ==

 There's four main things in this tree: 'autoqa', 'events', 'watchers', and 'tests'.
+foo!

 'autoqa' is the main binary that kicks off tests (using the autotest framework -
 see http://autotest.kernel.org/ for details). It's typically supposed to be 
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n

Git detects “hunks” (changed sections) in the file and asks for every hunk whether you want to stage it or not (see the prompt “Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?“) . You can see that I staged the first hunk, and I didn’t staged the second one. (For explanation of the all the letters in the prompts type in the question mark.).

Now I see something like this:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   README
#
# 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:   README
#

Some parts of README were staged, some parts were not. I can display the difference between my HEAD and the staging area:

$ git diff --staged 
diff --git a/README b/README
index 05ad773..bbc5249 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-Welcome to autoqa!

 Everything here is under heavy development so don't expect anything to
 stay stable - modules, APIs, whatever.  If it's expected to stay stable we

You can see that only one changed (the deleted line) is part of my staging area. Now I can commit and only this change will be part of it. Then I’ll stage the remaining changes in README and commit again. I’ve just easily split two changes in one file into two different commits. Voila!

Interactive mode

You can do the same way through interactive mode if you prefer:

$ git add --interactive 
           staged     unstaged path
  1:    unchanged        +1/-1 README

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now>

You are presented with the list of changed files and you can do different operations on them. Don’t forget to press h or 8 in this step. Action patch or 5 is exactly the same we saw with git add --patch. After you select which file to work on (and press Enter once more), you select all the desired hunks:

What now> p   
           staged     unstaged path
  1:    unchanged        +1/-1 README
Patch update>> 1
           staged     unstaged path
* 1:    unchanged        +1/-1 README
Patch update>> 
diff --git a/README b/README
index 05ad773..2768fc1 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-Welcome to autoqa!
 
 Everything here is under heavy development so don't expect anything to
 stay stable - modules, APIs, whatever.  If it's expected to stay stable we
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
@@ -8,6 +7,7 @@ but if you have questions please feel free to contact us.
 == Architecture ==
 
 There's four main things in this tree: 'autoqa', 'events', 'watchers', and 'tests'.
+foo!
 
 'autoqa' is the main binary that kicks off tests (using the autotest framework -
 see http://autotest.kernel.org/ for details). It's typically supposed to be 
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now>

Now I can display the status:

What now> s
           staged     unstaged path
  1:        +0/-1        +1/-0 README

*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now>

In the README file I have 0 lines added and 1 line removed in the staging area, and one line added and zero lines removed in the unstaged area.

I can easily display the diff between HEAD and the staging area:

What now> d
           staged     unstaged path
  1:        +0/-1        +1/-0 README
Review diff>> 1
diff --git a/README b/README
index 05ad773..bbc5249 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-Welcome to autoqa!
 
 Everything here is under heavy development so don't expect anything to
 stay stable - modules, APIs, whatever.  If it's expected to stay stable we
*** Commands ***
  1: status	  2: update	  3: revert	  4: add untracked
  5: patch	  6: diff	  7: quit	  8: help
What now>

That’s exactly what I want. Now I can just quit the interactive mode and commit.

The first approach (non-interactive) seems faster to me, but interactive mode can be also appealing for some people.

Enjoy!

Flattr this

Git Tip of the Day – double and triple points syntax

There’s a lot of cryptic syntax in git. Let’s talk today about using double and triple points in commands.

Double points syntax is used mostly when selecting an interval of commits. Let’s see an example:

$ git log master..feature

This will show you all commits in the feature branch that were added since it branched off from master. To be more precise – two points syntax will select all commits available in the feature branch and not available in the master branch.

By the way, you can also write the command above like this:

$ git log feature --not master

Again it means select all commits from freature that are not in master. The advantage of the latter syntax is that you can use it with more than two branches:

$ git log feature1 feature2 --not master

This will show you commits from two feature branches that are not in master.

Of course this is not just about log command. All commands working with commit intervals should support it. E.g. format-patch:

$ git format-patch master..feature

And other commands.

Triple points syntax is on the other hand used to select commits that are in either of the branches, but not at both at the same time. An example:

$ git log master...feature

This will show you all commits that are either in master or in feature, but not in both. (If you intend to use this command, be sure to study the –left-right option, that will add indicators to each commit from which branch they are from).

Now what I find most confusing about git is a double and triple points syntax in diff command. The git authors say that diff command doesn’t operate of commit interval but on two commits (end-points) directly, therefore the above semantics doesn’t apply. They (mis)used the syntax for something else in this command. Oh, well. Let’s see.

First, the standard usage of diff command is following:

$ git diff master feature

That will display differences directly between these two commits. If you added something in feature, it will be seen as an added line in the diff/patch view. If you added something in master (since the time the feature branched off), it will be seen as a removed line in the diff/patch view. Not really handy if you want to see just the changes you made.

The double points syntax is equivalent to the no points syntax:

$ git diff master..feature

So it does not look for their common ancestor and compares it to feature, as you might expect, no. That’s what the triple points syntax is for!

$ git diff master...feature

This will finally show you the changes you have done on feature since you branched off from master. It is equivalent to:

$ git diff $(git-merge-base master feature) feature

As the bottom line, most commands use a standardized meaning of double and triple points syntax (see man gitrevisions). However the diff command uses a different syntax – triple points equals to double points in gitrevisions and double points has no special meaning. Confusing? I believe so. But the syntax is too important so we have to remember it anyway.

Cheers.

Flattr this

Git Tip of the Day – track remote branches easily

Because I always forget it and then can’t remember, I have to write this down.

I have some local and remote branches in my project:

$ git branch -avv
* master                         74b7a34 [origin/master] Release 0.6.0
  release-0.6                    69adc4e [origin/release-0.6] Release 0.6.0
  remotes/origin/HEAD            -> origin/master
  remotes/origin/aqc             b4ff4ba update to Fedora 14, remove crontab handling
  remotes/origin/clumens         ce5b3f1 Rollback previous README change.  The --url hook argument still exists.
  remotes/origin/copylib         1888da9 copy config files and autoqa library to the client
  remotes/origin/hongqing        8fc7a53 run analog in install.py

Now if I want to check out a remote branch called aqc, have it available in a local branch and set it as tracking, there is a neat short command to do all of this at once (instead of combining different options and whatnot):

$ git checkout aqc
Branch aqc set up to track remote branch aqc from origin.
Switched to a new branch 'aqc'
$ git branch -vv
* aqc         b4ff4ba [origin/aqc] update to Fedora 14, remove crontab handling
  master      74b7a34 [origin/master] Release 0.6.0
  release-0.6 69adc4e [origin/release-0.6] Release 0.6.0

Oh yes.

Flattr this

AutoQA 0.6 released

AutoQA 0.6 has just been released and deployed.

Most important changes:

  • Upgradepath test checks FXX-updates-pending repositories now and if there is a build that fulfills upgrade path constraint then the test is marked as passed. Note that all such pending builds must be pushed at the same time in order to satisfy the upgrade path requirement. This means that package maintainers should no longer receive errors when pushing the same update simultaneously to several Fedora releases.
  • Depcheck test now operates on the latest versions of packages from the stable repositories. That should fix some of the encountered problems (ABRT guys know best).
  • There is a custom HTTP 404 page when accessing unavailable log file.

If you have some comments, bug reports or enhancement suggestions, please let us know. Our mailing list autoqa-devel or our Trac instance is at your disposal.

Git Tip of the Day – introduction text when emailing patches

You have created a great patch and want to send it to project maintainers, but you don’t want to create all the emails by hand (that means opening your email client and copy-n-pasting the patch into the message or – god forbid – adding those patches as attachments). At the same time you would like to provide some introduction text for your patches. How do you do that?

First option: use send-email --compose

git send-email is able to send the patches for you. If you use the --compose option, you will be able to write up an introduction that will be sent as the first email of the whole email series.

Second option: use format-patch --cover-letter

git format-patch creates a series of patches nicely formatted to be used as email bodies. The output is one file per commit. The magical option --cover-letter will create one more file, probably called 0000-cover-letter.patch, that you can use for introduction of your changes. This file will also contain a short summary of your patch series, including author names, commit messages and changed files. Neat.

Third option: misuse git patch syntax

If you have just a single commit change and you don’t like sending multiple emails, but you still want to provide further information (besides commit message), you can misuse the git patch syntax and smuggle some further information into it without affecting the very patch.

Let’s see a sample patch generated with git format-patch:

From 7ea3c50fa83950549de11c6834c465bc8f28b52b Mon Sep 17 00:00:00 2001
From: James Laska
Date: Mon, 1 Aug 2011 09:53:16 -0400
Subject: [PATCH] compose_tree - Save the setup.sh script for later debugging

---
 tests/compose_tree/compose_tree.sh |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/tests/compose_tree/compose_tree.sh b/tests/compose_tree/compose_tree.sh
index 66ecefd..c2e041d 100755
--- a/tests/compose_tree/compose_tree.sh
+++ b/tests/compose_tree/compose_tree.sh
@@ -204,6 +204,8 @@ let RETVAL+=$?

 # Report the results.  We do all the copies individually in case one of the files doesn't exist.
 mkdir -p ${RESULTSDIR}
+cp -f ${SETUP} ${RESULTSDIR}/setup.sh
+let RETVAL+=$?
 cp -f ${MOCKROOTDIR}/${KSCFG} ${RESULTSDIR}/ks.cfg
 let RETVAL+=$?
 cp -f ${MOCKROOTDIR}/../result/root.log ${RESULTSDIR}/
--
1.7.6

Now, if you look up the space between --- and tests/compose_tree/compose_tree.sh, that’s our secret spot. You can add any text in there, it will be visible to the developers, but it will not affect the patch. It then might look like this:

From 7ea3c50fa83950549de11c6834c465bc8f28b52b Mon Sep 17 00:00:00 2001
From: James Laska
Date: Mon, 1 Aug 2011 09:53:16 -0400
Subject: [PATCH] compose_tree - Save the setup.sh script for later debugging

---
This patch is really really important, because otherwise the world will end in
2012. Please accept it.
 tests/compose_tree/compose_tree.sh |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/tests/compose_tree/compose_tree.sh b/tests/compose_tree/compose_tree.sh
index 66ecefd..c2e041d 100755
... (the rest of the patch)

Don’t we love it, having so many options? I’m sure there are more of them, but I like these three.

Enjoy!

Flattr this

Git Tip of the Day – syntax highlighting for commit messages

If your favorite editor is vim, you surely already noticed that even with default configuration you’ll get a nice syntax highlighting for commit messages. It notifies you if your commit message is too long, it auto-wraps long lines in the description. Pretty.

 

 

But sometimes all of this does not work. You still get only black and white display, no colors, no highlighting, nothing. My colleague spent two hours of googling for vim documentation, adjusting configuration, copying my configuration, to no avail.

Well if you reached that point in your life, and I believe some of you did (according to similar problems mentioned throughout the Internets), then…

… just ensure vim is your default editor, will you? 😀

Because vi is the default editor in Fedora. If you don’t put something like

export EDITOR=vim

into your ~/.bashrc or somewhere, then you have vi by default. And that does not do any of that pretty stuff.

The solution can be simple, but sometimes it can be really hard to find. Enjoy the day.

Flattr this

Git Tip of the Day – detect moved files

When you rename your files in your project, git may display that as removing and adding the file. Let’s see a demo.

Let’s prepare a commit where we rename a file:

$ mv foo bar
$ git add -A
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	renamed:    foo -> bar
#
$ git commit -m 'improve naming'
[master 80d5850] improve naming
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename foo => bar (100%)

Now let’s show the default behavior of git:

$ git show
commit 80d5850fa16ecc783fa86a1d474c4324ec9f4c2d
Author: Kamil Páral
Date:   Wed Aug 3 13:49:01 2011 +0200

    improve naming

diff --git a/bar b/bar
new file mode 100644
index 0000000..4e4b354
--- /dev/null
+++ b/bar
@@ -0,0 +1,2 @@
+this is a
+sample file
diff --git a/foo b/foo
deleted file mode 100644
index 4e4b354..0000000
--- a/foo
+++ /dev/null
@@ -1,2 +0,0 @@
-this is a
-sample file

You will get the same output with git diffgit format-patch or git log -p. Basically the whole file is removed and added back again in another location. This is not great, because if the file has 1000 lines, then it will really clutter your view. There is a better approach:

$ git show -M
commit 80d5850fa16ecc783fa86a1d474c4324ec9f4c2d
Author: Kamil Páral
Date:   Wed Aug 3 13:49:01 2011 +0200

    improve naming

diff --git a/foo b/bar
similarity index 100%
rename from foo
rename to bar

We shortened the output to 4 simple lines. You see that similarity index is 100%, that means the files are the same, just at different locations (i.e. with different names). The similarity index can be lower, that means git can detect even files that changed a little during the move. The threshold can be set as an argument to the -M option.

If you use this option for patch creation, you may get a much shorter patches that are much more readable. The disadvantage? They are no longer “standard” patches that can be applied with patch command, you must use git to do that.


Flattr this