140

Is it possible in Git to switch to another branch without checking out all files?

After switching branch I need to delete all files, regenerate them, commit and switch back. So checking out files is just a waste of time (and there are about 14,000 files - it is a long operation).

To make everything clear:

I need all this to upload documentation to GitHub.

I have a repository with the gh-pages branch. When I rebuild documentation locally, I copy it to the repository directory, commit and push to GitHub. But I was not happy, because I had two copies of documentation locally. And I decided to create an empty branch and after committing, switch to empty and delete files. But switching back is a long operation - so I asked this question.

I know that I can just leave on the gh-pages branch and delete files, but I don't like dirty working trees.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tig
  • 25,841
  • 10
  • 64
  • 96
  • How long is "long" for you? What platform are you working on? Are you working over a network such as with NFS or other file sharing? – Greg Hewgill Aug 15 '09 at 21:56
  • What is the purpose of this exercise? Do you want to have two branches, one with detailed commits, second recording only major changes (coarse-grained)? – Jakub Narębski Aug 16 '09 at 08:55
  • Perhaps it's cheaper to create a temporary (or permanent?) clone of your working copy. [My related answer](http://stackoverflow.com/a/29616287/946850) and a [writeup](http://krlmlr.github.io/git-subbranch) show how this works even as a subdirectory of the main repository. – krlmlr Apr 13 '15 at 22:55

11 Answers11

154

Yes, you can do this.

git symbolic-ref HEAD refs/heads/otherbranch

If you need to commit on this branch, you'll want to reset the index too otherwise you'll end up committing something based on the last checked out branch.

git reset
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 1
    Use `echo "ebff34ffb665de0694872dceabe0edeaf50ec5a9" > .git/HEAD` followed by `git reset` to point to a ref instead of a branch. – cadorn Oct 18 '12 at 19:03
  • 2
    Directly writing to the HEAD file is less dependable. What if you are in a subdir? For detached head (head pointing to SHA1 directly), try this: `git update-ref HEAD refs/heads/otherbranch` – Alexander Bird Jan 23 '13 at 20:25
  • 2
    If you want to check out to a fresh branch from the current branch, another way to do this is to 1. `git stash` 2. `git checkout -b otherBranch` 3. `git stash pop` – Winny Jul 15 '14 at 01:14
  • @AlexanderBird: `git update-ref` is useful, but it also moves the tip of the current branch. – tomekwi Feb 11 '16 at 06:45
  • 1
    I used `git reset refs/heads/otherbranch`directly to get the same result – Joerg Aug 24 '21 at 18:49
  • git update-ref doesn't quite do what you want by default; you need to pass `no-deref` or else it modifies the current branch – Mark VY Dec 16 '22 at 04:53
74

Using basic git commands only:

This answer is a bit longer than that of Charles, but it consists solely of basic git commands that I can understand and thus remember, eliminating the need to keep looking it up.

Mark your current location (commit first if needed):

git checkout -b temp

Reset (moves) the marker to the other branch without changing working dir:

git reset <branch where you want to go>

now temp and other branch point to the same commit, and your working dir is untouched.

git checkout <branch where you want to go>

since your HEAD is already pointing to the same commit, working dir is not touched

git branch -d temp

Note that these commands are also readily available from any graphical client.

42

In v2.24 git switch is something like a safe git checkout.
Hence I renamed the alias below to git hop for
"hop on the branch without changing worktree"

For the benefit of the reader:

While I think that Charles Bailey's solution is a correct one, this solution needs a tweak when switching to something, which is not a local branch. Also there should be some way how to do it with regular commands which is easy to understand. Here is what I came up with:

git checkout --detach
git reset --soft commitish
git checkout commitish

Explained:

  • git checkout --detach is the same as git checkout HEAD^{} which leaves the current branch behind and goes into "detached head state". So the next modification of HEAD no more affects any branch. Detaching HEAD does not affect the worktree nor the index.
  • git reset --soft commitish then moves HEAD to the SHA of the given commitish. If you want to update the index, too, leave --soft away, but I do not recommend to do so. This, again, does not touch the worktree, and (--soft) not the index.
  • git checkout commitish then attaches HEAD to the given commitish (branch) again. (If commitish is a SHA nothing happens.) This, too, does not affect index nor worktree.

This solution accepts everything which refers to a commit, so this is ideal for some git alias. The rev-parse below is just a test to make sure, nothing breaks in the chain, such that typos do not accidentally switch into detached head state (error recovery would be way more complex).

This leads to following git hop treeish alias:

git config --global alias.hop '!f() { git rev-parse --verify "$*" && git checkout "HEAD^{}" && git reset --soft "$*" && git checkout "$*"; }; f'

