May the source be with you, but remember the KISS principle ;-)
Home Switchboard Unix Administration Red Hat TCP/IP Networks Neoliberalism Toxic Managers
(slightly skeptical) Educational society promoting "Back to basics" movement against IT overcomplexity and  bastardization of classic Unix


News Version Control & Configuration Management Tools Recommended Books Recommended Links Selected Papers Git tips  
Architecture Brooks law Conway Law A Note on the Relationship of Brooks Law and Conway Law The Mythical Man-Month Simplification and KISS Git
Software Life Cycle Models Software Prototyping Program Understanding Exteme programming as yet another SE fad Distributed software development anti-OO Literate Programming
Reverse Engineering Links Programming style Project Management Code Reviews and Inspections Configuration Management Design patterns CMM
Bad Software Information Overload Inhouse vs outsourced applications development OSS Development as a Special Type of Academic Research A Second Look at the Cathedral and Bazaar  Labyrinth of Software Freedom Programming as a profession
Testing Over 50 and unemployed Sysadmin Horror Stories Git tips SE quotes Humor Etc

The Eclipse Foundation reported in its annual community survey that as of May 2014, Git is now the most widely used source code management tool, with 42.9% of professional software developers reporting that they use Git as their primary source control system compared with 36.3% in 2013, 32% in 2012; or for Git responses excluding use of GitHub: 33.3% in 2014, 30.3% in 2013, 27.6% in 2012 and 12.8% in 2011.

Git's primitives are not inherently a source code management (SCM) system. They are like more like a virtual filesystem.  That might help to understand the design decisions you encounter in git. As Torvalds explains,

In many ways you can just see git as a filesystem – it's content-addressable, and it has a notion of versioning, but I really really designed it coming at the problem from the viewpoint of a filesystem person (hey, kernels is what I do), and I actually have absolutely zero interest in creating a traditional SCM system.

You might already have Git1 on your system because somebody installed it, To check this use which command:

which git

On RHEL and derivatives you can also use rpm command

rpm -q git

If you need to install it you can do it from main RHEL reposivry via yum install git

Consider also installing the software package named git-all. This package includes some additional dependency packages that add more power to Git.

Basic GIT-related terms:

Initializing a Repository in an Existing Directory

You can create a Git project using two main approaches. The first takes an existing project or directory and imports it into Git. The second clones an existing Git repository from another server.

If you’re starting to track an existing project in Git, you need to go to the project’s directory and type

git init

This creates a new subdirectory named .git that contains all of your necessary repository files – a Git repository skeleton. At this point, nothing in your project is tracked yet. 

If you want to start version-controlling existing files (as opposed to an empty directory), you should probably begin tracking those files and do an initial commit. You can accomplish that with a few git add commands that specify the files you want to track, followed by a git commit:

$ git add *.py
$ git add LICENSE
$ git commit -m 'initial project version'

We’ll go over what these commands do in just a minute. At this point, you have a Git repository with tracked files and an initial commit.

Cloning an Existing Repository

The copying of the existing repository to the local server is called cloning. Cloning performs three functions:

If you want to get a copy of an existing Git repository – for example, a project you’d like to contribute to – the command you need is git clone. If you’re familiar with other VCS systems such as Subversion, you’ll notice that the command is “clone” and not “checkout”. This is an important distinction – instead of getting just a working copy, Git receives a full copy of nearly all data that the server has. Every version of every file for the history of the project is pulled down by default when you run git clone. In fact, if your server disk gets corrupted, you can often use nearly any of the clones on any client to set the server back to the state it was in when it was cloned (you may lose some server-side hooks and such, but all the versioned data would be there

You clone a repository with git clone [url]. For example, if you want to clone the Git project named superproject, you can do so like this:

$ git clone

That creates a directory named “superproject” in your home directory, initializes a .git directory inside it, downloads the data for that repository, and checks out a working copy of the latest version. If you go into the new libgit2 directory, you’ll see the project files in there, ready to be worked on or used. If you want to clone the repository into a directory named something other than “libgit2”, you can specify that as the next command-line option:

$ git clone myproject

That command does the same thing as the previous one, but the target directory is called myproject.

Git has a number of different transfer protocols you can use. The most common is the https:// protocol, but you may also see git:// or user@server:path/to/repo.git, which uses the SSH transfer protocol.

To see the location of your remote repository, execute the git remote -v command:

$ git remote -v

Getting Help

If you ever need help while using Git, there are three ways to get the manual page (manpage) help for any of the Git commands:

$ git help <verb>;
$ git <verb> --help
$ man git-<verb>;

For example, you can get the manpage help for the config command by running

$ git help config

These commands are nice because you can access them anywhere, even offline. If the manpages and this book aren’t enough and you need in-person help, you can try the #git or #github channel on the Freenode IRC server ( These channels are regularly filled with hundreds of people who are all very knowledgeable about Git and are often willing to help.

Checking Your Settings

If you want to check your settings, you can use the git config --list command to list all the settings Git can find at that point:

$ git config --list

You may see keys more than once, because Git reads the same key from different files (/etc/gitconfig and ~/.gitconfig, for example). In this case, Git uses the last value for each unique key it sees.

You can also check what Git thinks a specific key’s value is by typing git config <key>:

$ git config

Imagine you are working on some files one day and it’s getting late. It is Friday afternoon and you just can’t wait for the weekend to start. On the following Monday you arrive at work and realize you have no idea in what area you left your file. Were they added to the staging area? All of them or just some? Did you commit any of them to the local repository?

This is when you want to run the git status command:

$ git status


A few tips on how to customize your Git environment

Now that you have Git on your system, you’ll want to do a few things to customize your Git environment. You should have to do these things only once on any given computer; they’ll stick around between upgrades. You can also change them at any time by running through the commands again.

Git comes with a tool called git config that lets you get and set configuration variables that control all aspects of how Git looks and operates. These variables can be stored in three different places:

  1. /etc/gitconfig file: Contains values for every user on the system and all their repositories. If you pass the option --system to git config, it reads and writes from this file specifically.
  2. ~/.gitconfig or ~/.config/git/config file: Specific to your user. You can make Git read and write to this file specifically by passing the --global option.
  3. config file in the Git directory (that is, .git/config) of whatever repository you’re currently using: Specific to that single repository.

Each level overrides values in the previous level, so values in .git/config trump those in /etc/gitconfig.

On Windows systems, Git looks for the .gitconfig file in the $HOME directory (C:\Users\$USER for most people). It also still looks for /etc/gitconfig, although it’s relative to the root, which is wherever you decide to install Git on your Windows system when you run the installer. If you are using Git for Windows 2.x or later, there is also a system-level config file at C:\Documents and Settings\All Users\Application Data\Git\config on Windows XP, and in C:\ProgramData\Git\config on Windows Vista and newer. This config file can only be changed by git config -f <file> as an admin.

Your Identity

The first thing you should do when you install Git is to set your user name and email address. This is important because every Git commit uses this information, and it’s immutably baked into the commits you start creating:

$ git config --global "John Doe"
$ git config --global [email protected]

Again, you need to do this only once if you pass the --global option, because then Git will always use that information for anything you do on that system. If you want to override this with a different name or email address for specific projects, you can run the command without the --global option when you’re in that project.

Many of the GUI tools will help you do this when you first run them.

Your Editor

Now that your identity is set up, you can configure the default text editor that will be used when Git needs you to type in a message. If not configured, Git uses your system’s default editor, which is system dependant.

If you want to use a different text editor, such as VIM, you can do the following:

$ git config --global core.editor vim

While on a Windows system, if you want to use a different text editor, such as Notepad++, you can do the following:

On a x86 system

$ git config --global core.editor "'C:/Program Files/Notepad++/notepad++.exe' -multiInst -nosession"

On a x64 system

$ git config --global core.editor "'C:/Program Files (x86)/Notepad++/notepad++.exe' -multiInst -nosession"
Warning: You may find, if you don’t setup an editor like this, you will likely get into a really confusing state when they are launched. Such example on a Windows system may include a prematurely terminated Git operation during a Git initiated edit.

Top Visited
Past week
Past month


Old News ;-)

[Nov 22, 2020] How to present a GitHub project for your resume The HFT Guy

Nov 22, 2020 |

Whether the naming convention will be " doc " or " docs " is an unimportant detail. For example, here are Simple Folder Structure Conventions for GitHub projects:

[Jul 14, 2020] The life-changing magic of git rebase -i -

Jul 14, 2020 |

The life-changing magic of git rebase -i Make everyone think you write perfect code the first time (and make your patches easier to review and merge). 29 Apr 2020 Dave Neary (Red Hat) Feed 66 up 5 comments Image by : WOCinTech Chat. Modified by CC BY-SA 4.0 x Subscribe now

Get the highlights in your inbox every week.

Software development is messy. So many wrong turns, typos to fix, quick hacks and kludges to correct later, off-by-one errors you find late in the process. With version control, you have a pristine record of every wrong turn and correction made during the process of creating the "perfect" final product -- a patch ready to submit upstream. Like the outtakes from movies, they are a little embarrassing and sometimes amusing.

Wouldn't it be great if you could use version control to save your work regularly at waypoints, and then when you have something you are ready to submit for review, you could hide all of that private drafting work and just submit a single, perfect patch? Meet git rebase -i , the perfect way to rewrite history and make everyone think that you produce perfect code the first time!

What does git rebase do?

In case you're not familiar with the intricacies of Git, here is a brief overview. Under the covers, Git associates different versions of your project with a unique identifier, which is made up of a hash of the parent node's unique identifier, and the difference between the new version and its parent node. This creates a tree of revisions, and each person who checks out the project gets their own copy. Different people can take the project in different directions, each starting from potentially different branch points.


The master branch in the "origin" repo on the left and the private branch on your personal copy on the right. Programming and development

There are two ways to integrate your work back with the master branch in the original repository: one is to use git merge , and the other is to use git rebase . They work in very different ways.

When you use git merge , a new commit is created on the master branch that includes all of the changes from origin plus all of your local changes. If there are any conflicts (for example, if someone else has changed a file you are also working with), these will be marked, and you have an opportunity to resolve the conflicts before committing this merge commit to your local repository. When you push your changes back to the parent repository, all of your local work will appear as a branch for other users of the Git repository.

But git rebase works differently. It rewinds your commits and replays those commits again from the tip of the master branch. This results in two main changes. First, since your commits are now branching off a different parent node, their hashes will be recalculated, and anyone who has cloned your repository may now have a broken copy of the repository. Second, you do not have a merge commit, so any merge conflicts are identified as your changes are being replayed onto the master branch, and you need to fix them before proceeding with the rebase. When you push your changes now, your work does not appear on a branch, and it looks as though you wrote all of your changes off the very latest commit to the master branch.


Merge commits (left) preserve history, while rebase (right) rewrites history.

However, both of these options come with a downside: everyone can see all your scribbles and edits as you worked through problems locally before you were ready to share your code. This is where the --interactive (or -i for short) flag to git rebase comes into the picture.

Introducing git rebase -i

The big advantage of git rebase is that it rewrites history. But why stop at just pretending you branched off a later point? There is a way to go even further and rewrite how you arrived at your ready-to-propose code: git rebase -i , an interactive git rebase .

This feature is the "magic time machine" function in Git. The flag allows you to make sophisticated changes to revision history while doing a rebase. You can hide your mistakes! Merge many small changes into one pristine feature patch! Reorder how things appear in revision history!


When you run git rebase -i , you get an editor session listing all of the commits that are being rebased and a number of options for what you can do to them. The default choice is pick .

When you are finished, simply save the final result, and the rebase will execute. At each stage where you have chosen to modify a commit (either with reword , edit , squash , or when there is a conflict), the rebase stops and allows you to make the appropriate changes before continuing.

The example above results in "One-liner bug fix" and "Integrate new header everywhere" being merged into one commit, and "New header for docs website" and "D'oh - typo. Fixed" into another. Like magic, the work that went into the other commits is still there on your branch, but the associated commits have disappeared from your history!

This makes it easy to submit a clean patch to an upstream project using git send-email or by creating a pull request against the parent repository with your newly tidied up patchset. This has a number of advantages, including that it makes your code easier to review, easier to accept, and easier to merge.

Rodrigo Graça on 29 Apr 2020

😂 Im used to do this manually. I work, then stash my changes before commitiing then checkout master, git pull, make new branch, apply my stash, commit and push.

Dave Neary on 07 May 2020

That works, but I find rebase -i works pretty well for balancing regular sync points and clean PRs. Thanks for the feedback!

Knusper on 30 Apr 2020

I like that you used little post-it notes for your illustration 😁

Dave Neary on 07 May 2020

Thank you! I tried in Inkscape, but doodling on post-it notes ended up being faster :-)

Joël Krähemann on 12 May 2020

I like the history of my code and value to have one.

Further if you don't show history to master certain services might not recognized your actual commits.

I prefer on master branch: `git merge -s recursive -X theirs 3.3.x`

[Jul 12, 2020] 6 handy Bash scripts for Git -

Jul 12, 2020 |

6 handy Bash scripts for Git These six Bash scripts will make your life easier when you're working with Git repositories. 15 Jan 2020 Bob Peterson (Red Hat) Feed 86 up 2 comments Image by : x Subscribe now

Get the highlights in your inbox every week. More on Git

I wrote a bunch of Bash scripts that make my life easier when I'm working with Git repositories. Many of my colleagues say there's no need; that everything I need to do can be done with Git commands. While that may be true, I find the scripts infinitely more convenient than trying to figure out the appropriate Git command to do what I want. 1. gitlog

gitlog prints an abbreviated list of current patches against the master version. It prints them from oldest to newest and shows the author and description, with H for HEAD , ^ for HEAD^ , 2 for HEAD~2, and so forth. For example:

$ gitlog
-----------------------[ recovery25 ]-----------------------
11 340d27a33895 Bob Peterson gfs2: drain the ail2 list after io errors
10 9b3c4e6efb10 Bob Peterson gfs2: clean up iopen glock mess in gfs2_create_inode
9 d2e8c22be39b Bob Peterson gfs2: Do proper error checking for go_sync family of glops
8 9563e31f8bfd Christoph Hellwig gfs2: use page_offset in gfs2_page_mkwrite
7 ebac7a38036c Christoph Hellwig gfs2: don't use buffer_heads in gfs2_allocate_page_backing
6 f703a3c27874 Andreas Gruenbacher gfs2: Improve mmap write vs. punch_hole consistency
5 a3e86d2ef30e Andreas Gruenbacher gfs2: Multi-block allocations in gfs2_page_mkwrite
4 da3c604755b0 Andreas Gruenbacher gfs2: Fix end-of-file handling in gfs2_page_mkwrite
3 4525c2f5b46f Bob Peterson Rafael Aquini's slab instrumentation
2 a06a5b7dea02 Bob Peterson GFS2: Add go_get_holdtime to gl_ops
^ 8ba93c796d5c Bob Peterson gfs2: introduce new function remaining_hold_time and use it in dq
H e8b5ff851bb9 Bob Peterson gfs2: Allow rgrps to have a minimum hold time

If I want to see what patches are on a different branch, I can specify an alternate branch:

$ gitlog recovery24
2. just prints the patch SHA1 IDs:

-----------------------[ recovery25 ]-----------------------
56908eeb6940 2ca4a6b628a1 fc64ad5d99fe 02031a00a251 f6f38da7dd18 d8546e8f0023 fc3cc1f98f6b 12c3e0cb3523 76cce178b134 6fc1dce3ab9c 1b681ab074ca 26fed8de719b 802ff51a5670 49f67a512d8c f04f20193bbb 5f6afe809d23 2030521dc70e dada79b3be94 9b19a1e08161 78a035041d3e f03da011cae2 0d2b2e068fcd 2449976aa133 57dfb5e12ccd 53abedfdcf72 6fbdda3474b3 49544a547188 187032f7a63c 6f75dae23d93 95fc2a261b00 ebfb14ded191 f653ee9e414a 0e2911cb8111 73968b76e2e3 8a3e4cb5e92c a5f2da803b5b 7c9ef68388ed 71ca19d0cba8 340d27a33895 9b3c4e6efb10 d2e8c22be39b 9563e31f8bfd ebac7a38036c f703a3c27874 a3e86d2ef30e da3c604755b0 4525c2f5b46f a06a5b7dea02 8ba93c796d5c e8b5ff851bb9

Again, it assumes the current branch, but I can specify a different branch if I want.

3. gitlog.id2

gitlog.id2 is the same as but without the branch line at the top. This is handy for cherry-picking all patches from one branch to the current branch:

$ # create a new branch
$ git branch --track origin/master
$ # check out the new branch I just created
$ git checkout recovery26
$ # cherry-pick all patches from the old branch to the new one
$ for i in `gitlog.id2 recovery25` ; do git cherry-pick $i ;done 4. gitlog.grep

gitlog.grep greps for a string within that collection of patches. For example, if I find a bug and want to fix the patch that has a reference to function inode_go_sync , I simply do:

