9

Is there a way to work on the same file but on different features/branches in GIT?

I'm sure there is a way but what is the easiest?
I don't want to stash my changes as that's cumbersome.

With SVN I was able to work on 2 separate branches as 2 different entities without any intervention and easy to switch between the two.

CodeWizard
  • 128,036
  • 21
  • 144
  • 167
KingKongFrog
  • 13,946
  • 21
  • 75
  • 124
  • Do you mean that you would like to have two separate working copies, and work in both of them in parallel? No problem with that if you have a remote repo. – JB Nizet Jun 30 '16 at 22:01
  • No. One repo and switch between branches seamlessly where the file is in the last state I left it. – KingKongFrog Jun 30 '16 at 22:02
  • 1
    Git is a repository-based (rather than file-based, e.g. SVN or Perforce) version control tool. The @CodeWizard answer might work for you, but in general I believe you should only be working on one branch at a time. – Tim Biegeleisen Jun 30 '16 at 23:22
  • @TimBiegeleisen I see what you're saying. But sometimes one has several tasks and 2 may overlap and go into different releases but not at a point to push. – KingKongFrog Jun 30 '16 at 23:37
  • Please explain why git worktree is not a good solution? Like SVN, you end up with two different folder in which your file can evolve and be pushed (later) to two different branches. – VonC Jul 26 '16 at 06:22
  • Why don't you have two branches??? Then when you want you can merge them together, or merge them to master. – jrf Jul 29 '16 at 19:38
  • @jrf I do have 2 branches, but if I work on the same file, won't my changes be reflected in both on the local file? – KingKongFrog Jul 29 '16 at 21:23
  • 2
    @KingKongFrog Can you explain how your workflow in SVN works, because your questions are really not clear. If you want to simply switch between branches, that's what `git checkout` is for. – Patryk Obara Aug 01 '16 at 00:25
  • Let me explain little more. Let's say I have branch A and branch B. Both have a file file.txt. Let's say I'm working on branch A, I edit file.txt to include a line AAAAAA. I AM NOT ready to check this in. Now I have to work on branch B. I add a line to file.txt BBBBBBB. But at this point file.txt has the line AAAAAA after i switched branches. I would like this to be in the state of no AAAAAA since that is still in progress in branch A. How do I keep the both separated and in progress? – KingKongFrog Aug 01 '16 at 19:11
  • @KingKongFrog I wrote an answer, it can be achieved by branching as some people said. – jrf Aug 01 '16 at 20:10
  • @KingKongFrog ok, then you should simply commit to branch A, but don't push. Then git checkout to branch B, do your work, commit, don't push. Checkout back to A, make changes, commit --amend. You can switch like that as much as you like. It is normal way of working with branches. In git you don't need to publish your changes after every commit. – Patryk Obara Aug 01 '16 at 22:52
  • @PatrykObara This is true, however if I want to push branch B before branch A is completed, this is not possible with this option, correct? – KingKongFrog Aug 02 '16 at 00:18
  • Yes you can push any branch whenever, they won't "merge" together until you merge them. If you push them, say to github, you will see both branches separately there. What @PatrykObara says is correct! Same thing as I wrote in the answer below. (Maybe we could add a little bit more of detail to it). – jrf Aug 02 '16 at 15:31

5 Answers5

17

use the git worktree.

git worktree

Git worktree was introduced in 2007 under the contrib folder in the git repo and was called new-workdir.

In git V2.5 it was named worktree and it allows you to have multiple instances of the same repository across different folders.

for example:

git worktree add <second path>

will create another folder on your computer which allows you to work on different branch simultaneously.


If you want to remove the worktree, delete the folder and then execute git worktree prune which will remove the worktree reference.

prune
Prune working tree information in $GIT_DIR/worktrees.

Creating new worktree

# create new branch inside the worktree folder 
git worktree -b <branch name> <path>

Removing worktree

# do your code and once you have done 
# commit, push and now you can delete your folder
rm -rf <path>

# Tell git to remove the workdir copy
git worktree prune

Update In the coming versions (git 2.17+) a git worktree delete will be exposed as a new command for deleting worktrees.

Listing worktree

enter image description here


If you use rebase later on:

  • Note: (Since Git 2.7)
    you can also use the git rebase [--no]-autostash as well.
CodeWizard
  • 128,036
  • 21
  • 144
  • 167
2

You seem to be fixated on wanting to do it the Subversion way. I can understand that; changing development habits can be a longish process, and up to a point it may be fine to bend the tools to your own habits instead; that's perfectly fine. So here we go:

Think about it this way: with svn you have one big directory tree where "branches" are not really first class entities but (technically) arbitrary subdirectories

