Commit a file to a Different Branch Without Checkout

20,028

Solution 1

It's not possible.

The changes you commit are related to the current working copy. If you want to commit to another branch it means that you could commit changes from your working copy, but base them from another copy state.

This is not a natural way of versioning your work, and this is why you need to make different steps (stash changes, checkout the branch, pop stash and commit) to accomplish it.

As for your specific use case, a simple way is to keep two copies of your work, one checked out at master branch, and the other at pages branch.

In the pages working copy, add the master copy as a remote repo.

  • You commit pages on master
  • Pull from master on the pages copy
  • push to GitHub
  • reset the master branch at its previous state.

Solution 2

It can be done by reimplementing git commit.

This can be done with various call to git hash-object

But this is hard to achieve.

Please read progit chapter 9 for more details and a full example of how to simulate a commit.

Solution 3

As several others have said, it is literally possible, but impractical.

However, as of Git 2.5 (with some important fixes in 2.6 and minor ones since then), there is a practical method for doing this using git worktree add.

Let's say, for instance, that you want to work on branches main and doc "at the same time", or branches develop and test "at the same time", but the two branches in question deliberately contain different things. (For instance, the doc branch has documentation that exists outside or alongside the code, or the test branch has tests that will be run against the code, but not distributed, or which are expected to have failures for which tests are deliberately skipped on the develop side, or whatever.)

Instead of just:

git clone -b develop <source> theclone

followed by working in theclone with constant switching back and forth between the two branches, you would:

git clone -b develop <source> theclone

but then:

cd theclone
git worktree add ../ct test  # check out branch test in ../ct

or just:

git worktree add ../test     # check out branch test in ../test

Now you can run your tests in ../test while developing in theclone. You can merge and/or rebase changes from one branch to the other in the usual way: the underlying repository is already shared, so no git push or git fetch is required. You simply have both branches checked out, into two separate work-trees, named theclone and test from the top level.

Solution 4

So long as you don't have anything in your current index that differs from your HEAD that you want to keep you can so something like this. (If you do want to keep your index you could temporarily export the GIT_INDEX_FILE environment variable to point at a temporary file for the duration of these commands.)

# Reset index and HEAD to otherbranch
git reset otherbranch

# make commit for otherbranch
git add file-to-commit
git commit "edited file"

# force recreate otherbranch to here
git branch -f otherbranch

# Go back to where we were before
# (two commits ago, the reset and the commit)
git reset HEAD@{2}

We've never actually checked out otherbranch and our working tree files haven't been touched.

Solution 5

I made a little tool that does exactly this: https://github.com/qwertzguy/git-quick

It let's you edit specific files from another branch without checking out the other branch completely (just the files you want to edit) and commit them. All this without ever affecting your working copy or staging area.

Behind the scenes it uses a combination of git worktree and sparse checkout. The source is fairly small, so you can read through.

Share:
20,028
Schneems
Author by

Schneems

I Ruby, I Rails, I teach at UT Austin, I work @Heroku.

Updated on July 26, 2022

Comments

  • Schneems
    Schneems almost 2 years

    Is it possible to commit a file in a git branch with out checking out that branch? If so how?

    Essentially I want to be able to save a file in my github pages branch without switching branches all the time. Any thoughts?

    Update: It's not possible to do what I want (see comments below for use case). What I ended up doing is programmatically cloning my current directory to a tmp directory, then checking out my branch in that tmp directory (doesn't affect my working directory) and committing my files to the tmp directory clone. When I'm done, I push back to my working directory and delete the tmp directory. Sucks, but it's the only way to commit files to another branch without changing the current working branch of the working directory. If anyone has a better solution, please feel free to add it below. If it's better than 'it cannot be done', I'll accept yours.

  • Schneems
    Schneems over 12 years
    It's possible to read files from a different branch without checking it out, so I was hoping there would be a way to write to a different branch without checkout. While i understand this isn't 'natural' i'm using GIT as a data store and not as pure version control in this situation. I have a workaround (see my comment above) but it is rather gross and prone to errors.
  • CharlesB
    CharlesB over 12 years
    Just curious: why maintain a separate pages branch?
  • Schneems
    Schneems over 12 years
    pages.github.com it's a way to serve html content such as docs or a demo page directly using github. It has to be in a separate branch because thats the way github wrote it. While the data (docs in this case) is related to the project it shouldn't necessarilly be in the main branch for a number of reasons, overwhelming the commit messages of master with changes unrelated to code being one of them.
  • jbenet
    jbenet over 11 years
    See answer by Charles Bailey below!
  • user541686
    user541686 about 6 years
    How is this answer correct? The changes you commit have nothing to do with your working copy. They're based entirely on what is added to the index. You can delete everything in your work tree and still git commit as if nothing had happened, because git only looks at the index.
  • StingyJack
    StingyJack almost 4 years
    This is not a natural way of versioning your work - its a natural way of wanting to backup work in progress periodically without having to fuss with branch switching and cherry picking
  • TTT
    TTT almost 4 years
    I wonder if this solves a related problem as to why you don't want to checkout another branch (or clone a copy of the repo): Suppose you have a very large repo and "otherbranch" is wildly different from your current branch (but still relatively close to the merge-base), and you want to add a minor hotfix to it. Checking out otherbranch, making the change, and going back to your working branch could set you back quite some time. Is it correct to assume that reset --mixed as presented in this approach would be significantly faster than checking out a very old branch?
  • TTT
    TTT almost 4 years
    This really ought to be the accepted answer. Today I had to create a branch from a tag from a year ago, and then add one commit to it. I tested it both ways. The branch differed from my current head by 72,000 files, and it took me 2.5 minutes to checkout. (Going back only took 1.5 minutes.) Then I tried your way and it only took about 15 seconds each way, most of which was the time it spent printing out the list of files. For larger repos, file sizes, and/or slower hard drives (mine's a speedy SSD), the difference would likely be more drastic. This is a great solution!
  • Moberg
    Moberg over 3 years
    Question title says Without Checkout. This answer uses checkout.