50

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?

9
  • 8
    Interesting - usually with SVN you would only checkout the trunk or branch you're working on, but I understand what you mean.
    – HorusKol
    Mar 19, 2017 at 9:49
  • 22
    You had broken workflow in SVN, now you try to mirror it into Git (while even good SVN-workflow applicable only partially for Git). Best solution - forget all SVN-habits and start in Git from scratch Mar 19, 2017 at 12:39
  • 6
    git clone is more like svnadmin hotcopy or svnrdump+svnadmin load than it is like svn checkout. With git, you don't request bits and pieces from the repository; you copy the entire repository and work with it locally, pushing changes back to the "source" repository when and if you feel like it. Git and Subversion use two entirely different models for source control.
    – chepner
    Mar 19, 2017 at 12:43
  • 6
    @LazyBadger it isn't broken workflow if it facilitate development and provided value. There isn't one right way to use branches.
    – dietbuddha
    Mar 19, 2017 at 19:13
  • 2
    You accepted an answer that was postet 10 minutes after the question, on the same day. This means that it is likely that people who scan the git tagged questions skip this one, thinking it is done already. Just saying. Give it 24 hours before accepting, to increase the chance to generate more diverse answers. You might want to re-evaluate the answery you got and check if you want to switch...
    – AnoE
    Mar 20, 2017 at 12:46

5 Answers 5

74

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 gitk

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

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

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
7
  • For the last workflow, stash is ideal.
    – CAD97
    Mar 19, 2017 at 17:58
  • @CAD97 not if you want to continue working on the first branch, while looking at the second for reference in parallel. git stash is good to move unfinished work out of the way, e.g. to switch branches and later come back.
    – das-g
    Mar 19, 2017 at 21:47
  • 1
    "mercurial workflows usually work by cloning the complete repository for diverging development" - Eh, I've heard of this, but most people who want Git branches just use bookmarks instead of cloning the whole repo. It's much easier.
    – Kevin
    Mar 19, 2017 at 22:27
  • @Kevin That might be. It's some time ago I've last used mercurial, so maybe it was different back then. (Or maybe it was just the workflows of the community I used it with that were like this.)
    – das-g
    Mar 19, 2017 at 23:40
  • Maybe the Hg Init text "[...] because you really should have been branching the Mercurial way, by cloning repositories [...]" should be updated in that regard, too.
    – das-g
    Mar 19, 2017 at 23:45
31

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.

Unless you check out the directory above trunk, in Subversion you generally do not have all branches available locally. In Git all content (commits, messages, branches etc.) are always cloned to every copy.

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

Not possible, see above. You can git checkout branch-name, but you can't git clone something branch-name. In Subversion, branches are folders. In Git, branches are pointers to a commit.

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

Run git branch. By default it shows only local branches. Use git branch --remote to see remote branches.

10
  • 4
    Hm. "By default it shows only local branches." sounds like there are other types of branches that are not local. But you also say "In Git all content (commits, messages, branches etc.) are always cloned to every copy.". I find that a bit confusing, because if all branches are cloned to your copy, what are those mysterious non-local branches? Maybe it's too complicated to answer in a comment field.
    – pipe
    Mar 19, 2017 at 11:03
  • 2
    @pipe When you commit changes you do this by creating a local commit, which changes the commit the branch is pointing to. If you want others to get these changes you need to push these changes to the remote repository. This results in the remote branch pointing to this new commit. Now everyone else can pull these changes from there and as a result everyone would eventually get a full copy of the repository
    – Frozn
    Mar 19, 2017 at 11:15
  • 11
    @pipe First, you can in fact clone only a branch using --single-branch (even only the tip with --depth 1). The difference between local and remote branches known to git is merely a kind of label. Remote branches are prefixed with the name of the remote source (often origin). Local branches do not have that prefix. But in the end, the data is available locally (unless you did --single-branch or something similar). When you run git checkout $branchname with a branch for which no local but a remote branch exists, git automatically sets up a local branch "tracking" the remote. Mar 19, 2017 at 14:19
  • 1
    "Unless you check out the directory above trunk" - My experience is that this is pretty common, since it makes merging a lot easier. (Though we use a single "live" branch instead of version tags, so it usually amounts to only 2-3 copies of the code)
    – Izkata
    Mar 19, 2017 at 20:23
  • 1
    @Izkata "it makes merging a lot easier", does it?
    – HorusKol
    Mar 19, 2017 at 21:11
4

As this is tagged with I hope that my lack of SVN knowledge is neglectable.

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

You are cloning the whole remote repository not only a specific branch.