$ gitlog.grep inode_go_sync
-----------------------[ recovery25 - 50 patches ]-----------------------
11 340d27a33895 Bob Peterson gfs2: drain the ail2 list after io errors
10 9b3c4e6efb10 Bob Peterson gfs2: clean up iopen glock mess in gfs2_create_inode
9 d2e8c22be39b Bob Peterson gfs2: Do proper error checking for go_sync family of glops
152:-static void inode_go_sync(struct gfs2_glock *gl)
153:+static int inode_go_sync(struct gfs2_glock *gl)
163:@@ -296,6 +302,7 @@ static void inode_go_sync(struct gfs2_glock *gl)
8 9563e31f8bfd Christoph Hellwig gfs2: use page_offset in gfs2_page_mkwrite
7 ebac7a38036c Christoph Hellwig gfs2: don't use buffer_heads in gfs2_allocate_page_backing
6 f703a3c27874 Andreas Gruenbacher gfs2: Improve mmap write vs. punch_hole consistency
5 a3e86d2ef30e Andreas Gruenbacher gfs2: Multi-block allocations in gfs2_page_mkwrite
4 da3c604755b0 Andreas Gruenbacher gfs2: Fix end-of-file handling in gfs2_page_mkwrite
3 4525c2f5b46f Bob Peterson Rafael Aquini's slab instrumentation
2 a06a5b7dea02 Bob Peterson GFS2: Add go_get_holdtime to gl_ops
^ 8ba93c796d5c Bob Peterson gfs2: introduce new function remaining_hold_time and use it in dq
H e8b5ff851bb9 Bob Peterson gfs2: Allow rgrps to have a minimum hold time

So, now I know that patch HEAD~9 is the one that needs fixing. I use git rebase -i HEAD~10 to edit patch 9, git commit -a --amend , then git rebase --continue to make the necessary adjustments.

5. gitbranchcmp3

gitbranchcmp3 lets me compare my current branch to another branch, so I can compare older versions of patches to my newer versions and quickly see what's changed and what hasn't. It generates a compare script (that uses the KDE tool Kompare , which works on GNOME3, as well) to compare the patches that aren't quite the same. If there are no differences other than line numbers, it prints [SAME] . If there are only comment differences, it prints [same] (in lower case). For example:

$ gitbranchcmp3 recovery24
Branch recovery24 has 47 patches
Branch recovery25 has 50 patches

38 87eb6901607a 340d27a33895 [same] gfs2: drain the ail2 list after io errors
39 90fefb577a26 9b3c4e6efb10 [same] gfs2: clean up iopen glock mess in gfs2_create_inode
40 ba3ae06b8b0e d2e8c22be39b [same] gfs2: Do proper error checking for go_sync family of glops
41 2ab662294329 9563e31f8bfd [SAME] gfs2: use page_offset in gfs2_page_mkwrite
42 0adc6d817b7a ebac7a38036c [SAME] gfs2: don't use buffer_heads in gfs2_allocate_page_backing
43 55ef1f8d0be8 f703a3c27874 [SAME] gfs2: Improve mmap write vs. punch_hole consistency
44 de57c2f72570 a3e86d2ef30e [SAME] gfs2: Multi-block allocations in gfs2_page_mkwrite
45 7c5305fbd68a da3c604755b0 [SAME] gfs2: Fix end-of-file handling in gfs2_page_mkwrite
46 162524005151 4525c2f5b46f [SAME] Rafael Aquini's slab instrumentation
47 a06a5b7dea02 [ ] GFS2: Add go_get_holdtime to gl_ops
48 8ba93c796d5c [ ] gfs2: introduce new function remaining_hold_time and use it in dq
49 e8b5ff851bb9 [ ] gfs2: Allow rgrps to have a minimum hold time

Missing from recovery25:
The missing:
Compare script generated at: /tmp/ 6. gitlog.find

Finally, I have gitlog.find , a script to help me identify where the upstream versions of my patches are and each patch's current status. It does this by matching the patch description. It also generates a compare script (again, using Kompare) to compare the current patch to the upstream counterpart:

$ gitlog.find
-----------------------[ recovery25 - 50 patches ]-----------------------
11 340d27a33895 Bob Peterson gfs2: drain the ail2 list after io errors
lo 5bcb9be74b2a Bob Peterson gfs2: drain the ail2 list after io errors
10 9b3c4e6efb10 Bob Peterson gfs2: clean up iopen glock mess in gfs2_create_inode
fn 2c47c1be51fb Bob Peterson gfs2: clean up iopen glock mess in gfs2_create_inode
9 d2e8c22be39b Bob Peterson gfs2: Do proper error checking for go_sync family of glops
lo feb7ea639472 Bob Peterson gfs2: Do proper error checking for go_sync family of glops
8 9563e31f8bfd Christoph Hellwig gfs2: use page_offset in gfs2_page_mkwrite
ms f3915f83e84c Christoph Hellwig gfs2: use page_offset in gfs2_page_mkwrite
7 ebac7a38036c Christoph Hellwig gfs2: don't use buffer_heads in gfs2_allocate_page_backing
ms 35af80aef99b Christoph Hellwig gfs2: don't use buffer_heads in gfs2_allocate_page_backing
6 f703a3c27874 Andreas Gruenbacher gfs2: Improve mmap write vs. punch_hole consistency
fn 39c3a948ecf6 Andreas Gruenbacher gfs2: Improve mmap write vs. punch_hole consistency
5 a3e86d2ef30e Andreas Gruenbacher gfs2: Multi-block allocations in gfs2_page_mkwrite
fn f53056c43063 Andreas Gruenbacher gfs2: Multi-block allocations in gfs2_page_mkwrite
4 da3c604755b0 Andreas Gruenbacher gfs2: Fix end-of-file handling in gfs2_page_mkwrite
fn 184b4e60853d Andreas Gruenbacher gfs2: Fix end-of-file handling in gfs2_page_mkwrite
3 4525c2f5b46f Bob Peterson Rafael Aquini's slab instrumentation
Not found upstream
2 a06a5b7dea02 Bob Peterson GFS2: Add go_get_holdtime to gl_ops
Not found upstream
^ 8ba93c796d5c Bob Peterson gfs2: introduce new function remaining_hold_time and use it in dq
Not found upstream
H e8b5ff851bb9 Bob Peterson gfs2: Allow rgrps to have a minimum hold time
Not found upstream
Compare script generated: /tmp/

The patches are shown on two lines, the first of which is your current patch, followed by the corresponding upstream patch, and a 2-character abbreviation to indicate its upstream status:

Some of my scripts make assumptions based on how I normally work with Git. For example, when searching for upstream patches, it uses my well-known Git tree's location. So, you will need to adjust or improve them to suit your conditions. The gitlog.find script is designed to locate GFS2 and DLM patches only, so unless you're a GFS2 developer, you will want to customize it to the components that interest you.

Source code

Here is the source for these scripts.

1. gitlog #!/bin/bash
branch = $1

if test "x $branch " = x; then
branch = ` git branch -a | grep "*" | cut -d ' ' -f2 `

patches = 0
tracking = ` git rev-parse --abbrev-ref --symbolic-full-name @ { u } `

LIST = ` git log --reverse --abbrev-commit --pretty =oneline $tracking .. $branch | cut -d ' ' -f1 | paste -s -d ' ' `
for i in $LIST ; do patches =$ ( echo $patches + 1 | bc ) ; done

if [[ $branch =~ . * for-next. * ]]
start =HEAD
# start=origin/for-next
start =origin / master

tracking = ` git rev-parse --abbrev-ref --symbolic-full-name @ { u } `

/ usr / bin / echo "-----------------------[" $branch "]-----------------------"
patches =$ ( echo $patches - 1 | bc ) ;
for i in $LIST ; do
if [ $patches -eq 1 ] ; then
cnt = " ^"
elif [ $patches -eq 0 ] ; then
cnt = " H"
if [ $patches -lt 10 ] ; then
cnt = " $patches "
cnt = " $patches "
/ usr / bin / git show --abbrev-commit -s --pretty =format: " $cnt %h %<|(32)%an %s %n" $i
patches =$ ( echo $patches - 1 | bc )
#git log --reverse --abbrev-commit --pretty=format:"%h %<|(32)%an %s" $tracking..$branch
#git log --reverse --abbrev-commit --pretty=format:"%h %<|(32)%an %s" ^origin/master ^linux-gfs2/for-next $branch 2. #!/bin/bash
branch = $1

if test "x $branch " = x; then
branch = ` git branch -a | grep "*" | cut -d ' ' -f2 `

tracking = ` git rev-parse --abbrev-ref --symbolic-full-name @ { u } `

/ usr / bin / echo "-----------------------[" $branch "]-----------------------"
git log --reverse --abbrev-commit --pretty =oneline $tracking .. $branch | cut -d ' ' -f1 | paste -s -d ' ' 3. gitlog.id2 #!/bin/bash
branch = $1

if test "x $branch " = x; then
branch = ` git branch -a | grep "*" | cut -d ' ' -f2 `

tracking = ` git rev-parse --abbrev-ref --symbolic-full-name @ { u } `
git log --reverse --abbrev-commit --pretty =oneline $tracking .. $branch | cut -d ' ' -f1 | paste -s -d ' ' 4. gitlog.grep #!/bin/bash
param1 = $1
param2 = $2

if test "x $param2 " = x; then
branch = ` git branch -a | grep "*" | cut -d ' ' -f2 `
string = $param1
branch = $param1
string = $param2

patches = 0
tracking = ` git rev-parse --abbrev-ref --symbolic-full-name @ { u } `

LIST = ` git log --reverse --abbrev-commit --pretty =oneline $tracking .. $branch | cut -d ' ' -f1 | paste -s -d ' ' `
for i in $LIST ; do patches =$ ( echo $patches + 1 | bc ) ; done
/ usr / bin / echo "-----------------------[" $branch "-" $patches "patches ]-----------------------"
patches =$ ( echo $patches - 1 | bc ) ;
for i in $LIST ; do
if [ $patches -eq 1 ] ; then
cnt = " ^"
elif [ $patches -eq 0 ] ; then
cnt = " H"
if [ $patches -lt 10 ] ; then
cnt = " $patches "
cnt = " $patches "
/ usr / bin / git show --abbrev-commit -s --pretty =format: " $cnt %h %<|(32)%an %s" $i
/ usr / bin / git show --pretty =email --patch-with-stat $i | grep -n " $string "
patches =$ ( echo $patches - 1 | bc )
done 5. gitbranchcmp3 #!/bin/bash
# gitbranchcmp3 <old branch> [<new_branch>]
oldbranch = $1
newbranch = $2
script = / tmp /

/ usr / bin / rm -f $script
echo "#!/bin/bash" > $script
/ usr / bin / chmod 755 $script
echo "# Generated by" >> $script
echo "# Run this script to compare the mismatched patches" >> $script
echo " " >> $script
echo "function compare_them()" >> $script
echo "{" >> $script
echo " git show --pretty=email --patch-with-stat \$ 1 > /tmp/gronk1" >> $script
echo " git show --pretty=email --patch-with-stat \$ 2 > /tmp/gronk2" >> $script
echo " kompare /tmp/gronk1 /tmp/gronk2" >> $script
echo "}" >> $script
echo " " >> $script

if test "x $newbranch " = x; then
newbranch = ` git branch -a | grep "*" | cut -d ' ' -f2 `

tracking = ` git rev-parse --abbrev-ref --symbolic-full-name @ { u } `

declare -a oldsha1s = ( ` git log --reverse --abbrev-commit --pretty =oneline $tracking .. $oldbranch | cut -d ' ' -f1 | paste -s -d ' ' ` )
declare -a newsha1s = ( ` git log --reverse --abbrev-commit --pretty =oneline $tracking .. $newbranch | cut -d ' ' -f1 | paste -s -d ' ' ` )

