19

Bounty short description

Is there a portable way to use a single repository w/ multiple checkouts? As an alternative to having multiple clones where there is just too much overhead (pushing/pulling/syncing...) and the risk of overwriting the .git/objects hard links (which does not even work on Windows).


New to git and curious to hear thoughts from experienced git users.

Is there a conceptual reason why git only works with ONE branch at a time? It seems absolutely impractical having to switch back and forward between branches, when most of the time I need to work on at least two different branches at the same time, e.g. building, running in parallel a.s.o.

Ok, so maybe a developer does not really need to work on two branches exactly at the same time. But checking out another branch does not automatically carry ignored stuff like build output files. So a new rebuild is required a.s.o.

There is this script git-new-workdir that is supposed to allow multiple working branches, but for one, it is not part of the git release althoug it has been around for about 3 years, so I do not trust it to keep my files consistent. And secondly, I cannot find it as part of the Windows distribution, which is one of the machines I use for development.

So the only official option is to create a new "clone" for every branch, which seems incorrect, since each clone is a full-blown repository. I would not even know what to call the clone directories -- do I use the repository name or the branch name or both? What if I create another branch off that branch, a.s.o.

Real-use case update (@Philip's suggestion)

I usually work on two major/minor releases, with features going on both in parallel. There is also an occasional development branch, where experimental features go into, before being merged to some future release.

During feature development, often behaviour degrades and it is more efficient to compare it with the behaviour before the changes. Checking out a previous branch/revision is not good enough, because many times it comes down to debugging side-by-side, which means both revisions need to be checked out simultaneously on the harddrive.

So the more convenient and natural approach would be to keep a few so-called "active" branches checked out each in its own directory, with its own compiled binaries. This would also save compilation time and occasional local setup (i.e. configuration files that need to be changed after every checkout in order to get the product running).

Nick
  • 5,765
  • 5
  • 27
  • 36
  • 1
    **Why** you think that you need two branches in the working tree? What you want to do? – Mot Sep 07 '11 at 10:16
  • 2
    I want to compare the behaviour of the same product across revisions, I want to avoid rebuilding everything from scratch when going back to another branch. I must work on three separate features in parallel. – Nick Sep 07 '11 at 10:18
  • @Nick: Could you describe your situation? e.g. Do you have three machines and you are running three variants side by side; or perhaps one machine but running three threads, two in the background, and your development in the foreground? For the former case simply use independent clones, for the latter there are workflow based choices. – Philip Oakley Sep 07 '11 at 15:30
  • @Nick: so what is your 'check-in' policy during side-by-side debugging? Is it just temporary saves while dicovering the faults, or are you updating both versions 'at the same time' with proper commit messages? I.e. will all [but one] be junked after the faults are located. – Philip Oakley Sep 07 '11 at 16:17
  • Most of the time the purpose is to observe changes in variables common to both revisions and changes in the call branching. Temporary changes might be in order occasionally. I was never confronted with faults that could not be fixed in a single comparison session. – Nick Sep 07 '11 at 17:06
  • 1
    It looks like Bazaar supports this natively with [shared repositories](http://doc.bazaar.canonical.com/latest/en/user-guide/organizing_your_workspace.html#feature-branches), for those who are interested. [More info](http://wiki.bazaar.canonical.com/SharedRepositoryTutorial). – netvope Mar 31 '12 at 21:44
  • See http://stackoverflow.com/a/30185564/6309: With Git 2.5, a git repo can have **multiple working trees**. I have edited [my answer](http://stackoverflow.com/a/7331842/6309) accordingly. – VonC May 12 '15 at 08:35

7 Answers7

16

A branch when checked out is only a pointer to a commit (within a graph of commit).
As such, Git cannot check out two commits of that graph at the same time.

Actually, see "Multiple working directories with Git?".
With Git 2.5+ (Q2 2015), you can have multiple working trees for one git repo, with git checkout --to=<path>.

That allows you to Check out a branch in a separate working directory at <path>. A new working directory is linked to the current repository.

That link is "portable" (ie works even on Windows) as it is recorded in the main repo $GIT_DIR/worktrees directory.


Original answer (2011):

git branches

(Source: Scott Chason, ProGIT Book, http://progit.org/book/ch3-1.html, CC-BY-NC-SA)

If you need to work on two different branches at the same time, simply clone your repo and select (git checkout) the other branch in that clone. You can use the name of the branch as the name of the root directory for that clone.
And you can create branches in any of those repos.


So the question is why can't Git have TWO pointers, one for each directory? –

As mentioned in "Popularity of Git/Mercurial/Bazaar vs. which to recommend", Git is at its core a content management. Each commit represents a full snapshot of the repo.
You cannot view two contents in the same container (working directory), even though you can keep reference of as many pointer (branches) as you want.

Git Local operations

You only populate the working directory with one content.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    So the question is why can't Git have TWO pointers, one for each directory? – Nick Sep 07 '11 at 09:56
  • @Nick where is the benefit of having two branches as working copies at the same time when switching between branches costs nearly nothing? Do you work at both branches at the same time? Or just wanna _peek_ - if so, use gitweb. – mbx Sep 07 '11 at 10:08
  • @VonC: Do you have any idea on solving this [problem](http://stackoverflow.com/questions/7259135/git-commit-to-multiple-branches-at-the-same-time) regarding pushing commit to different branches? – TheOneTeam Sep 07 '11 at 10:09
  • @mbx, what about carrying along ignored files created during the build process. Rebuilding an entire project takes 15 minutes. I want to carry those files with each branch. – Nick Sep 07 '11 at 10:16
  • 1
    @Nick: Of course you can have as many branches as you like in each repository, it's just that you can only have one *current* branch. When you're working with git, most of the tools deal with the relationship between your working copy (i.e. what you see when you type `ls`), the index (i.e. the staging area, or what would make up the next commit) and the current commit (i.e. `HEAD`, usually the tip of a branch). git would be a nightmare to work with if that lattermost state was non-unique. – Mark Longair Sep 07 '11 at 10:16
  • 2
    @Nick: when you do `git checkout new-branch`, the timestamps are only updated for files that are changed, so if you're using a build tool (such as `make`) that considers mtimes, only the minimal set of files should be rebuilt. – Mark Longair Sep 07 '11 at 10:18
  • @Mark, thanks, I see how complexity might be an issue. Regarding the `make` comment, it is a good point. Still, sometimes differences between two major/minor versions can be many and there is still the issue of running both alongside. – Nick Sep 07 '11 at 10:24
  • Please make sure to use the correct attribution if reusing pictures from other sources. Beside that: Nice Explanation! :) – Lars Sep 20 '11 at 08:16
  • @Lars: thank you for the edit. You are right for the importance of attribution. – VonC Sep 20 '11 at 10:57
9

If you git clone with a local path, everything under .git/objects (that is, most of the commits and data in your repository) is hardlinked to the old repo wherever possible and takes up next to no disk space. So if you want two different working directories, it's not very expensive to just clone your repo locally. Trying to manage two different working directories from one repository might work, but it's not worth the trouble.

Basically, run git clone /path/to/old/repo new_repo and everything will Just Work.

Clueless
  • 3,984
  • 1
  • 20
  • 27
  • I heard about the clone hardlinks -- but I cannot seem to get them working on Windows (msysgit), most likely due to Cygwin limitations with hard/soft links. – Nick Sep 07 '11 at 10:14
  • Well, I know that hardlinks are _possible_ in cygwin, you may be right that msysgit doesn't use them. Still, hard disk space is cheap. Is having two copies of the repo particularly undesirable? – Clueless Sep 07 '11 at 10:25
  • not a problem with disk space of course. It does seem counterintuitive to have to clone the entire repository (with all different branches & revisions) just to be able to work on two branches in parallel. – Nick Sep 07 '11 at 10:49
  • Windows hard links only came in with ~Vista, ~W7 -- the `mklink` command. I don't think that msysgit differentiates between XP and later.... – Philip Oakley Sep 07 '11 at 15:34
  • @Nick, normally a git repository does not contain branches - since the cloned repository *is* the branch. It's not the same as SVN where branches, tags, etc. are all in the same repository. – laurent Sep 13 '11 at 11:17
  • @Laurent, every repository contains all branches & revisions, so in theory it could allow more than just a single checkout to be made of a branch or revision. – Nick Sep 13 '11 at 11:23
6

What you are wanting is called alternates, which lets one repo be dependent on another for its objects. Its use isn't very common because disk space is cheap and you have to be careful not to accidentally delete objects the dependent repo needs.

Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94
  • Yes, this sounds like it. This means the closest thing to multiple working directories is to reuse the `.git/objects` directory for multiple repositories. Cannot see any risks with deleting objects that are required. I imagine the .git/objects directory holds the entire history, which will only grow, no matter what is deleted/added. Or maybe you are referring to deleting branches? – Nick Sep 07 '11 at 13:59
  • Just found a referen to `git clone -s`, which uses alternates instead of hard links to a local repository. It talks of the dangers of removing branches in terms of deleting branches and having unreferenced commits (not sure how this is possible though). – Nick Sep 07 '11 at 14:33
  • 1
    If you never delete unmerged branches you won't have a problem. This problem would happen if you delete an unmerged branch in the parent repo that contains commits still used in the clone. – Karl Bielefeldt Sep 07 '11 at 16:21
  • Alternates are almost the same as regular git clones, the only difference is that you share some disk space. The OP explicitly rejected using clones "where there is just too much overhead (pushing/pulling/syncing...)", so this may not be much help. – sleske Sep 27 '12 at 12:05
5

If you want to be doing this,

I'd 100% recommend to write a custom application1 that manages this process in a foolproof way.

A starting point could be

GIT_WORK_TREE=/worktrees/stable   git checkout -f origin/stable
GIT_WORK_TREE=/worktrees/unstable git checkout -f origin/unstable

etc.

I would specifically make sure that none of your working copies look (remotely) like a usual repository so that normal git commands do not apply.

Using the usual git (sub)commands is recipe for disaster because someday you'll run into a script that doesn't use the GIT_DIR, GIT_WORK_TREE, GIT_INDEX quite the way it should (or you expected).


1 (perhaps based on NGit, or similar Git bindings for Python, Ruby -- watever is portable in your universe)

sehe
  • 374,641
  • 47
  • 450
  • 633
4

Actually, git-new-workdir was made for exactly this problem. I have the same problem, and use it without any issues. About your reservations:

There is this script git-new-workdir that is supposed to allow multiple working branches, but for one, it is not part of the git release althoug it has been around for about 3 years, so I do not trust it to keep my files consistent.

Well, I (and many others) have used it for years, and I have not come across any bugs myself, nor have I heard of any real problems (apart from a minor limitations, see below). That may not sound like much, but even with git itself (or any -free- SW project, for that matter), the only real guarantee you get is "no one has found a problem yet". I don't know why git-new-workdir is still in contrib, but I don't think that should deter you.

And secondly, I cannot find it as part of the Windows distribution, which is one of the machines I use for development.

That may just be because you distribution chose not to include the contrib/ stuff.

If you use git under Cygwin, you can just download and use the original git-new-workdir script. It works fine, I use it myself. If you use msysGit, there are ports available: https://github.com/joero74/git-new-workdir/blob/master/git-new-workdir.cmd and https://github.com/dansmith65/git/blob/master/contrib/workdir/git-new-workdir-win . I don't know about their quality, though.

Limitations of git-new-workdir

Just for completeness' sake, here are the limitations I know of:

Community
  • 1
  • 1
sleske
  • 81,358
  • 34
  • 189
  • 227
2

Maybe the submodules could help you having independent directories?

Plouff
  • 3,290
  • 2
  • 27
  • 45
  • Interesting approach -- might be helpful to some degree, assuming one arranges branches into submodules. – Nick Sep 07 '11 at 12:37
2

If I understand correctly, you are looking for the ability to CopyOut a specific branch head or commit on a branch, onto an independent directory, for investigation and comparison purposes, while still having your current development/bugfix branch still checked out as your main git 'reference'.

You expect that the independently 'copied out' directory would be unlikely to be re-committed directly to git.

An untested approach would be to use the base git command with its [--git-dir=<path>] [--work-tree=<path>] options, and to, through a careful script, adjust/correct your HEAD / Index as you do the CopyOut and step back to your current branch.

The usual caveats apply..

  1. Create new work_dir
  2. Start git pointing to work_dir
  3. Save current HEAD
  4. Checkout required branch/commit (it should go into work_dir)
  5. Set HEAD back to saved value (This is a hack and may not work without steps to keep the index right!)
  6. close git to 'forget' the work_dir setting
  7. restart git back in your old directory and its associated branch

This would definately need careful testing, and probably needs adjustment around steps 3 & 5, such that step 6-7 is true.

The git work-tree option could give a mechanism for bring in changes from the copyout directory if it had bug fixes that needed committing into the appropriate branch.

UPDATE --------------------------------
The clunx instructions are based on a Windows user's view.

# this is run from your main work dir (that contains .git)
Copy .git/HEAD MyBackup
# HEAD will contain the line similar to "ref: refs/heads/master"
mkdir <path>
# the path should be 'somewhere else', not in your existing directory
# If the path doesn't exist the following checkout will fail
Git --work-tree=<path> checkout <branch>
# this will extract the latest commit on that branch to the work tree path
# or use a commit sha1 instead of <branch>, if you want an exact commit
# Note: your index will be updated to reflect the checked out files, as will HEAD.
Copy MyBackup .git/HEAD
# set the HEAD back to where it was
Git reset
# reset the index appropriate for the previous Head.
# note we dropped the --work-tree parameter

A similar technique can be used to grab any revisions from the extracted path by pointing --work-tree at the right place at the right time and making sure the index is correctly set. Don't forget the usual caveats [backup, backup, and backup].

This worked for me on a test repo I have. Some of the actions were in Windows.

Philip Oakley
  • 13,333
  • 9
  • 48
  • 71
  • This sounds very interesting -- however, I am not sure what some of the steps mean. When you say "save current HEAD" you mean save the ".git/HEAD" file? Also, when you say "close git" you are probably referring to a GUI tool. If you can detail this a bit more in terms of a commmand like utility, it would be great. – Nick Sep 08 '11 at 13:42
  • @Nick: I have updated my answer with my tested command sequence. – Philip Oakley Sep 12 '11 at 22:21
  • Thanks -- thought you'd forgotten. Am playing w/ it -- seems clunky, might need some tweaking, will get back. – Nick Sep 13 '11 at 11:15
  • I'd been away, but hadn't forgotten. Things are, I think, much easier if you already have a fixed destination available and a place to store the HEAD file. I didn't try to make an alias for it. Somebody downvoted it, but I'm not sure why. – Philip Oakley Sep 13 '11 at 11:48