The repository is best imagined as a database, so you are making a clone of the current state of the remote database. But after that, you are working on your own copy of that database; if you commit, you alter your local database.

The fetch command is used to keep the local database in sync with the remote one.

Usually from that local database, you checkout a branch to work on. This is nothing else like an internal marker for git, where your current work began.

Say, you are working on a simple repository, where there is no branch besides master, you could take a look into the .git folder to unveil the "magic":

Assume your last commit (on master) was 182e8220b404437b9e43eb78149d31af79040c66, you will find exactly that under cat .git/refs/heads/master.

From that you branch off a new branch git checkout -b mybranch, you will find the exact same pointer in the file cat .git/refs/heads/mybranch.

Branches are nothing more than "pointers". The "working" marker is called a HEAD.

If you want to know where your HEAD is at:

cat .git/HEAD which says e.g. ref: refs/heads/mybranch, which in turn points (cat .git/refs/heads/mybranch) to a commit hash 78a8a6eb6f82eae21b156b68d553dd143c6d3e6f

The actual commits are stored under the objects folder (the how is a topic of its own).

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.

Don't confuse the working directory with the "git-database" as a whole. As I said above, your working directory is only a snapshot of (maybe) a subset.

Say you have different branches, your working directory is dedicated to working on that branch only (although you could put the work from there elsewhere).

Usually, if you want to see, what branches are defined for the project, you have the possibility of

  • git branch for local branches
  • git branch --remote for remote branches
  • git branch -a for both

(or git branch -v)

Since git is a distributed version control system it is not only possible, but encouraged, to make different branches locally / remote.

My typical workflow is:

  • branch a feature branch off
  • branch a WIP (work in progress) branch from that
  • work like however you want - even if you commit after a single line; it doesn't matter

When the feature is complete:

  • squash/rework the WIP branch (with interactive rebasing) = make a single commit from that
  • merge the WIP branch into the feature branch and offer that (if you work with github that offer would be called a "pull request") to integrate into the stable (master) branch.

Also I would like to know how to handle a process where I need to wprl 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.

It depends on how your project is structured:

Say you have a stable master. And features are only developed off from that stable branch - so it is usually behind a feature branch. Then you would have a last commit on master that would be the root of the feature branch.

Then you would make a commit on the master branch and could decide, whether to merge both branches together or to rebase (which is a kind of merge for users with advanced needs so to say).

Or you are always able to make changes on branches ( e.g. master) and cherrypick them on to other branches.

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

It is up to you.

Typically, you end up with the repositories name.

But there are occasions, when this is not wanted:

e.g. you clone oh-my-zsh with git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh Here .oh-my-zsh is explicitely named as the target.

2

Git clone actually clones the whole repository, but sets your working directory to the default (usually master).

You can see other branches using git branch to see local branches or git branch -r to see remote ones and then git checkout to switch to an existing branch or create a new one based on the current branch.

You should read the git documentation for more details, and Atlassian have some good articles on it, too.

0

Branches in git and svn are fundamentally different things.

In svn a branch (or a tag) is a directory in the repo.

In git a branch (or a tag) is a pointer to a commit.

With svn you can if you wish checkout the root of the repo. This means you have every branch and tag checked out at once. Afaict this is not the normal way to use svn.

A git branch is not a directory, so there is no equivilent to "checking out the root of the repo". There is some support for multiple working trees so I guess you could cobble together a script to checkout every branch at the same time if you really wanted but it would be a rather unusual think to so.

Furthermore with SVN there is exactly one repo. With git every developer has their own repo. This means developers can work offline but it also means that different developers may have a different idea of what is on each branch.

By virtue of being directories and by virtue of svn's simple linear history model svn branches have robust histories. If you want to know what was on branch x on date y you can easilly ask that question.

Git branches on the other hand don't really have histories. There is the "reflog" but it is intended more as a disaster recovery mechanism than a long term history. In particular the reflog can't be read remotely and is disabled by default for bare repos.

Commits of course have histories but those histories are not sufficient to answer the question of "what was on branch x of repo y on date z".

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

You can list all local branches by typing "git branch".

You can list all branches both local and remote by using "git branch -a"

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.

A couple of options.

You can commit your changes on the "other branch", switch to master do your work there and then switch back.

You can also create an extra working tree. Google "git-worktree" for the details on the syntax.

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

I don't think there is an established convention. Having multiple working trees checked out at once is the exception not the rule.

1
  • 2
    this answer looks incomplete, why?
    – gnat
    Mar 20, 2017 at 12:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.