#echo "old: " $oldsha1s
oldcount = ${#oldsha1s[@]}
echo "Branch $oldbranch has $oldcount patches"
oldcount =$ ( echo $oldcount - 1 | bc )
#for o in `seq 0 ${#oldsha1s[@]}`; do
# echo -n ${oldsha1s[$o]} " "
# desc=`git show $i | head -5 | tail -1|cut -b5-`

#echo "new: " $newsha1s
newcount = ${#newsha1s[@]}
echo "Branch $newbranch has $newcount patches"
newcount =$ ( echo $newcount - 1 | bc )
#for o in `seq 0 ${#newsha1s[@]}`; do
# echo -n ${newsha1s[$o]} " "
# desc=`git show $i | head -5 | tail -1|cut -b5-`

for new in ` seq 0 $newcount ` ; do
newsha = ${newsha1s[$new]}
newdesc = ` git show $newsha | head -5 | tail -1 | cut -b5- `
oldsha = " "
same = "[ ]"
for old in ` seq 0 $oldcount ` ; do
if test " ${oldsha1s[$old]} " = "match" ; then
continue ;
olddesc = ` git show ${oldsha1s[$old]} | head -5 | tail -1 | cut -b5- `
if test " $olddesc " = " $newdesc " ; then
oldsha = ${oldsha1s[$old]}
#echo $oldsha
git show $oldsha | tail -n + 2 | grep -v "index.*\.\." | grep -v "@@" > / tmp / gronk1
git show $newsha | tail -n + 2 | grep -v "index.*\.\." | grep -v "@@" > / tmp / gronk2
diff / tmp / gronk1 / tmp / gronk2 &> / dev / null
if [ $? -eq 0 ] ; then
# No differences
same = "[SAME]"
oldsha1s [ $old ] = "match"
git show $oldsha | sed -n '/diff/,$p' | grep -v "index.*\.\." | grep -v "@@" > / tmp / gronk1
git show $newsha | sed -n '/diff/,$p' | grep -v "index.*\.\." | grep -v "@@" > / tmp / gronk2
diff / tmp / gronk1 / tmp / gronk2 &> / dev / null
if [ $? -eq 0 ] ; then
# Differences in comments only
same = "[same]"
oldsha1s [ $old ] = "match"
oldsha1s [ $old ] = "match"
echo "compare_them $oldsha $newsha " >> $script
echo " $new $oldsha $newsha $same $newdesc "

echo "Missing from $newbranch :"
the_missing = ""
# Now run through the olds we haven't matched up
for old in ` seq 0 $oldcount ` ; do
if test ${oldsha1s[$old]} ! = "match" ; then
olddesc = ` git show ${oldsha1s[$old]} | head -5 | tail -1 | cut -b5- `
echo " ${oldsha1s[$old]} $olddesc "
the_missing = ` echo " $the_missing ${oldsha1s[$old]} " `

echo "The missing: " $the_missing
echo "Compare script generated at: $script "
#git log --reverse --abbrev-commit --pretty=oneline $tracking..$branch | cut -d ' ' -f1 |paste -s -d ' ' 6. gitlog.find #!/bin/bash
# Find the upstream equivalent patch
# gitlog.find
cwd = $PWD
param1 = $1
ubranch = $2
patches = 0
script = / tmp /
echo "#!/bin/bash" > $script
/ usr / bin / chmod 755 $script
echo "# Generated by" >> $script
echo "# Run this script to compare the mismatched patches" >> $script
echo " " >> $script
echo "function compare_them()" >> $script
echo "{" >> $script
echo " cwd= $PWD " >> $script
echo " git show --pretty=email --patch-with-stat \$ 2 > /tmp/gronk2" >> $script
echo " cd ~/linux.git/fs/gfs2" >> $script
echo " git show --pretty=email --patch-with-stat \$ 1 > /tmp/gronk1" >> $script
echo " cd $cwd " >> $script
echo " kompare /tmp/gronk1 /tmp/gronk2" >> $script
echo "}" >> $script
echo " " >> $script

#echo "Gathering upstream patch info. Please wait."
branch = ` git branch -a | grep "*" | cut -d ' ' -f2 `
tracking = ` git rev-parse --abbrev-ref --symbolic-full-name @ { u } `

cd ~ / linux.git
if test "X ${ubranch} " = "X" ; then
ubranch = ` git branch -a | grep "*" | cut -d ' ' -f2 `
utracking = ` git rev-parse --abbrev-ref --symbolic-full-name @ { u } `
# gather a list of gfs2 patches from master just in case we can't find it
#git log --abbrev-commit --pretty=format:" %h %<|(32)%an %s" master |grep -i -e "gfs2" -e "dlm" > /tmp/gronk
git log --reverse --abbrev-commit --pretty =format: "ms %h %<|(32)%an %s" master fs / gfs2 / > / tmp / gronk.gfs2
# ms = in Linus's master
git log --reverse --abbrev-commit --pretty =format: "ms %h %<|(32)%an %s" master fs / dlm / > / tmp / gronk.dlm

cd $cwd
LIST = ` git log --reverse --abbrev-commit --pretty =oneline $tracking .. $branch | cut -d ' ' -f1 | paste -s -d ' ' `
for i in $LIST ; do patches =$ ( echo $patches + 1 | bc ) ; done
/ usr / bin / echo "-----------------------[" $branch "-" $patches "patches ]-----------------------"
patches =$ ( echo $patches - 1 | bc ) ;
for i in $LIST ; do
if [ $patches -eq 1 ] ; then
cnt = " ^"
elif [ $patches -eq 0 ] ; then
cnt = " H"
if [ $patches -lt 10 ] ; then
cnt = " $patches "
cnt = " $patches "
/ usr / bin / git show --abbrev-commit -s --pretty =format: " $cnt %h %<|(32)%an %s" $i
desc = `/ usr / bin / git show --abbrev-commit -s --pretty =format: "%s" $i `
cd ~ / linux.git
cmp = 1
up_eq = ` git log --reverse --abbrev-commit --pretty =format: "lo %h %<|(32)%an %s" $utracking .. $ubranch | grep " $desc " `
# lo = in local for-next
if test "X $up_eq " = "X" ; then
up_eq = ` git log --reverse --abbrev-commit --pretty =format: "fn %h %<|(32)%an %s" master.. $utracking | grep " $desc " `
# fn = in for-next for next merge window
if test "X $up_eq " = "X" ; then
up_eq = ` grep " $desc " / tmp / gronk.gfs2 `
if test "X $up_eq " = "X" ; then
up_eq = ` grep " $desc " / tmp / gronk.dlm `
if test "X $up_eq " = "X" ; then
up_eq = " Not found upstream"
cmp = 0
echo " $up_eq "
if [ $cmp -eq 1 ] ; then
UP_SHA1 = ` echo $up_eq | cut -d ' ' -f2 `
echo "compare_them $UP_SHA1 $i " >> $script
cd $cwd
patches =$ ( echo $patches - 1 | bc )
echo "Compare script generated: $script "

[Jan 16, 2020] Linux Today - 6 Git mistakes you will make -- and how to fix them

Jan 16, 2020 |


<img alt="dcsimg" width="1" height="1" src="//;WT.js=No&amp;;;WT.qs_dlk=XiAF1JT0q-EV9a-UG8XO4gAAABA&"/>
6 Git mistakes you will make -- and how to fix them Jan 15, 2020, 15:00 ( 0 Talkback[s] )
(Other stories by Serdar Yegulalp )

A big reason developers use a source control system like Git is to avoid disasters. If you do something as simple as mistakenly delete a file, or you discover that the changes you've made to a dozen files were all ill-advised, you can undo what you've done with little hassle.

Some Git mistakes are more intimidating and difficult to reverse, even for experienced Git users. But with a little care -- and provided you don't panic -- you can roll back from some of the worst Git disasters known to programmers.


<img alt="dcsimg" width="1" height="1" src="//;WT.js=No&amp;;;WT.qs_dlk=XiAF1JT0q-EV9a-UG8XO4gAAABA&"/>
Complete Story

Related Stories:

[Nov 08, 2019] Five tips for using revision control in operations

Nov 08, 2019 |

Whether you're still using Subversion (SVN), or have moved to a distributed system like Git , revision control has found its place in modern operations infrastructures. If you listen to talks at conferences and see what new companies are doing, it can be easy to assume that everyone is now using revision control, and using it effectively. Unfortunately that's not the case. I routinely interact with organizations who either don't track changes in their infrastructure at all, or are not doing so in an effective manner.

If you're looking for a way to convince your boss to spend the time to set it up, or are simply looking for some tips to improve how use it, the following are five tips for using revision control in operations.

1. Use revision control

A long time ago in a galaxy far, far away, systems administrators would log into servers and make changes to configuration files and restart services manually. Over time we've worked to automate much of this, to the point that members of your operations team may not even have login access to your servers. Everything may be centrally managed through your configuration management system, or other tooling, to automatically manage services. Whatever you're using to handle your configurations and expectations in your infrastructure, you should have a history of changes made to it.

Having a history of changes allows you to:

This first move can often be the hardest thing for an organization to do. You're moving from static configurations, or configuration management files on a filesystem, into a revision control system which changes the process, and often the speed at which changes can be made. Your engineers need to know how to use revision control and get used to the idea that all changes they put into production will be tracked by other people in the organization.

2. Have a plan for what should be put in revision control

This has a few major components: make sure you have multiple repositories in your infrastructure that are targeted at specific services; don't put auto-generated content or binaries into revision control; and make sure you're working securely.

First, you typically want to split up different parts of your services into different repositories. This allows fine-tuned control of who has access to commit to specific service repositories. It also prevents a repository from getting too large, which can complicate the life of your systems adminstrators who are trying to copy it onto their systems.

You may not believe that a repository can get very big, since it's just text files, but you'll have a different perspective when you have been using a repository for five years, and every copy includes every change ever made. Let me show you the system-config repository for the OpenStack Infrastructure project. The first commit to this project was made in July 2011:

elizabeth@r2d2$:~$ time git clone
Cloning into 'system-config'...
remote: Counting objects: 79237, done.
remote: Compressing objects: 100% (37446/37446), done.
remote: Total 79237 (delta 50215), reused 64257 (delta 35955)
Receiving objects: 100% (79237/79237), 10.52 MiB | 2.78 MiB/s, done.
Resolving deltas: 100% (50215/50215), done.
Checking connectivity... done.

real    0m7.600s
user    0m3.344s
sys     0m0.680s

That's over 10M of data for a text-only repository over five years.

Again, yes, text-only. You typically want to avoid stuffing binaries into revision control. You often don't get diffs on these and they just bloat your repository. Find a better way to distribute your binaries. You also don't want your auto-generated files in revision control. Put the configuration file that creates those auto-generated files into revision control, and let your configuration management tooling do its work to auto-generate the files.

Finally, split out all secret data into separate repositories. Organizations can get a considerable amount of benefit from allowing all of their technical staff see their repositories, but you don't necessarily want to expose every private SSH or SSL key to everyone. You may also consider open sourcing some of your tooling some day, so making sure you have no private data in your repository from the beginning will prevent a lot of headaches later on.

3. Make it your canonical place for changes, and deploy from it

Your revision control system must be a central part of your infrastructure. You can't just update it when you remember to, or add it to a checklist of things you should be doing when you make a change in production. Any time someone makes a change, it must be tracked in revision control. If it's not, file a bug and make sure that configuration file is added to revision control in the future.

This also means you should deploy from the configuration tracked in your revision control system. No one should be able to log into a server and make changes without it being put through revision control, except in rare case of an actual emergency where you have a strict process for getting back on track as soon as the emergency has concluded.

4. Use pre-commit scripts

Humans are pretty forgetful, and we sysadmins are routinely distracted and work in a very interrupt-driven environments. Help yourself out and provide your systems administrators with some scripts to remind them what the change message should include.

These reminders may include:

As a bonus, this also documents what reviewers of your change should look for.

Wait, reviewers? That's #5.

5: Hook it all into a code review system

Now that you have a healthy revision control system set up, you have an excellent platform for adding another great tool for systems administrators: code review. This should typically be a peer-review system where your fellow systems administrators of all levels can review changes, and your changes will meet certain agreed-upon criteria for merging. This allows a whole team to take responsibility for a change, and not just the person who came up with it. I like to joke that since changes on our team requires two people to approve, it becomes the fault of three people when something goes wrong, not just one!

Starting out, you don't need to do anything fancy, maybe just have team members submit a merge proposal or pull request and socially make sure someone other than the proposer is the one to merge it. Eventually you can look into more sophisticated code review systems. Most code review tools allow features like inline commenting and discussion-style commenting which provide a non-confrontational way to suggest changes to your colleagues. It's also great for remotely distributed teams who may not be talking face to face about changes, or even awake at the same time.

Bonus: Do tests on every commit!

You have everything in revision control, you have your fellow humans review it, why not add some robots? Start with simple things: Computers are very good at making sure files are alphabetized, humans are not. Computers are really good at making sure syntax is correct (the right number of spaces/tabs?); checking for these things is a waste of time for your brilliant systems engineers. Once you have simple tests in place, start adding unit tests, functional tests, and integration testing so you are confident that your changes won't break in production. This will also help you find where you haven't automated your production infrastructure and solve those problems in your development environment first. Topics Sysadmin About the author Elizabeth K. Joseph - After spending a decade doing Linux systems administration, today Elizabeth K. Joseph works as a developer advocate at IBM focused on IBM Z. As a systems administrator, she worked for a small services provider in Philadelphia before joining HPE where she worked for four years on the geographically distributed OpenStack Infrastructure team. This team runs the fully open source infrastructure for OpenStack development and lead to an interest in other open source projects that have opened up their... More about me

Recommended reading
What you probably didn't know about sudo

Build web apps to automate sysadmin tasks

A guide to human communication for sysadmins

Linux permissions 101

16 essentials for sysadmin superheroes

What does it mean to be a sysadmin hero?

Jenifer Soflous on 25 Jul 2016 Permalink

Great. This article is very useful for my research. I thank you so much for the Author.

Ben Cotton on 25 Jul 2016 Permalink

When I worked on a team of 10-ish, we had our SVN server email all commits to the team. This gave everyone a chance to see what changes were being made in real time, even if it was to a part of the system they didn't normally touch. I personally found it really useful in improving my own work. I'd love to see more articles expanding on each of your points here (hint hint!)

Elizabeth K. Joseph on 27 Jul 2016 Permalink

The email point is a good one! We have optional tuning of email via our code review system, and it's also allowed me to improve my own work and keep a better handle on what's going on with the team when I'm traveling (and thus not hovering over IRC).

Shawn H Corey on 25 Jul 2016 Permalink

For anyone using revision control, not just admins.

1. Add a critic to checkin. A critic not only checks syntax but for common bugs.

2. Add a tidy to checkin. It reformats the code into The Style. That way, developers don't have to waste time making conform to The Style.

[Oct 23, 2019] Ignoring Files and Directories in Git (.gitignore)

Oct 23, 2019 |

Often, when working on a project that uses Git, you'll want to exclude specific files or directories from being pushed to the remote repository.

The .gitignore file specifies what untracked files Git should ignore.

What Files Should be Ignored?

Ignored files are usually platform-specific files or automatically created files from the build systems. Some common examples include:

.gitignore Patterns

A .gitignore file is a plain text file in which each line contains a pattern for files or directories to ignore.

.gitignore uses globbing patterns to match filenames with wildcard characters. If you have files or directories containing a wildcard pattern, you can use a single backslash ( \ ) to escape the character.


Lines starting with a hash mark ( # ) are comments and are ignored. Empty lines can be used to improve the readability of the file and to group related lines of patterns.


The slash symbol ( / ) represents a directory separator. The slash at the beginning of a pattern is relative to the directory where the .gitignore resides.

If the pattern starts with a slash, it matches files and directories only in the repository root.

If the pattern doesn't start with a slash, it matches files and directories in any directory or subdirectory.

If the pattern ends with a slash, it matches only directories. When a directory is ignored, all of its files and subdirectories are also ignored.

Literal File Names

The most straightforward pattern is a literal file name without any special characters.

Pattern Example matches
/access.log access.log
access.log access.log
build/ build
Wildcard Symbols

* - The asterisk symbol matches zero or more characters.

Pattern Example matches
*.log error.log

** - Two adjacent asterisk symbols match any file or zero or more directories. When followed by a slash ( / ), it matches only directories.

Pattern Example matches
logs/** Matches anything inside the logs directory.
**/build var/build
foo/**/bar foo/bar

? - The question mark matches any single character.

Pattern Example matches
access?.log access0.log
foo?? fooab
Square brackets

[...] - Matches any of the characters enclosed in the square brackets. When two characters are separated by a hyphen - it denotes a range of characters. The range includes all characters that are between those two characters. The ranges can be alphabetic or numeric.

If the first character following the [ is an exclamation mark ( ! ), then the pattern matches any character except those from the specified set.

Pattern Example matches
*.[oa] file.o
*.[!oa] file.s
access.[0-2].log access.0.log
file.[a-c].out file.a.out
file.[a-cx-z].out file.a.out
access.[!0-2].log access.3.log
Negating Patterns

A pattern that starts with an exclamation mark ( ! ) negates (re-include) any file that is ignored by the previous pattern. The exception to this rule is to re-include a file if its parent directory is excluded.

Pattern Example matches
error.log or logs/error.log will not be ignored
.gitignore Example

Below is an example of what your .gitignore file could look like:

# Ignore the node_modules directory

# Ignore Logs

# Ignore the build directory

# The file containing environment variables 

# Ignore IDE specific files
Local .gitignore

A local .gitignore file is usually placed in the repository's root directory. However you can create multiple .gitignore files in different subdirectories in your repository. The patterns in the .gitignore files are matched relative to the directory where the file resides.

Patterns defined in the files that reside in lower-level directories (sub-directories) have precedence over those in higher-level directories.

Local .gitignore files are shared with other developers and should contain patterns that are useful for all other users of the repository.

Personal Ignore Rules

Patterns that are specific to your local repository and should not be distributed to other repositories, should be set in the .git/info/exclude file.

For example, you can use this file to ignore generated files from your personal project tools.

Global .gitignore

Git also allows you to create a global .gitignore file, where you can define ignore rules for every Git repository on your local system.

The file can be named anything you like and stored in any location. The most common place to keep this file is the home directory. You'll have to manually create the file and configure Git to use it.

For example, to set ~/.gitignore_global as the global Git ignore file, you would do the following:

  1. Create the file:
    touch ~/.gitignore_global
  2. Add the file to the Git configuration:
    git config --global core.excludesfile ~/.gitignore_global
  3. Open the file with your text editor and add your rules to it.

Global rules are particularly useful for ignoring particular files that you never want to commit, such as files with sensitive information or compiled executables.

Ignoring a Previously Committed Files

The files in your working copy can be either tracked or untracked.

To ignore a file that has been previously committed, you'll need to unstage and remove the file from the index, and then add a rule for the file in .gitignore :

git rm --cached filename

The --cached option tells git not to delete the file from the working tree but only to remove it from the index.

To recursively remove a directory, use the -r option:

git rm --cached filename

If you want to remove the file from both the index and local filesystem, omit the --cached option.

When recursively deleting files, use the -n option that will perform a "dry run" and show you what files will be deleted:

git rm -r -n directory
Debugging .gitignore File

Sometimes it can be challenging to determine why a specific file is being ignored, especially when you're are using multiple .gitignore files or complex patterns. This is where the git check-ignore command with the -v option, which tells git to display details about the matching pattern, comes handy.

For example, to check why the www/yarn.lock file is ignored you would run:

git check-ignore -v www/yarn.lock

The output shows the path to the gitignore file, the number of the matching line, and the actual pattern.

www/.gitignore:31:/yarn.lock       www/yarn.lock

The command also accepts more than one filename as arguments, and the file doesn't have to exist in your working tree.

Displaying All Ignored Files

The git status command with the --ignored option displays a list of all ignored files:

git status --ignored

The .gitignore file allows you to exclude files from being checked into the repository. The file contains globbing patterns that describe which files and directories should be ignored. is an online service that allows you to generate .gitignore files for your operating system, programming language, or IDE.

If you have any questions or feedback, feel free to leave a comment.

[Oct 07, 2019] How to commit to remote git repository

Apr 28, 2012 |

Ahmed ,Apr 28, 2012 at 14:32

I am new to git.
I have done a clone of remote repo as follows
git clone https://[email protected]/repo.git

then I did

git checkout master

made some changes and committed these changes to my local repository like below..

git add .

git commit -m "my changes"

Now I have to push these changes to the remote repository. I am not sure what to do.

Would I do a merge of my repo to remote ? what steps do I need to take ?

I have git bash and git gui

please advise,

zafarkhaja ,Apr 28, 2012 at 14:39

All You have to do is git push origin master , where origin is the default name (alias) of Your remote repository and master is the remote branch You want to push Your changes to.

You may also want to check these out:


Sergey K. ,Apr 28, 2012 at 14:54

You just need to make sure you have the rights to push to the remote repository and do
git push origin master

or simply

git push

haziz ,Apr 28, 2012 at 21:30

git push


git push server_name master

should do the trick, after you have made a commit to your local repository.

Bryan Oakley ,Apr 28, 2012 at 14:34

Have you tried git push ? has a nice section dealing with remote repositories .

You can also get help from the command line using the --help option. For example:

% git push --help
GIT-PUSH(1)                             Git Manual                             GIT-PUSH(1)

       git-push - Update remote refs along with associated objects

       git push [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
                  [--repo=<repository>] [-f | --force] [-v | --verbose] [-u | --set-upstream]
                  [<repository> [<refspec>...]]

[Aug 22, 2019] How to Remove Untracked Files in Git - Linuxize

Aug 22, 2019 |

The files in the Git working directory can be either tracked or untracked.

Tracked files are the ones that have been added and committed and git knows about. Tracked files can be unmodified, modified, or staged. All other files in the working directory are untracked and git is not aware of those files.

Sometimes your git working directory may get cluttered up with unnecessary files that are either auto-generated, leftover from merges or created by mistake. In those situations, you can either add those files in the .gitignore or remove them. If you want to keep your repository nice and clean the better option is to remove the unnecessary files.

This article explains how to remove untracked files in Git.

Removing Untracked Files

The command that allows you to remove untracked files is git clean .

It is always a good idea to backup your repository because once deleted, the files and changes made to them cannot be recovered.

Before running the actual command and removing untracked files and directories use the -n option that will perform a "dry run" and show you what files and directories will be deleted:

git clean -d -n

The output will look something like this:

Would remove content/test/
Would remove content/blog/post/

If some of the files listed above are important, you should either start tracking these files with git add <file> or add them to your .gitignore .

Once you are sure you want to go ahead and delete the untracked files and directories, type:

git clean -d -f

The command will print all successfully deleted files and directories:

Removing content/test/
Removing content/blog/post/

The -d option tells git to remove untracked directories too. If you don't want to delete empty untracked directories, omit -d option.

The -f option stands for force. If not used and the Git configuration variable clean.requireForce is set to true, Git will not delete the files.

If you want to interactively delete the untracked files use the -i option:

git clean -d -i

The output will show the files and directories to be removed, and ask you what to do with those files:

Would remove the following items:
  content/test/   content/blog/post/
*** Commands ***
    1: clean                2: filter by pattern    3: select by numbers
    4: ask each             5: quit                 6: help

Select one of the choices and hit Enter .

me title=

If you want to limit the clean operation to given directories, pass the paths to the directories to be checked for untracked files as arguments to the command. For example, to check for files under the src directory you would run:

git clean -d -n src
Copy Removing Ignored Files

The git clean command also allows removing ignored files and directories.

To remove the all ignored and untracked files use the -x option:

git clean -d -n -x

If you want to remove only the ignored files and directories use the -X option:

git clean -d -n -X

The command above will delete all files and directories listed in your .gitignore and keep the untracked files.


In this tutorial, we have shown you how to delete untracked files and directories in Git. Remember to always dry run the command before actually deleting files.

If you have feedback, leave a comment below.

[Jul 26, 2019] How To Create and List Local and Remote Git Branches

Jul 22, 2019 |
List Git Branches

To list all local Git branches use the git branch or git branch --list command:

git branch
* master

The current branch is highlighted with an asterisk * . In this example that is the master branch.

In Git, local and remote branches are separate objects. If you want to list both local and remote branches pass the -a option:

git branch -a
* master

The -r option will list only the remote branches.

git branch -r
Create a Git Branch

Creating a new branch is nothing more than creating a pointer to a given commit.

To create a new local branch use the git branch command followed by the name of the new branch. For example, to create a new branch named cool-feature you would type:

git branch cool-feature

The command will return no output. If the branch with the same name already exists you will see the following error message:

fatal: A branch named 'cool-feature' already exists.

To start working on the branch and adding commits to it you will need to select the branch using git checkout :

git checkout cool-feature

The output will inform you that the branch is switched:

Switched to branch 'cool-feature'

Instead of creating the branch and then switching to it, you can do that in a single command. When used with the -b option the git checkout command will create the given branch.

git checkout -b cool-feature
Switched to branch 'cool-feature'

From here you can use the standard git add and git commit commands to new commits to the new branch.

To push the new branch on the remote repository use the git push command followed by the remote repo name and branch name:

git push remote-repo cool-feature

In this tutorial, we have shown you how to list and create local and remote Git branches. Branches are a reference to a snapshot of your changes and have a short life cycle.

With the git branch command, you can also Rename and Delete local and remote Git branches.

If you hit a problem or have feedback, leave a comment below.

[Mar 22, 2019] Move your dotfiles to version control

Mar 22, 2019 |

Move your dotfiles to version control Back up or sync your custom configurations across your systems by sharing dotfiles on GitLab or GitHub. 20 Mar 2019 Matthew Broberg (Red Hat) Feed 11 up 4 comments x Get the newsletter

Join the 85,000 open source advocates who receive our giveaway alerts and article roundups.

What a Shell Dotfile Can Do For You , H. "Waldo" Grunenwald goes into excellent detail about the why and how of setting up your dotfiles. Let's dig into the why and how of sharing them. What's a dotfile?

"Dotfiles" is a common term for all the configuration files we have floating around our machines. These files usually start with a . at the beginning of the filename, like .gitconfig , and operating systems often hide them by default. For example, when I use ls -a on MacOS, it shows all the lovely dotfiles that would otherwise not be in the output.

dotfiles on master
➜ ls Rakefile bin misc profiles zsh-custom

dotfiles on master
➜ ls -a
. .gitignore .oh-my-zsh zsh-custom
.. .gitmodules .tmux Rakefile
.gemrc .global_ignore .vimrc bin
.git .gvimrc .zlogin misc
.gitconfig .maid .zshrc profiles

If I take a look at one, .gitconfig , which I use for Git configuration, I see a ton of customization. I have account information, terminal color preferences, and tons of aliases that make my command-line interface feel like mine. Here's a snippet from the [alias] block:

87 # Show the diff between the latest commit and the current state
88 d = !"git diff-index --quiet HEAD -- || clear; git --no-pager diff --patch-with-stat"
90 # `git di $number` shows the diff between the state `$number` revisions ago and the current state
91 di = !"d() { git diff --patch-with-stat HEAD~$1; }; git diff-index --quiet HEAD -- || clear; d"
93 # Pull in remote changes for the current repository and all its submodules
94 p = !"git pull; git submodule foreach git pull origin master"
96 # Checkout a pull request from origin (of a github repository)
97 pr = !"pr() { git fetch origin pull/$1/head:pr-$1; git checkout pr-$1; }; pr"

Since my .gitconfig has over 200 lines of customization, I have no interest in rewriting it on every new computer or system I use, and either does anyone else. This is one reason sharing dotfiles has become more and more popular, especially with the rise of the social coding site GitHub. The canonical article advocating for sharing dotfiles is Zach Holman's Dotfiles Are Meant to Be Forked from 2008. The premise is true to this day: I want to share them, with myself, with those new to dotfiles, and with those who have taught me so much by sharing their customizations.

Sharing dotfiles

Many of us have multiple systems or know hard drives are fickle enough that we want to back up our carefully curated customizations. How do we keep these wonderful files in sync across environments?

My favorite answer is distributed version control, preferably a service that will handle the heavy lifting for me. I regularly use GitHub and continue to enjoy GitLab as I get more experienced with it. Either one is a perfect place to share your information. To set yourself up:

  1. Sign into your preferred Git-based service.
  2. Create a repository called "dotfiles." (Make it public! Sharing is caring.)
  3. Clone it to your local environment. *
  4. Copy your dotfiles into the folder.
  5. Symbolically link (symlink) them back to their target folder (most often $HOME ).
  6. Push them to the remote repository.

* You may need to set up your Git configuration commands to clone the repository. Both GitHub and GitLab will prompt you with the commands to run.


Step 4 above is the crux of this effort and can be a bit tricky. Whether you use a script or do it by hand, the workflow is to symlink from your dotfiles folder to the dotfiles destination so that any updates to your dotfiles are easily pushed to the remote repository. To do this for my .gitconfig file, I would enter:

$ cd dotfiles /
$ ln -nfs .gitconfig $HOME / .gitconfig

The flags added to the symlinking command offer a few additional benefits:

You can review the IEEE and Open Group specification of ln and the version on MacOS 10.14.3 if you want to dig deeper into the available parameters. I had to look up these flags since I pulled them from someone else's dotfiles.

You can also make updating simpler with a little additional code, like the Rakefile I forked from Brad Parbs . Alternatively, you can keep it incredibly simple, as Jeff Geerling does in his dotfiles . He symlinks files using this Ansible playbook . Keeping everything in sync at this point is easy: you can cron job or occasionally git push from your dotfiles folder.

Quick aside: What not to share

Before we move on, it is worth noting what you should not add to a shared dotfile repository -- even if it starts with a dot. Anything that is a security risk, like files in your .ssh/ folder, is not a good choice to share using this method. Be sure to double-check your configuration files before publishing them online and triple-check that no API tokens are in your files.

Where should I start?

If Git is new to you, my article about the terminology and a cheat sheet of my most frequently used commands should help you get going.

There are other incredible resources to help you get started with dotfiles. Years ago, I came across and continue to go back to it for a broader look at what people are doing. There is a lot of tribal knowledge hidden in other people's dotfiles. Take the time to scroll through some and don't be shy about adding them to your own.

I hope this will get you started on the joy of having consistent dotfiles across your computers.

What's your favorite dotfile trick? Add a comment or tweet me @mbbroberg . Topics Git GitHub GitLab About the author

Matthew Broberg - Matt loves working with technology communities to develop products and content that invite delightful engagement. He's a serial podcaster, best known for the Geek Whisperers podcast , is on the board of the Influence Marketing Council , co-maintains the DevRel Collective , and often shares his thoughts on Twitter and GitHub ... More about me

James Phillips on 20 Mar 2019 Permalink

See also the rcm suite for managing dotfiles from a central location. This provides the subdirectory from which you can put your dotfiles into revision control.

Web refs:

Chris Hermansen on 20 Mar 2019 Permalink

An interesting article, Matt, thanks! I was glad to see "what not to share".

While most of my dot files hold no secrets, as you note some do - .ssh, .gnupg, .local/share among others... could be some others. Thinking about this, my dot files are kind of like my sock drawer - plenty of serviceable socks there, not sure I would want to share them! Anyway a neat idea.

Mark Pitman on 21 Mar 2019 Permalink

Instead of linking your dotfiles, give YADM a try:

It wraps the git command and keeps the actual git repository in a subdirectory.

Tom Payne on 21 Mar 2019 Permalink

Check out . It allows you to store secrets securely, too. Disclaimer: I'm the author of chezmoi.

[May 27, 2018] A guide to Git branching by Kedar Vijay Kulkarni

May 27, 2018 |

In this third article on getting started with Git, learn how to add and delete Git branches. 16 May 2018 Kedar Vijay Kulkarni (Red Hat) Feed 24 up 1 comment Get the newsletter

Join the 85,000 open source advocates who receive our giveaway alerts and article roundups.

In my two previous articles in this series, we started using Git and learned how to clone, modify, add, and delete Git files. In this third installment, we'll explore Git branching and why and how it is used.

tree branches

Picture this tree as a Git repository. It has a lot of branches, long and short, stemming from the trunk and stemming from other branches. Let's say the tree's trunk represents a master branch of our repo. I will use master in this article as an alias for "master branch" -- i.e., the central or first branch of a repo. To simplify things, let's assume that the master is a tree trunk and the other branches start from it.

Why we need branches in a Git repo

Programming and development

The main reasons for having branches are: Adding a branch

Let's go back to the previous article in this series and see what branching in our Demo directory looks like. If you haven't yet done so, follow the instructions in that article to clone the repo from GitHub and navigate to Demo . Run the following commands:

git branch
ls -la

The pwd command (which stands for present working directory) reports which directory you're in (so you can check that you're in Demo ), git branch lists all the branches on your computer in the Demo repository, and ls -la lists all the files in the PWD. Now your terminal will look like this:

Terminal output

There's only one file, , on the branch master. (Kindly ignore the other directories and files listed.)

Next, run the following commands:

git status
git checkout -b myBranch
git status

The first command, git status reports you are currently on branch master , and (as you can see in the terminal screenshot below) it is up to date with origin/master , which means all the files you have on your local copy of the branch master are also present on GitHub. There is no difference between the two copies. All commits are identical on both the copies as well.

The next command, git checkout -b myBranch , -b tells Git to create a new branch and name it myBranch , and checkout switches us to the newly created branch. Enter the third line, git status , to verify you are on the new branch you just created.

As you can see below, git status reports you are on branch myBranch and there is nothing to commit. This is because there is neither a new file nor any modification in existing files.

Terminal output

If you want to see a visual representation of branches, run the command gitk . If the computer complains bash: gitk: command not found , then install gitk . (See documentation for your operating system for the install instructions.)

The image below reports what we've done in Demo : Your last commit was Delete file.txt and there were three commits before that. The current commit is noted with a yellow dot, previous commits with blue dots, and the three boxes between the yellow dot and Delete file.txt tell you where each branch is (i.e., what is the last commit on each branch). Since you just created myBranch , it is on the same commit as master and the remote counterpart of master , namely remotes/origin/master . (A big thanks to Peter Savage from Red Hat who made me aware of gitk .)

Gitk output

Now let's create a new file on our branch myBranch and let's observe terminal output. Run the following commands:

echo "Creating a newFile on myBranch" > newFile
cat newFile
git status

The first command, echo , creates a file named newFile , and cat newFile shows what is written in it. git status tells you the current status of our branch myBranch . In the terminal screenshot below, Git reports there is a file called newFile on myBranch and newFile is currently untracked . That means Git has not been told to track any changes that happen to newFile .

Terminal output

The next step is to add, commit, and push newFile to myBranch (go back to the last article in this series for more details).

git add newFile
git commit -m "Adding newFile to myBranch"
git push origin myBranch

In these commands, the branch in the push command is myBranch instead of master . Git is taking newFile , pushing it to your Demo repository in GitHub, and telling you it's created a new branch on GitHub that is identical to your local copy of myBranch . The terminal screenshot below details the run of commands and its output.

Terminal output

If you go to GitHub, you can see there are two branches to pick from in the branch drop-down.

[Mar 13, 2018] git log - View the change history of a file using Git versioning

Mar 13, 2018 |

Richard ,Nov 10, 2008 at 15:42

How can I view the change history of an individual file in Git, complete details with what has changed?

I have got as far as:

git log -- [filename]

which shows me the commit history of the file, but how do I get at the content of each of the file changes?

I'm trying to make the transition from MS SourceSafe and that used to be a simple right-clickshow history .

chris ,May 10, 2010 at 8:58

The above link is no-longer valid. This link is working today: Git Community Bookchris May 10 '10 at 8:58

Claudio Acciaresi ,Aug 24, 2009 at 12:05

For this I'd use:
gitk [filename]

or to follow filename past renames

gitk --follow [filename]

Egon Willighagen ,Apr 6, 2010 at 15:50

But I rather even have a tool that combined the above with 'git blame' allowing me to browse the source of a file as it changes in time... – Egon Willighagen Apr 6 '10 at 15:50

Dan Moulding ,Mar 30, 2011 at 23:17

Unfortunately, this doesn't follow the history of the file past renames. – Dan Moulding Mar 30 '11 at 23:17

Florian Gutmann ,Apr 26, 2011 at 9:05

I was also looking for the history of files that were previously renamed and found this thread first. The solution is to use "git log --follow <filename>" as Phil pointed out here . – Florian Gutmann Apr 26 '11 at 9:05

mikemaccana ,Jul 18, 2011 at 15:17

The author was looking for a command line tool. While gitk comes with GIT, it's neither a command line app nor a particularly good GUI. – mikemaccana Jul 18 '11 at 15:17

hdgarrood ,May 13, 2013 at 14:57

Was he looking for a command line tool? "right click -> show history" certainly doesn't imply it. – hdgarrood May 13 '13 at 14:57

VolkA ,Nov 10, 2008 at 15:56

You can use
git log -p filename

to let git generate the patches for each log entry.


git help log

for more options - it can actually do a lot of nice things :) To get just the diff for a specific commit you can

git show HEAD

or any other revision by identifier. Or use


to browse the changes visually.

Jonas Byström ,Feb 17, 2011 at 17:13

git show HEAD shows all files, do you know how to track an individual file (as Richard was asking for)? – Jonas Byström Feb 17 '11 at 17:13

Marcos Oliveira ,Feb 9, 2012 at 21:44

you use: git show <revision> -- filename, that will show the diffs for that revision, in case exists one. – Marcos Oliveira Feb 9 '12 at 21:44

Raffi Khatchadourian ,May 9, 2012 at 22:29

--stat is also helpful. You can use it together with -p. – Raffi Khatchadourian May 9 '12 at 22:29

Paulo Casaretto ,Feb 27, 2013 at 18:05

This is great. gitk does not behave well when specifying paths that do not exist anymore. I used git log -p -- path . – Paulo Casaretto Feb 27 '13 at 18:05

ghayes ,Jul 21, 2013 at 19:28

Plus gitk looks like it was built by the boogie monster. This is a great answer and is best tailored to the original question. – ghayes Jul 21 '13 at 19:28

Dan Moulding ,Mar 30, 2011 at 23:25

git log --follow -p -- file

This will show the entire history of the file (including history beyond renames and with diffs for each change).

In other words, if the file named bar was once named foo , then git log -p bar (without the --follow option) will only show the file's history up to the point where it was renamed -- it won't show the file's history when it was known as foo . Using git log --follow -p bar will show the file's entire history, including any changes to the file when it was known as foo . The -p option ensures that diffs are included for each change.

Raffi Khatchadourian ,May 9, 2012 at 22:29

--stat is also helpful. You can use it together with -p. – Raffi Khatchadourian May 9 '12 at 22:29

zzeroo ,Sep 6, 2012 at 14:11

Dan's answer is the only real one! git log --follow -p filezzeroo Sep 6 '12 at 14:11

Trevor Boyd Smith ,Sep 11, 2012 at 18:54

I agree this is the REAL answer. (1.) --follow ensures that you see file renames (2.) -p ensures that you see how the file gets changed (3.) it is command line only. – Trevor Boyd Smith Sep 11 '12 at 18:54

Dan Moulding ,May 28, 2015 at 16:10

@Benjohn The -- option tells Git that it has reached the end of the options and that anything that follows -- should be treated as an argument. For git log this only makes any difference if you have a path name that begins with a dash . Say you wanted to know the history of a file that has the unfortunate name "--follow": git log --follow -p -- --followDan Moulding May 28 '15 at 16:10

NHDaly ,May 30, 2015 at 6:03

@Benjohn: Normally, the -- is useful because it can also guard against any revision names that match the filename you've entered, which can actually be scary. For example: If you had both a branch and a file named foo , git log -p foo would show the git log history up to foo , not the history for the file foo . But @DanMoulding is right that since the --follow command only takes a single filename as its argument, this is less necessary since it can't be a revision . I just learned that. Maybe you were right to leave it out of your answer then; I'm not sure. – NHDaly May 30 '15 at 6:03

Falken ,Jun 7, 2012 at 10:23

If you prefer to stay text-based, you may want to use tig .

Quick Install:

Use it to view history on a single file: tig [filename]
Or browse detailed repo history: tig

Similar to gitk but text based. Supports colors in terminal!

Tom McKenzie ,Oct 24, 2012 at 5:28

Excellent text-based tool, great answer. I freaked out when I saw the dependencies for gitk installing on my headless server. Would upvote again A+++ – Tom McKenzie Oct 24 '12 at 5:28

gloriphobia ,Oct 27, 2017 at 12:05

You can look at specific files with tig too, i.e. tig -- path/to/specific/filegloriphobia Oct 27 '17 at 12:05

farktronix ,Nov 11, 2008 at 6:12

git whatchanged -p filename is also equivalent to git log -p filename in this case.

You can also see when a specific line of code inside a file was changed with git blame filename . This will print out a short commit id, the author, timestamp, and complete line of code for every line in the file. This is very useful after you've found a bug and you want to know when it was introduced (or who's fault it was).

rockXrock ,Mar 8, 2013 at 9:45

+1, but filename is not optional in command git blame filename . – rockXrock Mar 8 '13 at 9:45

ciastek ,Mar 18, 2014 at 8:03

"New users are encouraged to use git-log instead. (...) The command is kept primarily for historical reasons;" – ciastek Mar 18 '14 at 8:03

Mark Fox ,Jul 30, 2013 at 18:55

SourceTree users

If you use SourceTree to visualize your repository (it's free and quite good) you can right click a file and select Log Selected

The display (below) is much friendlier than gitk and most the other options listed. Unfortunately (at this time) there is no easy way to launch this view from the command line -- SourceTree's CLI currently just opens repos.

Chris ,Mar 13, 2015 at 13:07

I particularly like the option "Follow renamed files", which allows you to see if a file was renamed or moved. – Chris Mar 13 '15 at 13:07

Sam Lewallen ,Jun 30, 2015 at 6:16

but unless i'm mistaken (please let me know!), one can only compare two versions at a time in the gui? Are there any clients which have an elegant interface for diffing several different versions at once? Possibly with a zoom-out view like in Sublime Text? That would be really useful I think. – Sam Lewallen Jun 30 '15 at 6:16

Mark Fox ,Jun 30, 2015 at 18:47

@SamLewallen If I understand correctly you want to compare three different commits? This sounds similar to a three-way merge (mine, yours, base) -- usually this strategy is used for resolving merge conflicts not necessarily comparing three arbitrary commits. There are many tools that support three way merges but the trick is feeding these tools the specific revisions Fox Jun 30 '15 at 18:47

Sam Lewallen ,Jun 30, 2015 at 19:02

Thanks Mark Fox, that's what I mean. Do you happen to know of any applications that will do that? – Sam Lewallen Jun 30 '15 at 19:02

AechoLiu ,Jan 25 at 6:58

You save my life. You can use gitk to find the SHA1 hash, and then open SourceTree to enter Log Selected.. based on the found SHA1 . – AechoLiu Jan 25 at 6:58

yllohy ,Aug 11, 2010 at 13:01

To show what revision and author last modified each line of a file:
git blame filename

or if you want to use the powerful blame GUI:

git gui blame filename

John Lawrence Aspden ,Dec 5, 2012 at 18:38

Summary of other answers after reading through them and playing a bit:

The usual command line command would be

git log --follow --all -p dir/file.c

But you can also use either gitk (gui) or tig (text-ui) to give much more human-readable ways of looking at it.

gitk --follow --all -p dir/file.c

tig --follow --all -p dir/file.c

Under debian/ubuntu, the install command for these lovely tools is as expected :

sudo apt-get install gitk tig

And I'm currently using:

alias gdf='gitk --follow --all -p'

so that I can just type gdf dir to get a focussed history of everything in subdirectory dir .

PopcornKing ,Feb 25, 2013 at 17:11

I think this is a great answer. Maybe you arent getting voted as well because you answer other ways (IMHO better) to see the changes i.e. via gitk and tig in addition to git. – PopcornKing Feb 25 '13 at 17:11

parasrish ,Aug 16, 2016 at 10:04

Just to add to answer. Locate the path (in git space, up to which exists in repository still). Then use the command stated above "git log --follow --all -p <folder_path/file_path>". There may be the case, that the filde/folder would have been removed over the history, hence locate the maximum path that exists still, and try to fetch its history. works ! – parasrish Aug 16 '16 at 10:04

cregox ,Mar 18, 2017 at 9:44

--all is for all branches, the rest is explained in @Dan's answer – cregox Mar 18 '17 at 9:44

Palesz ,Jun 26, 2013 at 20:12

Add this alias to your .gitconfig:
    lg = log --all --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\n--abbrev-commit --date=relative

And use the command like this:

> git lg
> git lg -- filename

The output will look almost exactly the same as the gitk output. Enjoy.

jmbeck ,Jul 22, 2013 at 14:40

After I ran that lg shortcut, I said (and I quote) "Beautiful!". However, note that the "\n" after "--graph" is an error. – jmbeck Jul 22 '13 at 14:40

Egel ,Mar 27, 2015 at 12:11

Also can be used git lg -p filename - it returns a beautiful diff of searched file. – Egel Mar 27 '15 at 12:11

Jian ,Nov 19, 2012 at 6:25

I wrote git-playback for this exact purpose
pip install git-playback
git playback [filename]

This has the benefit of both displaying the results in the command line (like git log -p ) while also letting you step through each commit using the arrow keys (like gitk ).

George Anderson ,Sep 17, 2010 at 16:50


gitx -- <path/to/filename>

if you're using gitx

Igor Ganapolsky ,Sep 4, 2011 at 16:19

For some reason my gitx opens up blank. – Igor Ganapolsky Sep 4 '11 at 16:19

zdsbs ,Jan 3, 2014 at 18:17

@IgorGanapolsky you have to make sure you're at the root of your git repository – zdsbs Jan 3 '14 at 18:17

Adi Shavit ,Aug 7, 2012 at 13:57

If you want to see the whole history of a file, including on all other branches use:
gitk --all <filename>

lang2 ,Nov 10, 2015 at 11:53

Lately I discovered tig and found it very useful. There are some cases I'd wish it does A or B but most of the time it's rather neat.

For your case, tig <filename> might be what you're looking for.

PhiLho ,Nov 28, 2012 at 15:58

With the excellent Git Extensions , you go to a point in the history where the file still existed (if it have been deleted, otherwise just go to HEAD), switch to the File tree tab, right-click on the file and choose File history .

By default, it follows the file through the renames, and the Blame tab allows to see the name at a given revision.

It has some minor gotchas, like showing fatal: Not a valid object name in the View tab when clicking on the deletion revision, but I can live with that. :-)

Evan Hahn ,May 9, 2013 at 20:39

Worth noting that this is Windows-only. – Evan Hahn May 9 '13 at 20:39

Shmil The Cat ,Aug 9, 2015 at 17:42

@EvanHahn not accurate, via mono one can use GitExtension also on Linux, we use it on ubuntu and quite happy w/ it. see The Cat Aug 9 '15 at 17:42

cori ,Nov 10, 2008 at 15:56

If you're using the git GUI (on Windows) under the Repository menu you can use "Visualize master's History". Highlight a commit in the top pane and a file in the lower right and you'll see the diff for that commit in the lower left.

jmbeck ,Jul 22, 2013 at 14:42

How does this answer the question? – jmbeck Jul 22 '13 at 14:42

cori ,Jul 22, 2013 at 15:34

Well, OP didn't specify command line, and moving from SourceSafe (which is a GUI) it seemed relevant to point out that you could do pretty much the same thing that you can do in VSS in the Git GUI on Windows. – cori Jul 22 '13 at 15:34

Malks ,Dec 1, 2011 at 5:24

The answer I was looking for that wasn't in this thread is to see changes in files that I'd staged for commit. i.e.
git diff --cached

ghayes ,Jul 21, 2013 at 19:47

If you want to include local (unstaged) changes, I often run git diff origin/master to show the complete differences between your local branch and the master branch (which can be updated from remote via git fetch ) – ghayes Jul 21 '13 at 19:47

Brad Koch ,Feb 22, 2014 at 0:04

-1, That's a diff, not a change history. – Brad Koch Feb 22 '14 at 0:04

user3885927 ,Aug 12, 2015 at 21:22

If you use TortoiseGit you should be able to right click on the file and do TortoiseGit --> Show Log . In the window that pops up, make sure:

Noam Manos ,Nov 30, 2015 at 10:54

TortoiseGit (and Eclipse Git as well) somehow misses revisions of the selected file, don't count on it! – Noam Manos Nov 30 '15 at 10:54

user3885927 ,Nov 30, 2015 at 23:20

@NoamManos, I haven't encountered that issue, so I cannot verify if your statement is correct. – user3885927 Nov 30 '15 at 23:20

Noam Manos ,Dec 1, 2015 at 12:06

My mistake, it only happens in Eclipse, but in TortoiseGit you can see all revisions of a file if unchecking "show all project" + checking "all branches" (in case the file was committed on another branch, before it was merged to main branch). I'll update your answer. – Noam Manos Dec 1 '15 at 12:06

Lukasz Czerwinski ,May 20, 2013 at 17:17

git diff -U <filename> give you a unified diff.

It should be colored on red and green. If it's not, run: git config color.ui auto first.

jitendrapurohit ,Aug 13, 2015 at 5:41

You can also try this which lists the commits that has changed a specific part of a file (Implemented in Git 1.8.4).

Result returned would be the list of commits that modified this particular part. Command goes like :

git log --pretty=short -u -L <upperLimit>,<lowerLimit>:<path_to_filename>

where upperLimit is the start_line_number and lowerLimit is the ending_line_number of the file.

Antonín Slejška ,Jun 1, 2016 at 10:44

SmartGit :
  1. In the menu enable to display unchanged files: View / Show unchanged files
  2. Right click the file and select 'Log' or press 'Ctrl-L'

AhHatem ,Jan 2, 2013 at 19:35

If you are using eclipse with the git plugin, it has an excellent comparison view with history. Right click the file and select "compare with"=> "history"

avgvstvs ,Sep 27, 2013 at 13:22

That won't allow you to find a deleted file however. – avgvstvs Sep 27 '13 at 13:22

golimar ,Oct 30, 2012 at 14:35

Comparing two versions of a file is different to Viewing the change history of a file – golimar May 7 '15 at 10:22

[Mar 13, 2018] GIT installation

Mar 13, 2018 |

You might already have Git 1 on your system because it is sometimes installed by default (or another administrator might have installed it). If you have access to the system as a regular user, you can execute the following command to determine whether you have Git installed:

If Git is installed, then the path to the git command is provided, as shown in the preceding command. If it isn't installed, then you either get no output or an error like the following:

[ocs@centos ~]# which git
/usr/bin/which: no git in (/usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/root/bin)

As an administrator on a Red Hat–based system, you could use the rpm command to determine whether the git package has been installed:

[root@centos ~]# rpm -q git

If you are logged in as the root user on a Red Hat–based system, you can use the following command to install Git:

yum install git

Consider installing the software package named git-all . This package includes some additional dependency packages that add more power to Git. Although you might not make use of these features in this introductory book, having them available when you are ready to perform more advanced Git functions will be good.

[Mar 13, 2018] GRV - A Tool for Viewing Git Repositories in Linux Terminal by Aaron Kili

Mar 13, 2018 |

Mar 12, 2018


  1. Go version 1.5 or later should be installed on your system.
  2. libncursesw, libreadline and libcurl.
  3. cmake (to build libgit2).

Tecmint: GRV (Git Repository Viewer) is a free open-source and simple terminal-based interface for viewing git repositories. It provides a way to view and search refs, commits, branches and diffs using Vi/Vim like key bindings. Its behavior and style can be easily customized through a configuration file.

[Oct 31, 2017] Committing part of a file by Tom Ryder

Jan 22, 2012 |

Posted on One of the advantages that Git has over Subversion and CVS is the use of its index as a staging area , which turns out to be a much more flexible model than Subversion. One of the things that always annoyed me about Subversion was that there seemed to be no elegant way to only commit only some of your changes to a particular tracked file. Subversion deals only in files in the working copy, and if you want to commit changes to a file, you have to commit all the changes in that file, even if they're not related.

Where Subversion falls short

As an example, suppose you're making changes to a working copy of a Subversion repository called myproject , and you've made a few changes to the main file, myproject.php ; on one line, you've fixed a bug caused by getting the parameters for htmlentities() in the wrong order. On another, near the head of the file, you've changed a php.ini setting to allow the script to run for a long time. Here's what the output of svn status and svn diff might look like in this case:

$ svn status
M myproject.php

$ svn diff
Index: myproject.php
--- myproject.php (revision 2)
+++ myproject.php (working copy)
@@ -1,5 +1,7 @@
+ini_set("max_execution_time", 300);
  * Open main class.
 @@ -120,7 +122,7 @@
 public function dumpvalue($value)
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");

Under Subversion, unless you move files around, you can't commit only one of these changes; you need to commit both. This isn't really the end of the world, since you could include a commit message describing both things you changed:

$ svn commit -m "Allowed longer runtime, fixed parameter order bug"
Transmitting file data .
Committed revision 3.

But if you're finicky like me, and you'd prefer to think of commits as grouping semantically related changes as much as possible, it would be much better to be able to commit these two changes separately, and this is where Git's use of an index shines.

Git's method

Let's work with the same project again, but this time as a Git repository. We'll make the same changes again, and view the output of git status and git diff :

$ git status
# On branch master
# Changes not staged for commit:
# modified: myproject.php
no changes added to commit

$ git diff
diff --git a/myproject.php b/myproject.php
index 7c20f21..c149190 100644
--- a/myproject.php
+++ b/myproject.php
@@ -1,5 +1,7 @@
+ini_set("max_execution_time", 300);
 * Open main class.
 @@ -120,7 +122,7 @@ class MyProject
 public function dumpvalue($value)
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");

So far, so good. Now when we run git add myproject.php to stage the changes in the index ready for commit, by default it does the same thing Subversion does, putting all of the changes in that file into the staging area. That's probably fine in most cases, but today we want to commit one change, and then the other. The most basic way to do this is using Git's --patch option.

The --patch option can be added to git add , and to some other Git commands concerned with manipulating the index as well, to explicitly prompt you about staging or not staging different sections of the file, that it terms hunks . In our case, the process of including only the first change would look something like this:

$ git add --patch myproject.php
diff --git a/myproject.php b/myproject.php
index 7c20f21..c149190 100644
--- a/myproject.php
+++ b/myproject.php
@@ -1,5 +1,7 @@
+ini_set("max_execution_time", 300);
 * Open main class.
 Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
 @@ -120,7 +122,7 @@ class MyProject
 public function dumpvalue($value)
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n

This done, if you compare the output of git diff --staged and git diff , you'll notice that there are changes staged ready for commit in the file, and also changes that are not staged that we can commit separately later:

$ git diff --staged
diff --git a/myproject.php b/myproject.php
index 7c20f21..4bb2362 100644
--- a/myproject.php
+++ b/myproject.php
@@ -1,5 +1,7 @@
+ini_set("max_execution_time", 300);
 * Open main class.

$ git diff
diff --git a/myproject.php b/myproject.php
index 4bb2362..c149190 100644
--- a/myproject.php
+++ b/myproject.php
@@ -122,7 +122,7 @@ class MyProject
 public function dumpvalue($value)
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");

So your staging area is all ready with just that one change in it, and all you need to do is type git commit with an appropriate message:

$ git commit -m "Allowed longer runtime"
[master 19d9068] Allowed longer runtime
1 files changed, 2 insertions(+), 0 deletions(-)

And the other change you made is still there, waiting to be staged and committed whenever you see fit:

$ git diff
diff --git a/myproject.php b/myproject.php
index 4bb2362..c149190 100644
--- a/myproject.php
+++ b/myproject.php
@@ -122,7 +122,7 @@ class MyProject
 public function dumpvalue($value)
-    print htmlentities($value, "UTF-8", ENT_COMPAT);
+    print htmlentities($value, ENT_COMPAT, "UTF-8");
Other methods

Because Git's index can be manipulated with its lower-level tools very easily, you can treat the differences between your changes and the index like any other diff task. This means more advanced tools like Fugitive for Vim can be even better for seeing changesets in individual files as you stage them for commit. Check out Drew Neil's Vimcast series on Fugitive if you're interested in doing this; it's quite an in-depth series of videos, but very much worth watching if you're a Vim user who wants to understand and use Git to its fullest, and you really value precision and clarity in your commits.

[Oct 31, 2017] Managing dot files with Git

Oct 31, 2017 |

Posted on January 23, 2012 by Tom Ryder Managing configuration files in your home directory on a POSIX system can be a pain when you often work on more than one machine, or when you accidentally remove or delete some useful option or file. It turns out that it's beneficial to manage your configuration files via a version control system, which will allow you both to track the changes you make, and also to easily implement them on other machines. In this case, I'm going to show you how to do it with Git, but in principle there's no reason most of this couldn't work with Subversion or Mercurial. Choosing which files to version

A good way to start is to take a look at the dot files and dot directories you have storing your carefully crafted configurations, and figure out for which of them it would be most important to track changes and to be able to rapidly deploy on remote systems. I use the following criteria:

With these criteria applied, it turns out there are configurations for three programs that I really want to be able to maintain easily across servers: my Vim configuration, my Git configuration, and my GNU Screen configuration.

Creating the repository

To start, we'll create a directory called .dotfiles to hold all our configuration, and initialise it as an empty Git repository.

$ mkdir .dotfiles
$ cd .dotfiles
$ git init

Then we'll copy in the configuration files we want to track, and drop symbolic links to them from where they used to be, so that the applications concerned read them correctly.

$ cd
$ mv .vim .dotfiles/vim
$ mv .vimrc .dotfiles/vimrc
$ mv .screenrc .dotfiles/screenrc
$ mv .gitconfig .dotfiles/gitconfig
$ ln -s .dotfiles/vim .vim
$ ln -s .dotfiles/vimrc .vimrc
$ ln -s .dotfiles/screenrc .screenrc
$ ln -s .dotfiles/gitconfig .gitconfig

Next, we drop into the .dotfiles directory, add everything to the staging area, and commit it:

$ cd .dotfiles
$ git add *
$ git commit -m "First commit of dotfiles."

And that's it, we've now got all four of those files tracked in our local Git repository.

Using a remote repository

With that done, if you want to take the next step of having a central location where you can always get your configuration from any machine with an internet connection, you can set up a repository for your dot files on GitHub, with a free account. The instructions for doing this on GitHub itself are great, so just follow them for your existing repository. On my machine, the results look like this:

$ git remote add origin [email protected]:tejr/dotfiles.git
$ git push -u origin master

Note that I'm pushing using a public key setup, which you can arrange in the SSH Public Keys section of your GitHub account settings.

With this done, if you update your configuration at any time, first add and commit the changes to your local repository, and then all you need to do to update the GitHub version as well is:

$ git push
Cloning onto another machine

Having done this, when you're working with a new machine onto which you'd like to clone your configuration, you clone the repository from GitHub, and delete any existing versions of those files in your home directory to replace them with symbolic links into your repository, like so:

$ git clone [email protected]:tejr/dotfiles.git .dotfiles
$ rm -r .vim .vimrc .screenrc .gitconfig
$ ln -s .dotfiles/vim .vim
$ ln -s .dotfiles/vimrc .vimrc
$ ln -s .dotfiles/screenrc .screenrc
$ ln -s .dotfiles/gitconfig .gitconfig

Finally, if you come back to use this machine later after you've tweaked these configuration files a bit and pushed them to GitHub, you can update them by just running a pull:

$ git pull
Making things easier

This ends up taking a lot of annoyances out of my day, as I know on any machine on which I frequently work, all I need to do is drop to my .dotfiles directory and run a git pull to get the most recent version of my configurations. This ends up being a lot better than manually running scp or rsync calls to keep things up to date. Posted in Git Tagged dot , dotfiles , files , github , versioning

[Jul 16, 2017] How to Install HTTP Git Server With Nginx on Ubuntu 16.04 by Hitesh Jethva

Jul 13, 2017 |

Git is a free and open-source version control system that can be used to track changes of code. Git allows you to create many repositories for the same application and coordinating work on those files among multiple people. It is primarily used for source code management in software development. In this article, we will learn how to install an HTTP Git Server With Nginx on Ubuntu 16.04.

Complete Story

[Mar 20, 2017] Git for Subversion users, Part 1 Getting started

Notable quotes:
"... The Wonderful Monkey Of Wittgenstein ..."
"... local ..."
"... branching ..."
"... automated regression test ..."
Mar 20, 2017 |
Getting started

Git gets demystified for Subversion version control system users

photo- teodor zlatanov Teodor Zlatanov
Published on August 04, 2009 Share this page

Facebook Twitter Linked In Google+ E-mail this page Comments 3

For anyone unfamiliar with free and open source version control systems (VCSs), Subversion has become the standard non-commercial VCS, replacing the old champ, Concurrent Versions System (CVS). CVS is still just fine for limited use, but Subversion's allure is that it requires only a little bit of setup on a Web server and not much beyond that. Subversion does have some issues, which I'll discuss here, but for the most part, it just works.

So, why do we need another one? Git (capital "G"; git is the command-line tool) is in many ways designed to be better than Subversion. It is one of many distributed VCSs. My own first experience with these was with Arch/tla, as well as Mercurial, Bazaar, darcs, and a few others. For many reasons, which I'll discuss as far as they are relevant, Git has become popular and is often considered together with Subversion as the two leading choices for a personal or corporate VCS.

There are two important reasons to be interested in Git if you are a Subversion user.

  • You are looking to move to Git because Subversion is limiting you in some way.
  • You are curious about Git and want to find out how it compares to Subversion.

Well, perhaps there's a third reason: Git is a relatively hot technology you want to include on your resume. I hope that's not your primary goal; learning about Git is one of the most rewarding things a developer can do. Even if you don't use Git now, the concepts and workflow embodied in this distributed VCS are certain to be crucial knowledge for most segments of the IT industry in the next 10 years as the industry undergoes massive changes in scope and geographical distribution.

Finally, though it might not be a compelling reason if you're not a Linux kernel developer, the kernel and a number of other important projects are maintained using Git, so you'll want to be familiar with it if you plan to contribute.

This article is intended for beginning-to-intermediate Subversion users. It requires beginner-level knowledge of Subversion and some general knowledge of version control systems. The information here is mainly for users of UNIX®-like (Linux® and Mac OS X) systems, with a little bit thrown in for Windows® users.

Part 2 of this series will discuss more advanced uses of Git: merging branches, generating diffs, and other common tasks.

Subversion and Git basics

Henceforth, I'll abbreviate "Subversion" as "SVN" to save wear and tear on my U, B, E, R, S, I, and O keys.


You may have heard of git-svn , a tool that lets you use Git against a Subversion repository. Though useful in some situations, being halfway distributed and using a centralized VCS is not the same as switching to a distributed VCS.

So, what's SVN good for? You might already know this, but a VCS is not about files; it's about changes. SVN, running on a central server, adds changes to its repository of data and can give you a snapshot after every change. That snapshot has a revision number; the revision number is very important to SVN and the people who use it. If your change goes in after mine, you're guaranteed to have a higher revision number.

Git has a similar goal-tracking changes-but has no centralized server. This difference is crucial. Where SVN is centralized, Git is distributed; therefore, Git has no way to provide an increasing revision number, because there is no "latest revision." It still has unique revision IDs; they are just not as useful on their own as the SVN revision numbers.

With Git, the crucial action is no longer the commit ; it is the merge . Anyone can clone a repository and commit to the clone. The owner of the repository is given the choice of merging changes back. Alternatively, developers can push changes back to the repository. I'll explore only the latter, authorized-push model.

Keeping a directory under SVN

Let's start with a simple common example: tracking a directory's contents with SVN. You'll need a SVN server and, obviously, a directory of files, as well as an account on that server with commit rights for at least one path. Get started by adding and committing the directory:

Listing 1. Setting up a directory under SVN
1 2 3 4 5 % svn co http://svnserver/...some path here.../top % cd top % cp -r ~/my_directory . % svn add my_directory % svn commit -m 'added directory'

What does this let you do? Now you can get the latest version of any file committed under this directory, delete files, rename them, create new files or directories, commit changes to existing files, and more:

Listing 2. Basic file operations under SVN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # get latest % svn up # what's the status? % svn st # delete files % svn delete # rename files (really a delete + add that keeps history) % svn rename # make directory % svn mkdir # add file % svn add # commit changes (everything above, plus any content changes) % svn commit

I won't examine these commands in detail here, but do keep them in mind. For help on any of these commands, just type svn help COMMAND , and Subversion will show you some basic help; go to the manual for more.

Keeping a directory under Git

I'll follow the same path as I did with the SVN example. As before, I'm assuming you already have a directory full of data.

For the remote server, I'll use the free service, but, of course, you can set up your own server if you like. GitHub is an easy way to play with a remote Git repository. As of this writing, for a free account you're limited to 300MB of data and your repository must be public. I signed up as user "tzz" and created a public repository called "datatest"; feel free to use it. I gave my public SSH key; you should generate one if you don't have one already. You may also want to try the Gitorious server or You'll find a long list of Git hosting services on the Wiki (see Related topics for a link).

One nice thing about GitHub is that it's friendly. It tells you exactly what commands are needed to set up Git and initialize the repository. I'll walk through those with you.

First, you need to install Git, which is different on every platform, and then initialize it. The Git download page (see Related topics ) lists a number of options depending on platform. (On Mac OS X, I used the port install git-core command, but you need to set up MacPorts first. There's also a standalone MacOS X Git installer linked from the Git download page; that will probably work better for most people.)

Once you have it installed, here are the commands I used for a basic setup (pick your own user name and e-mail address, naturally):

Listing 3. Basic Git setup
1 2 % git config --global "Ted Zlatanov" % git config --global "[email protected]"

Already you might see a difference from SVN; there, your user identity was server-side and you were whomever the server said you were. In Git, you can be The Wonderful Monkey Of Wittgenstein if you want (I resisted the temptation).

Next, I set up the data files and initialize my repository with them. (GitHub will also import from a public SVN repository, which can be helpful.)

Listing 4. Directory setup and first commit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # grab some files % cp -rp ~/.gdbinit gdbinit % mkdir fortunes % cp -rp ~/.fortunes.db fortunes/data.txt # initialize % git init # "Initialized empty Git repository in /Users/tzz/datatest/.git/" # add the file and the directory % git add gdbinit fortunes % git commit -m 'initializing' #[master (root-commit) b238ddc] initializing # 2 files changed, 2371 insertions(+), 0 deletions(-) # create mode 100644 fortunes/data.txt # create mode 100644 gdbinit

In the output above, Git is telling us about file modes; 100644 refers to the octal version of the permission bits on those files. You don't need to worry about that, but the 2371 insertions is puzzling. It only changed two files, right? That number actually refers to the number of lines inserted. We didn't delete any, of course.

How about pushing our new changes to the GitHub server? The docs tell us how to add a remote server called "origin" (you can use any name). I should mention here that if you want to learn more about any Git command, for example, git remote , you'd type git remote --help or git help remote . This is typical for command-line tools, and SVN does something very similar.

Listing 5. Push the changes to the remote
1 2 3 4 5 6 7 8 9 10 11 12 # remember the remote repository is called "datatest"? % git remote add origin [email protected]:tzz/datatest.git # push the changes % git push origin master #Warning: Permanently added ',' (RSA) to the list of known hosts. #Counting objects: 5, done. #Delta compression using 2 threads. #Compressing objects: 100% (4/4), done. #Writing objects: 100% (5/5), 29.88 KiB, done. #Total 5 (delta 0), reused 0 (delta 0) #To [email protected]:tzz/datatest.git # * [new branch] master -> master

The warning is from OpenSSH because was not a known host before. Nothing to worry about.

Git messages are, shall we say, thorough . Unlike SVN's messages, which are easy to understand, Git is written for mentats, by mentats. If you're from Frank Herbert's Dune universe and are trained as a human computer, you've probably already written your own version of Git, just because you can. For the rest of us, delta compression and the number of threads used by it are just not very relevant (and they make our heads hurt).

The push was done over SSH, but you can use other protocols, such as HTTP, HTTPS, rsync, and file. See git push --help .

Here is the crucial, most important, basic difference between SVN and Git. SVN's commit says "push this to the central server." Until you commit in SVN, your changes are ethereal. With Git, your commit is local , and you have a local repository no matter what happens on the remote side. You can back out a change, branch, commit to the branch, and so on without any interaction with the remote server. Pushing with Git is effectively a synchronization of your repository's state with the remote server.

All right, so finally let's see the Git log of what just happened:

Listing 6. The Git log
1 2 3 4 5 6 % git log #commit b238ddca99ee582e1a184658405e2a825f0815da #Author: Ted Zlatanov <[email protected]> #Date: ...commit date here... # # initializing

Only the commit is in the log (note the long, random-looking commit ID as opposed to the SVN revision number). There is no mention of the synchronization via git push .

Collaborating through Git

So far we've been using Git as a SVN replacement. Of course, to make it interesting, we have to get multiple users and changesets involved. I'll check out the repository to another machine (running Ubuntu GNU/Linux in this case; you'll need to install git-core and not git ):

Listing 7. Setting up another Git identity and checking out the repository
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 % git config --global "The Other Ted" % git config --global "[email protected]" % git clone [email protected]:tzz/datatest.git #Initialized empty Git repository in /home/tzz/datatest/.git/ #Warning: Permanently added ',' (RSA) to the list of known hosts. #remote: Counting objects: 5, done. #remote: Compressing objects: 100% (4/4), done. #Indexing 5 objects... #remote: Total 5 (delta 0), reused 0 (delta 0) # 100% (5/5) done % ls datatest #fortunes gdbinit % ls -a datatest/.git # . .. branches config description HEAD hooks index info logs objects refs % ls -a datatest/.git/hooks # . .. applypatch-msg commit-msg post-commit post-receive post-update # pre-applypatch pre-commit pre-rebase update

Again, notice the OpenSSH warning indicating we have not done business with GitHub over SSH before from this machine. The git clone command is like a SVN checkout, but instead of getting a synthesized version of the contents (a snapshot as of a particular revision, or the latest revision), you are getting the whole repository.

I included the contents of the datatest/.git directory and the hooks subdirectory under it to show that you really do get everything. Git keeps no secrets by default, unlike SVN, which keeps the repository private by default and only allows access to snapshots.

Incidentally, if you want to enforce some rules on your Git repository, whether on every commit or at other times, the hooks are the place. They are shell scripts, much like the SVN hooks, and have the same "return zero for success, anything else for failure" standard UNIX convention. I won't go into more detail on hooks here, but if your ambition is to use Git in a team, you should definitely read up on them.

All right, so "The Other Ted" is frisky and wants to add a new file in the master branch (roughly equivalent to SVN's TRUNK) and also make a new branch with some changes to the gdbinit file.

Listing 8. Adding a file and making a new branch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 # get a file to add... % cp ~/bin/ . % git add % git commit -m 'adding' #Created commit 6750342: adding # 1 files changed, 1 insertions(+), 0 deletions(-) # create mode 100644 % git log #commit 675034202629e5497ed10b319a9ba42fc72b33e9 #Author: The Other Ted <[email protected]> #Date: ...commit date here... # # adding # #commit b238ddca99ee582e1a184658405e2a825f0815da #Author: Ted Zlatanov <[email protected]> #Date: ...commit date here... # # initializing % git branch empty-gdbinit % git branch # empty-gdbinit #* master % git checkout empty-gdbinit #Switched to branch "empty-gdbinit" % git branch #* empty-gdbinit # master % git add gdbinit % git commit -m 'empty gdbinit' #Created commit 5512d0a: empty gdbinit # 1 files changed, 0 insertions(+), 1005 deletions(-) % git push #updating 'refs/heads/master' # from b238ddca99ee582e1a184658405e2a825f0815da # to 675034202629e5497ed10b319a9ba42fc72b33e9 #Generating pack... #Done counting 4 objects. #Result has 3 objects. #Deltifying 3 objects... # 100% (3/3) done #Writing 3 objects... # 100% (3/3) done #Total 3 (delta 0), reused 0 (delta 0)

That was a long example and I hope you didn't fall asleep; if you did, I hope you dreamt of Git repositories synchronizing in an endless waltz of changesets. (Oh, you'll have those dreams, don't worry.)

First, I added a file (, only one line) and committed it. After the commit, the remote repository at GitHub did not have any idea I had made changes. I then made a new branch called empty-gdbinit and switched to it (I could have done this with git checkout -b empty-gdbinit as well). In that branch, I emptied the gdbinit file and committed that change. Finally, I pushed to the remote server.

If I switch to the master branch, I won't see the empty gdbinit in the logs. So, each branch has its own log, which makes sense.

Listing 9. Looking at logs between branches
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # we are still in the empty-gdbinit branch % git log #commit 5512d0a4327416c499dcb5f72c3f4f6a257d209f #Author: The Other Ted <[email protected]> #Date: ...commit date here... # # empty gdbinit # #commit 675034202629e5497ed10b319a9ba42fc72b33e9 #Author: The Other Ted <[email protected]> #Date: ...commit date here... # # adding # #commit b238ddca99ee582e1a184658405e2a825f0815da #Author: Ted Zlatanov <[email protected]> #Date: ...commit date here... # # initializing % git checkout master #Switched to branch "master" % git log #commit 675034202629e5497ed10b319a9ba42fc72b33e9 #Author: The Other Ted <[email protected]> #Date: ...commit date here... # # adding # #commit b238ddca99ee582e1a184658405e2a825f0815da #Author: Ted Zlatanov <[email protected]> #Date: ...commit date here... # # initializing

When I did the push, Git said, "Hey, look at that, a new file called" on GitHub's servers.

GitHub's Web interface will now display But there's still only one branch on GitHub. Why was the empty-gdbinit branch not synchronized? It's because Git doesn't assume you want to push branches and their changes by default. For that, you need to push everything:

Listing 10. Pushing all
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 % git push -a #updating 'refs/heads/empty-gdbinit' # from 0000000000000000000000000000000000000000 # to 5512d0a4327416c499dcb5f72c3f4f6a257d209f #updating 'refs/remotes/origin/HEAD' # from 0000000000000000000000000000000000000000 # to b238ddca99ee582e1a184658405e2a825f0815da #updating 'refs/remotes/origin/master' # from 0000000000000000000000000000000000000000 # to b238ddca99ee582e1a184658405e2a825f0815da #Generating pack... #Done counting 5 objects. #Result has 3 objects. #Deltifying 3 objects... # 100% (3/3) done #Writing 3 objects... # 100% (3/3) done #Total 3 (delta 1), reused 0 (delta 0)

Again, the mentat interface is here in full glory. But we can figure things out, right? We may not be mentats, but at least we have the common sense to figure that 0000000000000000000000000000000000000000 is some kind of special initial tag. We can also see from the logs in Listing 9 that tag 5512d0a4327416c499dcb5f72c3f4f6a257d209f is the last (and only) commit in the empty-gdbinit branch. The rest might as well be in Aramaic for most users; they just won't care. GitHub will now show the new branch and the changes in it.

You can use git mv and git rm to manage files, renaming and removing them, respectively.


In this article, I explained basic Git concepts and used Git to keep a simple directory's contents under version control, comparing Git with Subversion along the way. I explained branching using a simple example.

In Part 2, I will explore merging, generating diffs, and some of the other Git commands. I strongly encourage you to read the very understandable Git manual or at least go through the tutorial. It's all accessible from the Git home page, so please spend some time exploring it. (See the Related topics below for links.) From the perspective of a SVN user, you don't need much more.

On the other hand, Git is a very rich DVCS; learning more about its features will almost certainly lead to using them to simplify and improve your VCS workflow. Plus, you may even have a dream or two about Git repositories.

This is the second of a two-part series. You should read the Part 1 if you haven't already, as I'll use the same Git and Subversion (SVN) setup, and it will get you used to my sense of humor. Branching and merging in SVN

Easily the greatest source of headaches for version control system (VCS) managers are branching and merging . The vast majority of developers prefer to commit all of their changes in the trunk. As soon as branching and merging come up, developers start to complain, and the VCS manager gets to deal with it.

To be fair to developers, branching and merging are scary operations. The results are not always obvious, and merging can cause problems by undoing other people's work.

SVN manages the trunk well and many developers don't bother with branching. SVN clients before 1.5 were a bit primitive about tracking merges, so if you're used to older SVN clients, you might not know about SVN's svn:mergeinfo property.

There's also a tool called (see Related topics for a link). can track merges without the svn:mergeinfo support and thus works for older SVN clients.

Because of the complexity and variations in SVN's merge support, I won't provide specific examples. Instead, let's just talk about Git's branch merging. You can read the SVN manual referenced in the Related topics section if you are interested.

Branching and merging in Git

If Concurrent Versions System (CVS) is the village idiot when it comes to branching and merging, SVN is the vicar and Git is the mayor. Git was practically designed to support easy branching and merging. This Git feature not only impresses in the demo but is also handy every day.

To give you an example, Git has multiple merge strategies, including one called the octopus strategy, which allows you to merge multiple branches at once. An octopus strategy! Just think about the insanity of attempting to do this kind of merge in CVS or SVN. Git also supports a different kind of merging called rebasing . I won't examine rebasing here, but it is quite helpful for simplifying the repository history, so you might want to look it up.

Before I proceed with the merge example below, you should be familiar with the branch setup in Part 1 . You have HEAD (the current branch, in this case master ) and the empty-gdbinit branch. First, let's merge empty-gdbinit into HEAD , then make a change in HEAD and merge it the other way into empty-gdbinit :

Listing 1. Merging changes from branch to HEAD with Git
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # start clean % git clone [email protected]:tzz/datatest.git # ...clone output... # what branches are available? % git branch -a #* master # origin/HEAD # origin/empty-gdbinit # origin/master # do the merge % git merge origin/empty-gdbinit #Updating 6750342..5512d0a #Fast forward # gdbinit | 1005 --------------------------------------------------------------- # 1 files changed, 0 insertions(+), 1005 deletions(-) # now push the merge to the server % git push #Total 0 (delta 0), reused 0 (delta 0) #To [email protected]:tzz/datatest.git # 6750342..5512d0a master -> master

This is not hard as long as you realize that master has HEAD , and after the merge with the empty-gdbinit branch, the master branch gets pushed to the remote server to synchronize with origin/master . In other words, you merged locally from a remote branch and then pushed the result to another remote branch.

What's important here is to see how Git does not care which branch is authoritative. You can merge from a local branch to another local branch or to a remote branch. The Git server only gets involved for remote operations. In contrast, SVN always requires the SVN server, because with SVN the repository on the server is the only authoritative version.

Of course, Git is a distributed VCS, so none of this is surprising. It was designed to work without central authority. Still, the freedom can be a bit jarring to developers used to CVS and SVN.

Now, properly prepared with all this grand talk, let's make another local branch:

Listing 2. Creating and switching to a release branch on machine A
1 2 3 4 5 6 7 8 9 10 11 # create and switch to the stable branch % git checkout -b release-stable #Switched to a new branch "release-stable" % git branch # master #* release-stable # push the new branch to the origin % git push --all #Total 0 (delta 0), reused 0 (delta 0) #To [email protected]:tzz/datatest.git # * [new branch] release-stable -> release-stable

Now, on a different machine we will remove the gdbinit file from the master branch. Of course, it doesn't have to be a different machine, it can simply be in a different directory, but I'm reusing "The Other Ted" identity on Ubuntu from Part 1 for machine B.

Listing 3. Removing gdbinit from master branch on machine B
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # start clean % git clone [email protected]:tzz/datatest.git # ...clone output... % git rm gdbinit # rm 'gdbinit' # hey, what branch am I in? % git branch #* master # all right, commit my changes % git commit -m "removed gdbinit" #Created commit 259e0fd: removed gdbinit # 1 files changed, 0 insertions(+), 1 deletions(-) # delete mode 100644 gdbinit # and now push the change to the remote branch % git push #updating 'refs/heads/master' # from 5512d0a4327416c499dcb5f72c3f4f6a257d209f # to 259e0fda9a8e9f3b0a4b3019781b99a914891150 #Generating pack... #Done counting 3 objects. #Result has 2 objects. #Deltifying 2 objects... # 100% (2/2) done #Writing 2 objects... # 100% (2/2) done #Total 2 (delta 1), reused 0 (delta 0)

Nothing crazy here (except for "deltifying," which sounds like something you'd do at the gym or something a river might do near a large body of water). But what happens on machine A in the release-stable branch?

Listing 4. Merging removal of gdbinit from master branch to release-stable branch on machine A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # remember, we're in the release-stable branch % git branch # master #* release-stable # what's different vs. the master? % git diff origin/master #diff --git a/gdbinit b/gdbinit #new file mode 100644 #index 0000000..8b13789 #--- /dev/null #+++ b/gdbinit #@@ -0,0 +1 @@ #+ # pull in the changes (removal of gdbinit) % git pull origin master #From [email protected]:tzz/datatest # * branch master -> FETCH_HEAD #Updating 5512d0a..259e0fd #Fast forward # gdbinit | 1 - # 1 files changed, 0 insertions(+), 1 deletions(-) # delete mode 100644 gdbinit # push the changes to the remote server (updating the remote release-stable branch) % git push #Total 0 (delta 0), reused 0 (delta 0) #To [email protected]:tzz/datatest.git # 5512d0a..259e0fd release-stable -> release-stable

The mentat interface, which I referred to in Part 1 , strikes again in the diff. You're supposed to know that /dev/null is a special file that contains nothing, and thus the remote master branch has nothing, whereas the local release-stable branch has the gdbinit file. That's not always obvious to most users.

After all that fun, the pull merges the local branch with origin/master and then the push updates origin/release-stable with the changes. As usual, "delta" is the Git developer's favorite word-one never misses a chance to use it.

Bisecting changes

I won't go into the git bisect command in detail here, because it is quite complicated, but I wanted to mention it because it is a terrific tool. Bisecting changes is really a binary search across the commit log. "Binary" means that the search splits the search interval down the middle and tests the middle each time to decide if the wanted segment is above or below the middle.

The way it works is simple. You tell Git that version A is good and version Z is bad. Git then asks you (or asks an automated script) if the version halfway between A and Z, say Q, is bad. If Q is bad, then the bad commit is between A and Q; otherwise the bad commit is between Q and Z. The process is repeated until the bad commit is found.

It's especially nice that bisecting can be automated with a test script. That makes it possible to write a test for version Z and use it backwards to find when a feature broke, which most developers would call an automated regression test . Those will save you time.

Resolving conflicts

Merge conflicts are inevitable in any VCS and especially so in a distributed VCS such as Git. What happens if two people change a file in conflicting ways in the same branch? Both of the following examples are in the master branch of the datatest repository we've been using so far.

First we make a change to on machine B:

Listing 5. "Does not work" on machine B
1 2 3 4 5 6 7 8 9 10 # we're at time T1 # change the contents % echo "# this script doesn't work" > % git commit -a -m 'does not work' #Created commit e61713b: does not work # 1 files changed, 1 insertions(+), 1 deletions(-) # we're at time T2 now, what's our status? % git status # On branch master #nothing to commit (working directory clean)

Now we make a change to on machine A without awareness of the changes on machine B, and push it:

Listing 6. "Does work" on machine A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # we're at time T2 # change the contents % echo "this script does work" > % git commit -a -m 'does not work' #Created commit e61713b: does not work # 1 files changed, 1 insertions(+), 1 deletions(-) # we're at time T3 now, what's our status? % git status # On branch master # Your branch is ahead of 'origin/master' by 1 commit. # #nothing to commit (working directory clean) % git push #Counting objects: 5, done. #Delta compression using 2 threads. #Compressing objects: 100% (2/2), done. #Writing objects: 100% (3/3), 298 bytes, done. #Total 3 (delta 0), reused 0 (delta 0) #To [email protected]:tzz/datatest.git # 259e0fd..f949703 master -> master

Now, on machine B, we do a git pull and realize things are not so wonderful:

Listing 7. Uh-oh on machine B
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 % git pull #remote: Counting objects: 5, done. #Compressing objects: 100% (2/2), done.) #remote: Total 3 (delta 0), reused 0 (delta 0) #Unpacking 3 objects... # 100% (3/3) done #* refs/remotes/origin/master: fast forward to branch 'master' # of [email protected]:tzz/datatest # 259e0fd..f949703 #Auto-merged #CONFLICT (content): Merge conflict in #Automatic merge failed; fix conflicts and then commit the result. # the next command is optional % echo uh-oh #uh-oh # you can also use "git diff" to see the conflicts % cat #<<<<<<< ## this script doesn't work #======= #this script works #>>>>>>>

This situation is very common in SVN as well. Someone else's changes disagree with your version of a file. Just edit the file and commit:

Listing 8. Fixing and committing on machine B
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # fix before this to contain only "# this script doesn't work"... % echo "# this script doesn't work" > # commit, conflict resolved % git commit -a -m '' #Created commit 05ecdf1: Merge branch 'master' of [email protected]:tzz/datatest % git push #updating 'refs/heads/master' # from f9497037ce14f87ff984c1391b6811507a4dd86c # to 05ecdf164f17cd416f356385ce8f5c491b40bf01 #updating 'refs/remotes/origin/HEAD' # from 5512d0a4327416c499dcb5f72c3f4f6a257d209f # to f9497037ce14f87ff984c1391b6811507a4dd86c #updating 'refs/remotes/origin/master' # from 5512d0a4327416c499dcb5f72c3f4f6a257d209f # to f9497037ce14f87ff984c1391b6811507a4dd86c #Generating pack... #Done counting 8 objects. #Result has 4 objects. #Deltifying 4 objects... # 100% (4/4) done #Writing 4 objects... # 100% (4/4) done #Total 4 (delta 0), reused 0 (delta 0)

That was easy, wasn't it? Let's see what happens on machine A next time it updates.

Listing 9. Fixing and committing on machine B
1 2 3 4 5 6 7 8 9 10 11 12 13 % git pull #remote: Counting objects: 8, done. #remote: Compressing objects: 100% (3/3), done. #remote: Total 4 (delta 0), reused 0 (delta 0) #Unpacking objects: 100% (4/4), done. #From [email protected]:tzz/datatest # f949703..05ecdf1 master -> origin/master #Updating f949703..05ecdf1 #Fast forward # | 2 +- # 1 files changed, 1 insertions(+), 1 deletions(-) % cat ## this script doesn't work

Fast forward means that the local branch caught up with the remote branch automatically, because it contained nothing new to the remote branch. In other words, a fast forward implies no merging was needed; all the local files were no newer than the remote branch's latest push.

Finally, I should mention git revert and git reset , which are very useful for undoing a commit or other changes to the Git tree. There's no room to explain them here, but make sure you know how to use them.


This article opened up the concept of merging, showing what it's like to keep the local and remote branches on two machines and resolving conflicts between them. I also drew attention to the complicated, even arcane Git messages, because compared with SVN, Git is much more verbose and much less intelligible. When you couple this fact with the complex syntax of Git's commands, it can make Git pretty intimidating for most beginners. However, once a few basic concepts are explained, Git gets much easier-even pleasant!

Downloadable resources
Related topics

Related topics

The Git - SVN Crash Course is a handy reference for those already familiar with SVN. Another good tutorial is Flavio Castelli's Howto use Git and SVN together .


[Feb 20, 2017] Git - Branches Atlassian Git Tutorial

Feb 20, 2017 |

git branch

A branch represents an independent line of development. Branches serve as an abstraction for the edit/stage/commit process discussed in Git Basics , the first module of this series. You can think of them as a way to request a brand new working directory, staging area, and project history. New commits are recorded in the history for the current branch, which results in a fork in the history of the project.

The git branch command lets you create, list, rename, and delete branches. It doesn't let you switch between branches or put a forked history back together again. For this reason, git branch is tightly integrated with the git checkout and git merge commands.

git branch

List all of the branches in your repository.

git branch <branch>

Create a new branch called <branch> . This does not check out the new branch.

git branch -d <branch>

Delete the specified branch. This is a "safe" operation in that Git prevents you from deleting the branch if it has unmerged changes.

git branch -D <branch>

Force delete the specified branch, even if it has unmerged changes. This is the command to use if you want to permanently throw away all of the commits associated with a particular line of development.

git branch -m <branch>

Rename the current branch to <branch> .


In Git, branches are a part of your everyday development process. When you want to add a new feature or fix a bug-no matter how big or how small-you spawn a new branch to encapsulate your changes. This makes sure that unstable code is never committed to the main code base, and it gives you the chance to clean up your feature's history before merging it into the main branch.

Git Tutorial: git branch

For example, the diagram above visualizes a repository with two isolated lines of development, one for a little feature, and one for a longer-running feature. By developing them in branches, it's not only possible to work on both of them in parallel, but it also keeps the main master branch free from questionable code.

Branch Tips

The implementation behind Git branches is much more lightweight than SVN's model. Instead of copying files from directory to directory, Git stores a branch as a reference to a commit. In this sense, a branch represents the tip of a series of commits-it's not a container for commits. The history for a branch is extrapolated through the commit relationships.

This has a dramatic impact on Git's merging model. Whereas merges in SVN are done on a file-basis, Git lets you work on the more abstract level of commits. You can actually see merges in the project history as a joining of two independent commit histories.

Example Creating Branches

It's important to understand that branches are just pointers to commits. When you create a branch, all Git needs to do is create a new pointer-it doesn't change the repository in any other way. So, if you start with a repository that looks like this:

Git Tutorial: repository without any branches

Then, you create a branch using the following command:

git branch crazy-experiment

The repository history remains unchanged. All you get is a new pointer to the current commit:

Git Tutorial: Create new branch

Note that this only creates the new branch. To start adding commits to it, you need to select it with git checkout , and then use the standard git add and git commit commands. Please see the git checkout section of this module for more information.

Deleting Branches

Once you've finished working on a branch and have merged it into the main code base, you're free to delete the branch without losing any history:

git branch -d crazy-experiment

However, if the branch hasn't been merged, the above command will output an error message:

error: The branch 'crazy-experiment' is not fully merged. If you are sure you want to delete it, run 'git branch -D crazy-experiment'.

This protects you from losing your reference to those commits, which means you would effectively lose access to that entire line of development. If you really want to delete the branch (e.g., it's a failed experiment), you can use the capital -D flag:

git branch -D crazy-experiment

This deletes the branch regardless of its status and without warnings, so use it judiciously.

git checkout

The git checkout command lets you navigate between the branches created by git branch . Checking out a branch updates the files in the working directory to match the version stored in that branch, and it tells Git to record all new commits on that branch. Think of it as a way to select which line of development you're working on.

In the previous module , we saw how git checkout can be used to view old commits. Checking out branches is similar in that the working directory is updated to match the selected branch/revision; however, new changes are saved in the project history-that is, it's not a read-only operation.

git checkout <existing-branch>

Check out the specified branch, which should have already been created with git branch . This makes <existing-branch> the current branch, and updates the working directory to match.

git checkout -b <new-branch>

Create and check out <new-branch>. The -b option is a convenience flag that tells Git to run git branch <new-branch> before running git checkout <new-branch> . git checkout -b <new-branch> <existing-branch>

Same as the above invocation, but base the new branch off of <existing-branch> instead of the current branch.


git checkout works hand-in-hand with git branch . When you want to start a new feature, you create a branch with git branch , then check it out with git checkout . You can work on multiple features in a single repository by switching between them with git checkout .

Git Tutorial: Switch between multiple features in a single repo with git checkout.

Having a dedicated branch for each new feature is a dramatic shift from the traditional SVN workflow. It makes it ridiculously easy to try new experiments without the fear of destroying existing functionality, and it makes it possible to work on many unrelated features at the same time. In addition, branches also facilitate several collaborative workflows.

Detached HEADs

Now that we've seen the three main uses of git checkout we can talk about that "detached HEAD " we encountered in the previous module.

Remember that the HEAD is Git's way of referring to the current snapshot. Internally, the git checkout command simply updates the HEAD to point to either the specified branch or commit. When it points to a branch, Git doesn't complain, but when you check out a commit, it switches into a "detached HEAD " state.

Git Tutorial: Attached vs Detached Head

This is a warning telling you that everything you're doing is "detached" from the rest of your project's development. If you were to start developing a feature while in a detached HEAD state, there would be no branch allowing you to get back to it. When you inevitably check out another branch (e.g., to merge your feature in), there would be no way to reference your feature:

Git Tutorial: Detached Head state

The point is, your development should always take place on a branch-never on a detached HEAD . This makes sure you always have a reference to your new commits. However, if you're just looking at an old commit, it doesn't really matter if you're in a detached HEAD state or not.


The following example demonstrates the basic Git branching process. When you want to start working on a new feature, you create a dedicated branch and switch into it:

git branch new-feature git checkout new-feature

Then, you can commit new snapshots just like we've seen in previous modules:

# Edit some files git add <file> git commit -m "Started work on a new feature" # Repeat

All of these are recorded in new-feature , which is completely isolated from master . You can add as many commits here as necessary without worrying about what's going on in the rest of your branches. When it's time to get back to "official" code base, simply check out the master branch:

git checkout master

This shows you the state of the repository before you started your feature. From here, you have the option to merge in the completed feature, branch off a brand new, unrelated feature, or do some work with the stable version of your project.

[Feb 20, 2017] Git Behind the Curtain What Happens When You Commit, Branch, and Merge - DZone DevOps

Feb 20, 2017 |
Discover how to optimize your DevOps workflows with our cloud-based automated testing infrastructure, brought to you in partnership with Sauce Labs .

This is the script for a talk that I gave at BarCamp Philly . The talk is a "live committing" exercise, and this post contains all the information needed to follow along, as well as some links to relevant source material and minus my pauses, typos, and attempts at humor.

Object Management

I think that the first thing to understand about Git is that it's not strictly a source control system; it's more like a versioned filesystem that happens to be good at source control. Traditionally, source control systems focused on the evolution of files. For example, RCS (and its successor CVS) maintain a separate file in the repository for each source file. These repository files hold the entire history of the file as a sequence of diffs that allow the tool to reconstruct any version. Subversion applies the idea of diffs to the entire repository, allowing it to track files as they move between directories.

Git takes a different approach. Rather than constructing the state of the repository via diffs, it maintains snapshots of the repository and constructs diffs from those (if you don't believe this, read on). This allows very efficient comparisons between any two points in history but does consume more disk space. I think the key insight is not just that disk is cheap and programmer time expensive, but that real-world software projects don't have a lot of large files, and those files don't experience a lot of churn.

To see Git in action, we'll create a temporary directory, initialize it as a repository, and create a couple of files. I should note here that I'm using bash on Linux; if you're running Windows you're on your own re commands. Anything that starts with " > " is a command that I typed; anything else is the response from the system.

... ... ...

So, what does it mean that git log produces the illusion of a series of merged commits? Consider what happens when you check out one of the commits in the list, for example 2e68e9b9 .

This was a commit that was made on the branch. If you check out that commit and look at the commit log from that point, you'll see that commit 5269b074 no longer appears. It was made on master , in a completely different chain of commits.

In a complex series of merges (say, multiple development branches onto an integration branch and several integration branches onto a feature branch) you can completely lose track of where and why a change was made. If you try to diff your way through the commit history, you'll find that the code changes dramatically between commits, and appears to flip-flop. You're simply seeing the code state on different branches.

[Sep 13, 2016] Saving changes Atlassian Git Tutorial

Notable quotes:
"... entire contents ..."
Sep 13, 2016 |

The git add command adds a change in the working directory to the staging area. It tells Git that you want to include updates to a particular file in the next commit. However, git add doesn't really affect the repository in any significant way-changes are not actually recorded until you run git commit .

In conjunction with these commands, you'll also need git status to view the state of the working directory and the staging area.


git add <file>

Stage all changes in <file> for the next commit.

git add <directory>

Stage all changes in <directory> for the next commit.

git add -p

Begin an interactive staging session that lets you choose portions of a file to add to the next commit. This will present you with a chunk of changes and prompt you for a command. Use y to stage the chunk, n to ignore the chunk, s to split it into smaller chunks, e to manually edit the chunk, and q to exit.


The git add and git commit commands compose the fundamental Git workflow. These are the two commands that every Git user needs to understand, regardless of their team's collaboration model. They are the means to record versions of a project into the repository's history.

Developing a project revolves around the basic edit/stage/commit pattern. First, you edit your files in the working directory. When you're ready to save a copy of the current state of the project, you stage changes with git add . After you're happy with the staged snapshot, you commit it to the project history with git commit .

The git add command should not be confused with svn add , which adds a file to the repository. Instead, git add works on the more abstract level of changes . This means that git add needs to be called every time you alter a file, whereas svn add only needs to be called once for each file. It may sound redundant, but this workflow makes it much easier to keep a project organized.

The Staging Area

The staging area is one of Git's more unique features, and it can take some time to wrap your head around it if you're coming from an SVN (or even a Mercurial) background. It helps to think of it as a buffer between the working directory and the project history.

Instead of committing all of the changes you've made since the last commit, the stage lets you group related changes into highly focused snapshots before actually committing it to the project history. This means you can make all sorts of edits to unrelated files, then go back and split them up into logical commits by adding related changes to the stage and commit them piece-by-piece. As in any revision control system, it's important to create atomic commits so that it's easy to track down bugs and revert changes with minimal impact on the rest of the project.


When you're starting a new project, git add serves the same function as svn import . To create an initial commit of the current directory, use the following two commands:

git add . git commit

Once you've got your project up-and-running, new files can be added by passing the path to git add :

git add git commit

The above commands can also be used to record changes to existing files. Again, Git doesn't differentiate between staging changes in new files vs. changes in files that have already been added to the repository.

git commit

The git commit command commits the staged snapshot to the project history. Committed snapshots can be thought of as "safe" versions of a project-Git will never change them unless you explicity ask it to. Along with git add , this is one of the most important Git commands.

While they share the same name, this command is nothing like svn commit . Snapshots are committed to the local repository, and this requires absolutely no interaction with other Git repositories.


git commit

Commit the staged snapshot. This will launch a text editor prompting you for a commit message. After you've entered a message, save the file and close the editor to create the actual commit. git commit -m "<message>"

Commit the staged snapshot, but instead of launching a text editor, use <message> as the commit message.

git commit -a

Commit a snapshot of all changes in the working directory. This only includes modifications to tracked files (those that have been added with git add at some point in their history).


Snapshots are always committed to the local repository. This is fundamentally different from SVN, wherein the working copy is committed to the central repository. In contrast, Git doesn't force you to interact with the central repository until you're ready. Just as the staging area is a buffer between the working directory and the project history, each developer's local repository is a buffer between their contributions and the central repository.

This changes the basic development model for Git users. Instead of making a change and committing it directly to the central repo, Git developers have the opportunity to accumulate commits in their local repo. This has many advantages over SVN-style collaboration: it makes it easier to split up a feature into atomic commits, keep related commits grouped together, and clean up local history before publishing it to the central repository. It also lets developers work in an isolated environment, deferring integration until they're at a convenient break point.

Snapshots, Not Differences

Aside from the practical distinctions between SVN and Git, their underlying implementation also follow entirely divergent design philosophies. Whereas SVN tracks differences of a file, Git's version control model is based on snapshots . For example, an SVN commit consists of a diff compared to the original file added to the repository. Git, on the other hand, records the entire contents of each file in every commit.

This makes many Git operations much faster than SVN, since a particular version of a file doesn't have to be "assembled" from its diffs-the complete revision of each file is immediately available from Git's internal database.

Git's snapshot model has a far-reaching impact on virtually every aspect of its version control model, affecting everything from its branching and merging tools to its collaboration workflows.


The following example assumes you've edited some content in a file called and are ready to commit it to the project history. First, you need to stage the file with git add , then you can commit the staged snapshot.

git add git commit

This will open a text editor (customizable via git config ) asking for a commit message, along with a list of what's being committed:

# Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # #modified:

Git doesn't require commit messages to follow any specific formatting constraints, but the canonical format is to summarize the entire commit on the first line in less than 50 characters, leave a blank line, then a detailed explanation of what's been changed. For example:

Change the message displayed by - Update the sayHello() function to output the user's name - Change the sayGoodbye() function to a friendlier message

Note that many developers also like to use present tense in their commit messages. This makes them read more like actions on the repository, which makes many of the history-rewriting operations more intuitive.

How do I pull from a Git repository through an HTTP proxy

Stack Overflow
Note: while the use-case described is about using submodules within a project, the same applies to a normal git clone of a repository over HTTP.

I have a project under Git control. I'd like to add a submodule:

git submodule add vendor/plugins/metric_fu

But I get

got 1b0313f016d98e556396c91d08127c59722762d0
got 4c42d44a9221209293e5f3eb7e662a1571b09421
got b0d6414e3ca5c2fb4b95b7712c7edbf7d2becac7
error: Unable to find abc07fcf79aebed56497e3894c6c3c06046f913a under
Cannot obtain needed commit abc07fcf79aebed56497e3894c6c3c06046f913a
while processing commit ee576543b3a0820cc966cc10cc41e6ffb3415658.
fatal: Fetch failed.
Clone of '' into submodule path 'vendor/plugins/metric_fu'

I have my HTTP_PROXY set up:

c:\project> echo %HTTP_PROXY%

I even have a global Git setting for the http proxy:

c:\project> git config --get http.proxy

Has anybody gotten HTTP fetches to consistently work through a proxy? What's really strange is that a few project on GitHub work fine (awesome_nested_set for example), but others consistently fail (rails for example).


You can also set the HTTP proxy that Git uses in global configuration property http.proxy:
C:\> git config --global http.proxy %HTTP_PROXY%

What finally worked was setting the $http_proxy environment variable. I had set $HTTP_PROXY correctly, but git apparently likes the lower-case version better.

There's some great answers on this already. However, I thought I would chip in as some proxy servers require you to authenticate with a user Id and password. Sometimes this can be on a domain.

So, for example if your proxy server configuration is as follows:

Server: myproxyserver
Port: 8080
Username: mydomain\myusername
Password: mypassword

Then, add to your .gitconfig file using the following command:

git config --global http.proxy http://mydomain\\myusername:mypassword@myproxyserver:8080

Don't worry about https. As long as the specified proxy server supports http, and https, then one entry in the config file will suffice.

You can then verify that the command added the entry to your .gitconfig file successfully by doing cat .gitconfig:

At the end of the file you will see an entry as follows:

    proxy = http://mydomain\\myusername:mypassword@myproxyserver:8080

Recommended Links

Google matched content

Softpanorama Recommended

Top articles


Top articles


Git (software) - Wikipedia, the free encyclopedia

Free Books

Git - Book

The entire Pro Git book written by Scott Chacon and Ben Straub is available to read online for free. Dead tree versions are available on


Youtube videos

Version Control with Git by Michael Koby

For more videos please go to

Git Branching and Merging Strategies - YouTube

GitHub Tutorials



Groupthink : Two Party System as Polyarchy : Corruption of Regulators : Bureaucracies : Understanding Micromanagers and Control Freaks : Toxic Managers :   Harvard Mafia : Diplomatic Communication : Surviving a Bad Performance Review : Insufficient Retirement Funds as Immanent Problem of Neoliberal Regime : PseudoScience : Who Rules America : Neoliberalism  : The Iron Law of Oligarchy : Libertarian Philosophy


War and Peace : Skeptical Finance : John Kenneth Galbraith :Talleyrand : Oscar Wilde : Otto Von Bismarck : Keynes : George Carlin : Skeptics : Propaganda  : SE quotes : Language Design and Programming Quotes : Random IT-related quotesSomerset Maugham : Marcus Aurelius : Kurt Vonnegut : Eric Hoffer : Winston Churchill : Napoleon Bonaparte : Ambrose BierceBernard Shaw : Mark Twain Quotes


Vol 25, No.12 (December, 2013) Rational Fools vs. Efficient Crooks The efficient markets hypothesis : Political Skeptic Bulletin, 2013 : Unemployment Bulletin, 2010 :  Vol 23, No.10 (October, 2011) An observation about corporate security departments : Slightly Skeptical Euromaydan Chronicles, June 2014 : Greenspan legacy bulletin, 2008 : Vol 25, No.10 (October, 2013) Cryptolocker Trojan (Win32/Crilock.A) : Vol 25, No.08 (August, 2013) Cloud providers as intelligence collection hubs : Financial Humor Bulletin, 2010 : Inequality Bulletin, 2009 : Financial Humor Bulletin, 2008 : Copyleft Problems Bulletin, 2004 : Financial Humor Bulletin, 2011 : Energy Bulletin, 2010 : Malware Protection Bulletin, 2010 : Vol 26, No.1 (January, 2013) Object-Oriented Cult : Political Skeptic Bulletin, 2011 : Vol 23, No.11 (November, 2011) Softpanorama classification of sysadmin horror stories : Vol 25, No.05 (May, 2013) Corporate bullshit as a communication method  : Vol 25, No.06 (June, 2013) A Note on the Relationship of Brooks Law and Conway Law


Fifty glorious years (1950-2000): the triumph of the US computer engineering : Donald Knuth : TAoCP and its Influence of Computer Science : Richard Stallman : Linus Torvalds  : Larry Wall  : John K. Ousterhout : CTSS : Multix OS Unix History : Unix shell history : VI editor : History of pipes concept : Solaris : MS DOSProgramming Languages History : PL/1 : Simula 67 : C : History of GCC developmentScripting Languages : Perl history   : OS History : Mail : DNS : SSH : CPU Instruction Sets : SPARC systems 1987-2006 : Norton Commander : Norton Utilities : Norton Ghost : Frontpage history : Malware Defense History : GNU Screen : OSS early history

Classic books:

The Peter Principle : Parkinson Law : 1984 : The Mythical Man-MonthHow to Solve It by George Polya : The Art of Computer Programming : The Elements of Programming Style : The Unix Hater’s Handbook : The Jargon file : The True Believer : Programming Pearls : The Good Soldier Svejk : The Power Elite

Most popular humor pages:

Manifest of the Softpanorama IT Slacker Society : Ten Commandments of the IT Slackers Society : Computer Humor Collection : BSD Logo Story : The Cuckoo's Egg : IT Slang : C++ Humor : ARE YOU A BBS ADDICT? : The Perl Purity Test : Object oriented programmers of all nations : Financial Humor : Financial Humor Bulletin, 2008 : Financial Humor Bulletin, 2010 : The Most Comprehensive Collection of Editor-related Humor : Programming Language Humor : Goldman Sachs related humor : Greenspan humor : C Humor : Scripting Humor : Real Programmers Humor : Web Humor : GPL-related Humor : OFM Humor : Politically Incorrect Humor : IDS Humor : "Linux Sucks" Humor : Russian Musical Humor : Best Russian Programmer Humor : Microsoft plans to buy Catholic Church : Richard Stallman Related Humor : Admin Humor : Perl-related Humor : Linus Torvalds Related humor : PseudoScience Related Humor : Networking Humor : Shell Humor : Financial Humor Bulletin, 2011 : Financial Humor Bulletin, 2012 : Financial Humor Bulletin, 2013 : Java Humor : Software Engineering Humor : Sun Solaris Related Humor : Education Humor : IBM Humor : Assembler-related Humor : VIM Humor : Computer Viruses Humor : Bright tomorrow is rescheduled to a day after tomorrow : Classic Computer Humor

The Last but not Least Technology is dominated by two types of people: those who understand what they do not manage and those who manage what they do not understand ~Archibald Putt. Ph.D

Copyright © 1996-2021 by Softpanorama Society. was initially created as a service to the (now defunct) UN Sustainable Development Networking Programme (SDNP) without any remuneration. This document is an industrial compilation designed and created exclusively for educational use and is distributed under the Softpanorama Content License. Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.

FAIR USE NOTICE This site contains copyrighted material the use of which has not always been specifically authorized by the copyright owner. We are making such material available to advance understanding of computer science, IT technology, economic, scientific, and social issues. We believe this constitutes a 'fair use' of any such copyrighted material as provided by section 107 of the US Copyright Law according to which such material can be distributed without profit exclusively for research and educational purposes.

This is a Spartan WHYFF (We Help You For Free) site written by people for whom English is not a native language. Grammar and spelling errors should be expected. The site contain some broken links as it develops like a living tree...

You can use PayPal to to buy a cup of coffee for authors of this site


The statements, views and opinions presented on this web page are those of the author (or referenced source) and are not endorsed by, nor do they necessarily reflect, the opinions of the Softpanorama society. We do not warrant the correctness of the information provided or its fitness for any purpose. The site uses AdSense so you need to be aware of Google privacy policy. You you do not want to be tracked by Google please disable Javascript for this site. This site is perfectly usable without Javascript.

Last modified: May 05, 2021