Multiple working directories with Git?

I'm not sure if this is something supported by Git, but in theory it seems like it should work to me.

My workflow often involves my editing of files in multiple branches simultaneously. In other words, I often want to open a few files in one branch is while I edit the contents of another file in another branch.

My typical solution to this is to make two checkouts, but it's a shame I can't share branches and refs between them. What I would like is to just have two working directories managed by the same .git folder.

I'm aware of local git clone solutions (the default, which is to hardlink shared objects, and the --shared option, which sets up an alternate object store with the original repo), but these solutions only cut down on disk space usage, and especially in the case of --shared, seem fraught with peril.

Is there a way to use one .git folder, and have two working directories backed by it? Or is Git hardcoded to have just one working directory checked out at any time?


Git 2.5 proposes since July 2015 a replacement for contrib/workdir/git-new-workdir : git worktree

See commit 68a2e6a by Junio C Hamano ( gitster ).

The release note mentions:

A replacement for contrib/workdir/git-new-workdir that does not rely on symbolic links and make sharing of objects and refs safer by making the borrowee and borrowers aware of each other.

See commit 799767cc9 (Git 2.5rc2)

That means you now can do a git worktree add <path> [<branch>]

Create <path> and checkout <branch> into it. The new working directory is linked to the current repository, sharing everything except working directory specific files such as HEAD, index, etc. The git worktree section adds:

A git repository can support multiple working trees , allowing you to check out more than one branch at a time.
With git worktree add , a new working tree is associated with the repository.

