Git vs SVN – Understanding Branch Differences

gitgithub

I am a user of SVN and now I am learning Git.

In SVN I usually checkout on my local machine a repo, which includes all branches in my project and I used to select the folder for my branch I am interested to and work there.

I see a difference using Git.

Currently I am cloning a repo and clone a specific branch using gitk.

The project folder contains only the content for that branch and I cannot see all branches as in SVN, which is a little confusing for me.

I cannot find an easy way to see all branches in my local repository using Git.

  • I would like to know if the Git process I described is "standard" and some how correct or I am missing something.

  • Also I would like to know how to handle a process where I need to work on two branches at the same time in case, for example, I need to make an hotfix on master but keep the content of another branch too.

  • What is a recommend name conventions to make the folders which include the branch cloned from the repo in Git, example myproject-branchname?

Best Answer

I am a user of SVN and now I am learning GIT.

Welcome to the gang!

SVN Re-education

In SVN I usually [...]

Hold on for a moment. While CVS and SVN and other traditional (i.e. centralized) version control system fulfill (mostly) the same purpose as modern (i.e. distributed) version control systems like mercurial and Git, you'll be much better off learning Git from the ground up instead of trying to transfer your SVN workflow to Git.

http://hginit.com (view on archive.org) by Joel Spolsky (one of the very founders of Stack Exchange) is a tutorial for mercurial, not Git, but it's zero-th chapter, "Subversion Re-education" is useful for people switching away from SVN to any distributed version control system, as it tells you what SVN concepts you have to (temporarily) un-learn (or to stash away, as Git users might say) to be able wrap your head around the distributed version control system concepts and the idiomatic workflows established to work with them.

So you can read that zero-th chapter and mostly just replace the word "mercurial" with "Git" and thereby properly prepare yourself and your mind for Git.

The fine print

(You might skip this for now.) While mercurial and Git are much more similar to each other than to SVN, there are some conceptual differences between them, so some of the statements and advice in "Subversion Re-education" will become technically wrong when replacing "mercurial" with "Git":

  • While mercurial internally tracks and stores changesets, Git internally tracks and stores revisions (i.e. states of the content of a directory tree), just like Subversion does. But other than Subversion Git performs merges by looking at the differences between each involved branch respectively and a common ancestor revision (a true 3-point-merge), so the result is much the same as for mercurial: Merging is much easier and less error-prone than in SVN.
  • While you can branch in Git by cloning the repository (as is customary in mercurial), it's much more common to create a new branch within a Git repository. (That's because Git branches are simply (moving) pointers to revisions, whereas mercurial branches are permanent labels applied to every revision. These permanent labels are usually unwanted, so mercurial workflows usually work by cloning the complete repository for diverging development.)

In SVN, everything is a directory (but you shouldn't necessarily treat it as such)

But I've been interrupting you. You were saying?

In SVN I usually checkout on my local machine a repo, which includes all branches in my project and I used to select the folder for my branch I am interested to and work there.

If, by that, you mean you've checked out the SVN repository's root folder (or any other folder corresponding to more than to trunk or to a single branch, e.g. in the conventional SVN repo layout the branches folder containing all non-trunk branches) then I dare say you've probably used SVN wrong(ish), or at least abused a bit the fact that trunk, branches and tags are all folded into a single (though hierarchical) namespace together with directories within the revisions/codebase.

While it might be tempting to change several SVN branches in parallel, that is AFAIK not how SVN is intended to be used. (Though I'm unsure about what specific downsides working like that might have.)

In Git, only directories are directories

Every clone of a Git repository is itself a Git repository: By default, it gets a full copy of the origin repository's history, including all revisions, branches and tags. But it will keep all that our of your way: Git stores it in a file-based database of sorts, located in the repository's root folder's hidden .git/ subfolder.

But what are the non-hidden files you see in the repository folder?

When you git clone a repository, Git does several things. Roughly:

  1. It creates the target folder, and in it, the .git/ subfolder with the "database"
  2. It transfers the references (tags, branches etc.) of the origin repository's database and makes a copy of them in the new database, giving them a slighly modified name that marks them as "remote" references.
  3. It transfers all the revisions (i.e. file tree states) that these references point to, as well all revisions that these revisions point to directly or transitively (their parents and ancestors) to the new database and stores them, so that the new remote references actually point to something that's available in the new repository.
  4. It creates a local branch tracking the remote revision corresponding to the origin repository's default branch (usually master).
  5. It checks out that local branch.

That last step means that Git looks up the revision that this branch points at, and unpacks the file-tree stored there (the database uses some means of compression and de-duplication) into the repository's root folder. That root folder and all its subfolders (excluding the special .git subfolder) are known as your repository's "working copy". That's where you interact with the content of the currently checked-out revision/branch. They're just normal folders and files. However, to interact with the repository per-se (the "database" of revisions and references) you use git commands.

Seeing Git branches and interacting with Git branches

Currently I am cloning a repo and clone a specific branch using gitk.

The version of gitk I got cannot clone repositories. It can only view repo history and create branches and check out branches. Also, there's no "cloning" a branch. You can only clone repositories.

Did you mean you clone a repo using git clone ... and then, using gitk, check out a branch?

The project folder contains only the content for that branch and I cannot see all branches as in SVN, which is a little confusing for me.

[...]

  • I would like to know if the git process I described is "standard" and some how correct [...]

Yes, that is pretty standard:

  • Clone a repository using git clone ...
  • Check out the branch you want to work on with git checkout ... or using a graphical tool like gikt

I cannot find an easy way to see all branches in my local repository using GIT.

  • [...] or I am missing smt.

Maybe:

  • you can list local branches with git branch
  • you can list remote branches with git branch -r or all branches with git branch -a
  • you can use gitk to view the complete history (all branches tags etc. that your local repo knows about) by invoking it with

    gitk --all
    

How to work with multiple branches in parallel

  • Also I would like to know how to handle a process where I need to work on two branches at the same time in case, for example, I need to make an hotfix on master but keep the content of another branch too.

There's different scenarios here:

A new (yet to be created) change needs to be applied to multiple branches

Use this workflow:

  1. Create a new branch c from a revision that already is in the ancestry of all these branches (e.g. the revision that introduced the bug when the change will be a bugfix) or from a revision that (including all its ancestors) is acceptable to be introduced in all these branches.
  2. Make and commit the change on that new branch c.
  3. For each branch b that needs the change:

    1. Check out b:

      git checkout b
      
    2. Merge c into b:

      git merge c
      
  4. Remove branch c:

    git branch --delete c
    

An existing change is needed on a different branch

(... but without the other changes made on the where that change resides)

  1. Check out the branch where the change is needed
  2. Cherry-pick the revision(s) making the change, in order

On one branch a, you want to change one or some files to the exact state they have on a different branch b

  1. Check out a
  2. Get the file contents from branch b:

    git checkout b -- path/to/a/file.txt path/to/another/file.cpp or/even/a/complete/directory/ ...
    

    (Other than git checkout without passing paths, this won't switch to branch b, only get the requested file contents from there. These files might or might not already exist on a. If they do, they're overwritten with their content on b.)

While working on one branch, you want to look at how things are on another branch

Check out the branch you want to work on.

Then, for looking at the other branch,

  • either use graphical tools that allow you to view the contents of not currently checked out revisions (e.g. in gitk try to switch the radio buttons from "patch" to "tree")
  • or clone the repository to a temporary directory and check out the other branch there
  • or use git worktree to create a separate working directory of the same repository (i.e. also using the database in .git/ directory of your current local repository) where you can check out that other branch
Related Topic