Can I rebase a Git branch without modifying my working copy?

后端 未结 7 1979
南旧
南旧 2020-12-08 09:16

Suppose I have my \"master\" branch checked out. I\'ve committed some production changes to \"master\", and now I want to rebase my \"experimental\" branch onto the latest m

相关标签:
7条回答
  • 2020-12-08 09:53

    Since git 2.5, an even better solution is to use a second worktree.

    A git repository can support multiple working trees, allowing you to check out more than one branch at a time.

    $ git worktree add ../second-copy experimental
    $ cd ../second-copy/
    $ git rebase master experimental
    

    And that's it. Afterwards, you can rm -rf second-copy if you want, or keep it for more rebases in the future.

    $ git rebase master experimental
    
    0 讨论(0)
  • 2020-12-08 09:59

    I would also love it, but this leaves no hope to me:

    If <branch> is specified, git rebase will perform an automatic git checkout before doing anything else. Otherwise it remains on the current branch.

    http://git-scm.com/docs/git-rebase

    0 讨论(0)
  • 2020-12-08 10:03

    Is there a way to do all of the above in one step, without ever modifying anything in the working copy?

    This is unfortunately impossible (without creating a modifiable copy of the working copy - see also Petr's answer), because git performs all merge-y operations (real merges, cherry-picks, rebases, patch application) on the work tree. This is mentioned several times before, for example in one of the knowledgeable Jakub Narębski's answers:

    There is no way that merge (or rebase) can work without touching the working directory (and index), as there can be merge conflicts that have to be resolved using working directory (and/or index).

    Yes, it's a design decision, but it's a pretty understandable one - it'd be a bit of a chore to build up all the structure necessary to attempt a merge in memory, then as soon as it hits a conflict, dump everything into the work tree, when instead you could simply do it in the work tree in the first place. (I'm not a git developer; don't take this as absolute complete truth. There could be other reasons.)

    My suggestion, rather than writing a script to do all that mtime manipulation, would be simply to clone the repository, perform the rebase in the clone, then push it back into your original repository:

    git clone project project-for-rebase
    cd project-for-rebase
    git branch experimental origin/experimental
    git rebase master experimental
    git push origin experimental
    

    That of course assumes that experimental isn't checked out in your original repo. If it is, instead of the push, you'd do something like git fetch ../project-for-rebase experimental; git reset --hard FETCH_HEAD or more readable, git remote add for-rebase ../project-for-rebase; git fetch for-rebase; git reset --hard for-rebase/experimental. That will naturally touch whatever files differ between the original and rebased experimental branches, but that's definitely correct behavior. (This wasn't the example you gave, of course, but I want these instructions to be general!)

    0 讨论(0)
  • 2020-12-08 10:08

    Update, since git 2.5 this answer is superseded by the in-built mechanism "worktree" which is basically the same. See above answer: https://stackoverflow.com/a/12481546/1499102

    Similar to creating a clone of your repository, I find that it's much tidier to make use of multiple workdirs to do things like this. Also a clone will use a lot of disk space, this will use almost none.

    https://github.com/git/git/blob/master/contrib/workdir/git-new-workdir

    You create a new workdir like this:

    git-new-workdir project-dir new-workdir branch
    

    Then you can treat that as if it was a clone except that fetches and commits in your original workdir will be reflected here (although not on the working branch without recheckout). The only exception to this is if you have submodules as those are done separately for each workdir by default. Frankly I've never looked in to that because I try and avoid submodules.

    So basically just:

    cd new-workdir
    git checkout experimental
    git rebase master
    

    Not exactly a single command, but pretty simple.

    An advantage of this approach (like the clone approach) over the stash approach below is that if you have code currently executing (or otherwise being used by some processes) from your working directory, it isn't interrupted.


    The other option which isn't mentioned here is to do it in your current working directory, but stash your changes so that you can then instantly restore your working directory state.

    # current branch is master (with changes to working state)
    git stash -u
    git checkout experimental
    git rebase master
    git checkout master
    git stash pop
    

    Make sure to use stash -u if you have any new files as otherwise they will not be stashed. Again, not one step, but pretty clean and simple.

    0 讨论(0)
  • 2020-12-08 10:08

    So you want a rebase done on for a branch before you checkout that branch? I really can't see the reason for that, since if you don't checkout that branch you can't work on it. Why do you want to rebase a branch that you don't work on? Do the checkout, it will change your mtime and then do the rebase. The rebase will touch files that are changed and of course you need to rebuild them.

    However, a simple way to solve this is to use an other worktree for the rebase. Just set the enviroment variable GIT_WORK_TREE to an other worktree. Just don't forget to have your HEAD match your worktree.

    Depending on which branch he is at and what's pushed, a push to a non-bare repo can be dangerous. A much better solution is to fetch from the repo with the precious worktree instead. Example:

    ` orgbranch=$(git rev-parse HEAD)

    mkdir /tmp/tmp_wd

    cp -r !(.git) /tmp/tmp_wd

    export GIT_WORK_TREE=/tmp/tmp_wd

    git checkout branch1

    git rebase master

    git checkout $orgbranch

    export GIT_WORK_TREE=

    rm -rf /tmp/tmp_wd`

    0 讨论(0)
  • 2020-12-08 10:10

    I've created a small script to do this on linux. It's based on Jefromi's answer and a few additions (mainly, setting up alternates so the object database isn't copied, and only pulling the needed branches). Some of you may find it useful: https://github.com/encukou/bin/blob/master/oot-rebase

    If it doesn't do quite what you like, pull requests are welcome :)

    0 讨论(0)
提交回复
热议问题