FYI, you can find it in my list of git aliases.

Tino
  • 9,583
  • 5
  • 55
  • 60
  • Don't you want to use `$@` rather than `$*`? The difference is that $@ with not expand quoted arguments, which have spaces inside. – kyb Sep 07 '18 at 19:35
  • 2
    @kyb [The function trick is stolen from another SO answer](https://stackoverflow.com/a/7005698/490291). And `$@` is definitively not meant here. `$*` is used instead of `$1`, such that `git switch -f b` becomes the same as `git switch '-f b'` which should be an error. This way I can shorten the alias by leaving away some error handling like `!f() { [ 1 = $# ] || { echo 'WTF!'; return 1; }; ..` – Tino Sep 17 '18 at 20:11
  • Very good solution. Especially, that it can be used for remote branches! – Nils-o-mat Feb 14 '20 at 09:44
13

I think you're looking for the plumbing command git read-tree. This will update the index but will not update any files in your working directory. For example, assuming branch is the name of the branch to read:

git read-tree branch

If you want to then commit to the branch you just read, you will also need to:

git symbolic-ref HEAD refs/heads/branch
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
13

Wouldn't be a better solution to have two working directories (two working areas) with one repository, or even two repositories?

There is git-new-workdir tool in contrib/ section to help you with this.

Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • Is git-new-workdir the same as git's own worktree command? I use worktree when I want to checkout a branch into a different folder (without having to clone the entire repo). – Ryuu Apr 12 '17 at 12:53
  • The `git-new-worktree` script predates `git worktree` subcommand; this command was not available when the answer was written. The script for example requires symlink support; IMHO it is better to use native support. – Jakub Narębski Apr 12 '17 at 23:27
7

You can overwrite your HEAD file with a different branch name:

echo "ref: refs/heads/MyOtherBranch" > .git/HEAD

user157005
  • 91
  • 2
  • 13
    It's probably better to use the symbolic-ref command to do this for you: `git symbolic-ref HEAD refs/heads/MyOtherBranch` http://www.kernel.org/pub/software/scm/git/docs/git-symbolic-ref.html – Greg Hewgill Aug 15 '09 at 21:44
  • @GregHewgill, this is the only way I know of to move the HEAD to a commit hash. Can you do that with `git symbolic-ref`? – tomekwi Feb 11 '16 at 06:47
3

Or just use a patch file to patch from your otherbranch to your master

git diff otherbranch master > ~/tmp/otherbranch.diff
git checkout master
git apply ~/tmp/otherbranch.diff
Tomer Ben David
  • 8,286
  • 1
  • 43
  • 24
  • So simple, yet so effective, and doesn't mess with any git internals. You can even just use `git diff master` without specifying the current branch if it's already checked out. – john Jun 19 '23 at 13:55
1

say you want to be in branch A, but with the files from branch B

find the current commit ref of branch A with git log, e.g. "99ce9a2",

git checkout A
git reset --hard B
git reset 99ce9a2

you should now be on branch A, with a folder structure corresponding to B, which show up as unstaged changes (A history has not changed).

Dugas
  • 381
  • 1
  • 3
  • 9
0

With so many files, you may be best off just keeping two repos, one for each branch. You can pull changes back and forth as needed. This is going to be less surprising than trying to play scurvy tricks with git.

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
  • You can use `git-new-worktree` for that instead (in `contrib/`) – Jakub Narębski Aug 16 '09 at 08:49
  • I often did something similar, which is copying my local directory before doing any "scary git stuff" as a newbie (like changing branches, etc). I'd encourage people to go that route until you feel confident in your git-fu, but to move away from it when possible. Keeping two distinct repos is ok, but adds a layer of complication and doesn't let you take advantage of git's many useful features (merging, cherry-picking, etc). – David Aug 04 '17 at 14:10
0

If you are simply trying to change where a remote branch points, you can do it with "git push" without touching your local copy.

http://kernel.org/pub/software/scm/git/docs/git-push.html

The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>, followed by a colon :, followed by the destination ref <dst>. It is used to specify with what <src> object the <dst> ref in the remote repository is to be updated.

eg, to update foo to commit c5f7eba do the following:

git push origin c5f7eba:foo

Not sure if that's what you were after or not.

Tim Abell
  • 11,186
  • 8
  • 79
  • 110
  • The question already got an answer: http://stackoverflow.com/questions/1282639/switch-git-branch-without-files-checkout/1282706#1282706 – tig Feb 04 '10 at 16:21
0

you can make use of

      1. git checkout -f <new-branch>
      2. git cherry-pick -x <previous-branch-commit-id>

previous-branch-commit-id is the commit from where you want to copy old the data.

Haseena Parkar
  • 939
  • 2
  • 12
  • 28