This new working tree is called a "linked working tree" as opposed to the "main working tree" prepared by " git init " or " git clone " .
A repository has one main working tree (if it's not a bare repository) and zero or more linked working trees.

details:

Each linked working tree has a private sub-directory in the repository's $GIT_DIR/worktrees directory.
The private sub-directory's name is usually the base name of the linked working tree's path, possibly appended with a number to make it unique.
For example, when $GIT_DIR=/path/main/.git the command git worktree add /path/other/test-next next creates:

  • the linked working tree in /path/other/test-next and
  • also creates a $GIT_DIR/worktrees/test-next directory (or $GIT_DIR/worktrees/test-next1 if test-next is already taken).
  • Within a linked working tree:

  • $GIT_DIR is set to point to this private directory (eg /path/main/.git/worktrees/test-next in the example) and
  • $GIT_COMMON_DIR is set to point back to the main working tree's $GIT_DIR (eg /path/main/.git ).
  • These settings are made in a .git file located at the top directory of the linked working tree.

    When you are done with a linked working tree you can simply delete it.
    The working tree's administrative files in the repository will eventually be removed automatically (see gc.pruneworktreesexpire in git config ), or you can run git worktree prune in the main or any linked working tree to clean up any stale administrative files.


    Warning: there is still a git worktree "BUGS" section to be aware of.

    The support for submodules is incomplete .
    It is NOT recommended to make multiple checkouts of a superproject.


    Note: with git 2.7rc1 (Nov 2015) you are able to list your worktrees.
    See commit bb9c03b, commit 92718b7, commit 5193490, commit 1ceb7f9, commit 1ceb7f9, commit 5193490, commit 1ceb7f9, commit 1ceb7f9 (08 Oct 2015), commit 92718b7, commit 5193490, commit 1ceb7f9, commit 1ceb7f9 (08 Oct 2015), commit 5193490, commit 1ceb7f9 (08 Oct 2015), commit 1ceb7f9 (08 Oct 2015), and commit ac6c561 (02 Oct 2015) by Michael Rappazzo ( rappazzo ).
    (Merged by Junio C Hamano -- gitster -- in commit a46dcfb, 26 Oct 2015)

    worktree : add ' list ' command

    ' git worktree list ' iterates through the worktree list, and outputs details of the worktree including the path to the worktree, the currently checked out revision and branch, and if the work tree is bare.

    $ git worktree list
    /path/to/bare-source            (bare)
    /path/to/linked-worktree        abcd1234 [master]
    /path/to/other-linked-worktree  1234abc  (detached HEAD)
    

    There is also porcelain format option available.

    The porcelain format has a line per attribute.

  • Attributes are listed with a label and value separated by a single space.
  • Boolean attributes (like 'bare' and 'detached') are listed as a label only, and are only present if and only if the value is true.
  • An empty line indicates the end of a worktree
  • For instance:

    $ git worktree list --porcelain
    
    worktree /path/to/bare-source
    bare
    
    worktree /path/to/linked-worktree
    HEAD abcd1234abcd1234abcd1234abcd1234abcd1234
    branch refs/heads/master
    
    worktree /path/to/other-linked-worktree
    HEAD 1234abc1234abc1234abc1234abc1234abc1234a
    detached
    

    Note: if you MOVE a worktree folder, you need to manually update the gitdir file.

    See commit 618244e (22 Jan 2016), and commit d4cddd6 (18 Jan 2016) by Nguyễn Thái Ngọc Duy ( pclouds ).
    Helped-by: Eric Sunshine ( sunshineco ).
    (Merged by Junio C Hamano -- gitster -- in commit d0a1cbc, 10 Feb 2016)

    The new doc in git 2.8 (March 2016) will include:

    If you move a linked working tree, you need to update the ' gitdir ' file in the entry's directory.
    For example, if a linked working tree is moved to /newpath/test-next and its .git file points to /path/main/.git/worktrees/test-next , then update /path/main/.git/worktrees/test-next/gitdir to reference /newpath/test-next instead.


    Be careful when deleting a branch: before git 2.9 (June 2016), you could delete one in use in another working tree.

    When " git worktree " feature is in use, " git branch -d " allowed deletion of a branch that is checked out in another worktree.

    See commit f292244 (29 Mar 2016) by Kazuki Yamaguchi ( rhenium ).
    Helped-by: Eric Sunshine ( sunshineco ).
    (Merged by Junio C Hamano -- gitster -- in commit 4fca4e3, 13 Apr 2016)

    branch -d : refuse deleting a branch which is currently checked out

    When a branch is checked out by current working tree, deleting the branch is forbidden.
    However when the branch is checked out only by other working trees, deleting incorrectly succeeds.
    Use find_shared_symref() to check if the branch is in use, not just comparing with the current working tree's HEAD.


    Similarly, before git 2.9 (June 2016), renaming a branch checked out in another worktree did not adjust the symbolic HEAD in said other worktree.

    See commit 18eb3a9 (08 Apr 2016), and commit 70999e9, commit 2233066 (27 Mar 2016) by Kazuki Yamaguchi ( rhenium ).
    (Merged by Junio C Hamano -- gitster -- in commit 741a694, 18 Apr 2016)

    branch -m : update all per-worktree HEADs

    When renaming a branch, currently only the HEAD of current working tree is updated, but it must update HEADs of all working trees which point at the old branch.

    This is the current behavior, /path/to/wt's HEAD is not updated:

      % git worktree list
      /path/to     2c3c5f2 [master]
      /path/to/wt  2c3c5f2 [oldname]
      % git branch -m master master2
      % git worktree list
      /path/to     2c3c5f2 [master2]
      /path/to/wt  2c3c5f2 [oldname]
      % git branch -m oldname newname
      % git worktree list
      /path/to     2c3c5f2 [master2]
      /path/to/wt  0000000 [oldname]
    

    This patch fixes this issue by updating all relevant worktree HEADs when renaming a branch.


    The locking mechanism is officially supported with git 2.10 (Q3 2016)

    See commit 080739b, commit 6d30862, commit 58142c0, commit 346ef53, commit 346ef53, commit 58142c0, commit 346ef53, commit 346ef53 (13 Jun 2016), and commit 984ad9e, commit 6835314 (03 Jun 2016) by Nguyễn Thái Ngọc Duy ( pclouds ).
    Suggested-by: Eric Sunshine ( sunshineco ).
    (Merged by Junio C Hamano -- gitster -- in commit 2c608e0, 28 Jul 2016)

    git worktree lock [--reason <string>] <worktree>
    git worktree unlock <worktree>
    

    If a linked working tree is stored on a portable device or network share which is not always mounted, you can prevent its administrative files from being pruned by issuing the git worktree lock command, optionally specifying --reason to explain why the working tree is locked.

    <worktree> : If the last path components in the working tree's path is unique among working trees, it can be used to identify worktrees.
    For example if you only have to working trees at " /abc/def/ghi " and " /abc/def/ggg ", then " ghi " or " def/ghi " is enough to point to the former working tree.


    Git 2.13 (Q2 2017) add a lock option in commit 507e6e9 (12 Apr 2017) by Nguyễn Thái Ngọc Duy ( pclouds ).
    Suggested-by: David Taylor ( dt ).
    Helped-by: Jeff King ( peff ).
    (Merged by Junio C Hamano -- gitster -- in commit e311597, 26 Apr 2017)

    Allow to lock a worktree immediately after it's created.
    This helps prevent a race between " git worktree add; git worktree lock " and " git worktree prune ".

    So git worktree add' --lock is the equivalent of git worktree lock after git worktree add , but without race condition.


    Git 2.17+ (Q2 2018) adds git worktree move / git worktree remove : see this answer.


    The git distribution comes with a contributed script called git-new-workdir . You would use it as follows:

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

    where project-dir is the name of the directory containing your .git repository. This scripts creates another `.git' directory with many symlinks to the original one except for files that cannot be shared (like the current branch), allowing you to work in two different branches.

    It sounds a bit fragile, but it's an option.


    I came across this question hoping for a solution I didn't find here. So now that I did find what I needed, I decided to post it here for others.

    Caveat: This is probably not a good solution if you need to edit multiple branches simultaneously, like OP states. It is for having multiple branches checked out simultaneously that you don't intend to edit. (Multiple working directories backed by one .git folder.)

    There were a few things I've learned since I came to this question the first time:

  • What a "bare repository" is. It is essentially the contents of the .git directory, without being located in a working tree.

  • The fact that you can specify the location of the repo you are using (the location of your .git dir) on the command line with the git option --git-dir=

  • The fact that you can specify the location of your working copy with --work-tree=

  • What a "mirror repo" is.

  • This last is a pretty important distinction. I don't actually want to work on the repo, I just need to have copies of different branches and/or tags checked out simultaneously. In actual fact, I need to guarantee that the branches don't end up different from my remote's branches. So a mirror is perfect for me.

    So for my use case, I got what I needed by doing:

    git clone --mirror <remoteurl> <localgitdir> # Where localgitdir doesn't exist yet
    mkdir firstcopy
    mkdir secondcopy
    git --git-dir=<localgitdir> --work-tree=firstcopy checkout -f branch1
    git --git-dir=<localgitdir> --work-tree=secondcopy checkout -f branch2
    

    The big caveat about this is that there isn't a separate HEAD for the two copies. So after the above, running git --git-dir=<localgitdir> --work-tree=firstcopy status will show all the differences from branch2 to branch1 as uncommitted changes - because HEAD is pointing at branch2. (That's why I use the -f option to checkout , because I'm not actually planning to make any changes locally at all. I can checkout any tag or branch for any work-tree, as long as I use the -f option.)

    For my use case of having multiple checkouts co-existing on the same computer without needing to edit them , this works perfectly. I don't know if there is any way to have multiple HEADs for the multiple work trees without a script such as is covered in the other answers, but I hope this is helpful to someone else anyway.

    链接地址: http://www.djcxy.com/p/4440.html

    上一篇: 在git克隆后,我没有看到我的分支

    下一篇: 多个工作目录与Git?