mystuff/trunk
mystuff/branches/feature1
mystuff/branches/feature2
...
mystuff/tags/1.0   
mystuff/tags/1.1
...

So, if you are used this to and happy with it, the exact same solution is possible with git as well, by checking out different copies of the repository in one (non-git) parent:

mystuff/master
mystuff/master/.git
mystuff/feature1
mystuff/feature1/.git
...

This is conceptionally exactly the same as before. You keep the different repositories on their respective branch at all times. You'll have a common ancestor to push/pull to/from for merges, as usual (note that this can well be handled locally, no need for a physically remote location, unless you have one anyways; you can/could also in theory use your master repository directly as origin).

What you do not get, and will never get with git, is committing changes in different branches in one go (under one "commit"). A "branch" in git is a fundamentally different thing than in Subversion, the same as a "commit" in git is a fundamentally different thing there (commit != revision). You will have to wrap your head around this, no tool will save you from that.

Some operations:

  • Create a new feature branch feature2 (starting at master):

    mystuff> git clone {URL-or-filesystem-path-of-common-origin-repository} feature2
    mystuff/feature2> git checkout -b feature2
    
  • Merge your work from a branch to master:

    mystuff/feature1> git add ... ; git commit ...
    mystuff/feature1> git push
    
    mystuff/master> git fetch
    mystuff/master> git merge origin/feature1
    

    Same for any other merges; the master branch is technically no different from any other branches in git, it's just a naming convention (like /trunk being a naming convention in Subversion).

  • Get rid of a branch:

    mystuff/feature1> git push origin :feature1      # deletes the branch on the common repository
    mystuff> rm -rf feature1
    

All of this uses up a bit more HDD storage than necessary; there are advanced ways to clone a repository locally, re-using the object store. Let me know in a comment if that is of importance to you; unless you really have space constraints, I frankly would not bother.

AnoE
  • 8,048
  • 1
  • 21
  • 36
  • Up-voted - this is a good answer that additionally addresses a concise mental process for switching from `svn` to `git`. – Daniel Langemann Aug 01 '16 at 22:36
  • As the OP mentioned that he does not want to stash all the time in order to switch, maybe he also does not like half-finished commits to appear within the history. To prevent this, git provides the [squash merge or interactive rebase](http://stackoverflow.com/questions/2427238/in-git-what-is-the-difference-between-merge-squash-and-rebase) functionality. This allows to summarize all changes done since the last synchronization point between both branches and to commit these changes in a single commit. – Roman Vottner Aug 01 '16 at 23:02
2

From what I understood from the comments section: is that you want to have 2 different "versions" of the same file. (As you mentioned on the comments, having file.txt have "AAAAAAA" line on one, and "BBBBBBB" on one, but not "AAAA").

This CAN be achieved by branching very easily.

Before starting your work, you will be "standing" on one branch (probably master). You can at this point create a new branch git checkout -b feature1 (This command creates and switches you to branch feature1). where you will make some changes to file.txt. Say you will write "AAAAAA". Then, you will have to commit it. git commit -a -m "Added the AAAA line". If you now git checkout master (you go back to master). Your file WON'T have "AAAAA" written on it, you can then do other changes to this file (either on this branch, or another branch). You can write "BBBBBBB" to this file, and you will have 2 "versions" of the same file "file.txt", one will have "AAAA" (the one on branch feature1) and the other on master will have BBBB.

Note: I created a scenario where you haven't already changed the file originally, but if you have, there are also ways of achieving this by branching. You should def read https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging

jrf
  • 490
  • 5
  • 16
1

I don't want to stash my changes as thats cumbersome.

The best way you could go about this is simply editing the file on both branches.

The idea would be to have the common base on your master branch, and add a feature per branch, given your use case.

0

I think, that your workflow might be affected by how slow branch switching is in SVN. In Git, you don't need to worry about this - checkout of new branch will take fraction of second (maybe few seconds for very large projects).

The most sensible thing for you would be to work on single branch and once you will do some commits, cherry-pick them to other branch. Example: (let's assume that you want to work on file f.txt, which is identical on both branches)

$ git fetch
$ git checkout branch-1 # assuming remote branches exists already
$ vim f.txt # do some editing
$ git commit -am "f file changed"
$ git checkout branch-2
$ git cherry-pick branch-1 # <- this will pick single last commit from branch-1
                           # and apply it to branch-2

You are not limited to picking last commit, of course - you can pick by commit id, tag, you can use ~ operator to select commit ancestor, etc. You can even do many cherry-picks at once by rebasing or, even nicer, interactive rebasing.

After your work is done, you need to do two separate pushes per branch (or you can change your push.default config option to matching to do one push from all local branches).

Patryk Obara
  • 1,847
  • 14
  • 19