Working on a file in two git branches at one time

I have a file in two different branches of a project that is so drastically different (hundreds upon hundreds of changes) that merging was becoming unreasonable. I’ll call that file CONVOLUTED.module.
I wanted to go old-school and compare the files side by side. Cutting and pasting changes with regular commits so I could test the incremental changes as I manually merged the files.
I figured there are several options to do this.

Option 1

One might be to move out of my working directory and re-clone the repository. In which case I would also specify the branch that I wanted to clone and use a new directory name to clone into.
Then I could open the both version of the CONVOLUTED.module file.

To do that I’d use a normal ‘git clone’ with the -b option to specify the branch and then I’d append the command with the new directory name.

To illustrate that; here’s the code to pull down a clone of the remote repo: ssh://git@GIT.HOSTING.URL/PROJECT_NAME/REPO_NAME.git;

$ git clone ssh://git@GIT.HOSTING.URL/PROJECT_NAME/REPO_NAME.git

No fancy stuff, just a clone.

Now I’ll insert ‘-b DEV_BRANCH’ to choose my branch and I’ll append a new directory name: ‘REPO_NAME_DIRECTORY_NAME’ to clone into.

$ git clone -b DEV_BRANCH ssh://git@GIT.HOSTING.URL/PROJECT_NAME/REPO_NAME.git REPO_NAME_DIRECTORY_NAME

Done.
Now I have a copy of the different versions of the file(s) that I can open simultaneously.

This may be ideal in some cases; I use it all the time with my web servers when I have a dev and live versions of a site that live in different directories but they share the same repo so I can share assets and check out code changes as needed.

Note: of course you can achieve this same result without using a remote repository; by duplicating the entire working directory and then changing the active branch in the directory copy.

But that’s not what we’re looking to do here. So in summary, this option has it’s drawbacks if you only need to compare one file. If your project is large, you’re using up storage space for no reason.

Option 2

We could create an empty directory and pull down just the one file.
Steps overview:

  1. Create the new dir. (repo. name – branch name)
  2. git initialize the new dir.
  3. Set the git remote
  4. Fetch (don’t pull) the files
  5. Check out the single file from the specified branch

On the command line that could look like this:

$ mkdir REPO_NAME-DEV_BRANCH
$ cd REPO_NAME-DEV_BRANCH/
$ git init
Initialized empty Git repository in /file/path/REPO_NAME-DEV_BRANCH/.git/
$ git remote add origin ssh://git@GIT.HOSTING.URL/PROJECT_NAME/REPO_NAME.git
$ git fetch
remote: Counting objects: 738, done.
remote: Compressing objects: 100% (580/580), done.
remote: Total 738 (delta 318), reused 470 (delta 149)
Receiving objects: 100% (738/738), 10.89 MiB | 19.52 MiB/s, done.
Resolving deltas: 100% (318/318), done.
From ssh://git@GIT.HOSTING.URL/PROJECT_NAME/REPO_NAME.git
 * [new branch]      master     -> origin/master
 * [new branch]      MAIN_BRANCH    -> origin/MAIN_BRANCH
 * [new branch]      DEV_BRANCH -> origin/DEV_BRANCH

At this point you can see I’m down to step 4. I’ve fetched but my directory is all but empty.

$ ls -al
total 0
drwxr-xr-x   3 vid  admin   102 May  3 13:27 .
drwxr-xr-x+ 30 vid  admin  1020 May  3 13:27 ..
drwxr-xr-x  12 vid  admin   408 May  3 13:31 .git

Just one .git dir in there. That’s quite a bit smaller than if I’d done a clone or pull and had all the files in the branch.

To continue, I’ll checkout the file I want (CONVOLUTED.module) from the remote branch (DEV_BRANCH):

$ git checkout origin/DEV_BRANCH -- CONVOLUTED.module
$ ls -al
total 104
drwxr-xr-x   4 vid  admin    136 May  3 13:31 .
drwxr-xr-x+ 30 vid  admin   1020 May  3 13:27 ..
drwxr-xr-x  13 vid  admin    442 May  3 13:31 .git
-rw-r--r--   1 vid  admin  49183 May  3 13:31 CONVOLUTED.module


There it is. Now I’ve basically achieved the same thing in two different ways. Perhaps saving some space, but not any time.

Yet there’s another option.

Option 3

In this scenario I stay in my working branch and create a temporary copy of a file from the other branch.

If I simply checkout the file from the other branch I would overwrite my file. So a better way would be to ‘show’ the file contents and pipe them into a new file.

Let’s break that down;
To show the file we can use ‘git show‘ and specify the branch:file name:

git show DEV_BRANCH:CONVOLUTED.module

If you are not in your repository’s root, you can put in the absolute path to the file or just use the relative link (./).

git show DEV_BRANCH:./CONVOLUTED.module

‘git show’ displays the file contents in std out. Give it a shot and you’ll get a screen full of code.

So to pipe that into a file it’s a simple as appending ‘>’ and a new file name*.
I choose the name CONVOLUTED_DEV_BRANCH.module;

git show DEV_BRANCH:./CONVOLUTED.module > CONVOLUTED_DEV_BRANCH.module

You could also keep the same name and pipe the file into a temp dir.

*Note it doesn’t have to be simple if you don’t want it to be… you can play with different pipe options: &> or 2> among them.

Bottom line

You have several ways to go old-school with your file comparison. These are a few that I played with today.
I suppose you are only limited by your imagination.

Comments
  • Max says:

    I was in a similar situation a few weeks ago. I knew it was going to be ugly so I created a new branch and then went ahead with the merge knowing it would break.

    I then created a branch based on the working commit and began checking in each commit one by one. This caused a merge conflict on pretty much every commit but I was able to see in manageable chunks what was different. I used the GUI sourcetree to help me with the subsequent insane branching. 20 merges with many changes in them later, I had a clean branch.

    Because the clean branch was based on the original messed up one, I was able to merge the clean into the messy one. I intended to rebase so it looked nice, but that go confusing so I left it as is.

    So, another way is to checkout and merge commit by commit. I am sure there are better ways as outlined above.

  • jankiiahir says:

    I was in a similar situation a few weeks ago. I knew it was going to be ugly so I created a new branch and then went ahead with the merge knowing it would break.

  • jankiiahir says:

    clean branch was based on the original messed up one, I was able to merge the clean into the messy one. I intended to rebase so it looked nice, but that go confusing so I left it as is.

Leave a Comment