Like almost everyone in the DevOps world, I use GitHub, not because I like it, but because everyone uses it. In fact, I dislike it, because it’s confusing by design. Its misleading language excludes users, rather than welcoming them. To use it, you have to forget the actual meaning of words in English, or even their technical meaning, and use them as though they are arbitrary strings.
Worst of all, because most people memorize a command sequence without fully understanding how it works, when something goes wrong, it’s almost impossible to troubleshoot.
Okay, enough ranting.
I just discovered a solution to one of the problems I encounter frequently, so I want to share it with you. My utmost thanks to PowerShell MVP and Principal Engineer – Community Engineering for Chef, Steven Murawski for showing me the solution and letting me try it by myself (twice) while he was standing nearby in case of disaster. If this solution works for you, tweet your thanks to Steven.
Your Fork is Outdated
Here’s the problem. You create a fork (an online copy) of a repository and a clone (a local copy on disk) of your fork. You spend some time examining the files and interpreting the code. Then, when you’re ready to work, you notice the message on your fork.
“This branch is n commits behind (source-repo).”
That makes sense. While you were working, other people were working, too. So, you want to synchronize your branch of your fork (the online copy) and its clone (the disk copy) with the files in the source repository.
This happens so often and it’s so predictable that you’d think there would be a Sync button or something like it, but there’s not.
Also, if you try something rational, like opening your Git Shell (I use Git for Windows with posh-git) and trying to fetch/merge or pull (or push or anything else you can think of), that won’t work, because, by default, those commands synchronize your clone (on disk) with your fork (online) and they’re already in sync. You need to synchronize your fork/clone with their source.
Remote: A link to the Source
One solution, the one I had been using, is to copy any files you’ve changed to a different directory, delete your fork, delete your clone, then re-fork and re-clone, and move the changed files back in. Yes, I’ve done this. Whatever it takes.
The correct Steven-Murawski-approved solution is to create a remote, that is, a named link to the source repository, in your clone (on disk). This lets you get and send things from your clone to the source repo.
A remote is just another name, alias, or nickname for the URL to an online repository. In any command that you use the name of the remote, you can use the URL, and vice versa.
Actually, you already have one remote; a remote to the fork, although you might not have noticed. When you clone (make a disk copy) an online repo, the clone command automatically creates a remote to the repository that you cloned, typically your fork. That remote is named origin.
To see your remotes, use git remote.
C:\Github\dbatools [master]> git remote origin
To see the URL of the remote, add -v (for verbose). These are remotes to my fork, so they have my username, juneb, in the URL.
C:\Github\dbatools [master]> git remote -v origin https://github.com/juneb/dbatools.git (fetch) origin https://github.com/juneb/dbatools.git (push)
By default, when you use git fetch, git merge, git pull, or git push, git uses the origin remote. As a result, it gets stuff from or sends stuff to the fork that you cloned.
So, to get or send to the source repo, you need another remote.
Update your clone and fork
To get all changed files from a source repository and copy them to the clone, and then to the fork.
- On GitHub.com, on the page for the source repository (not your fork), copy the URL of the source repo. It’s the same URL that you would use to clone the source repo.
- In your shell, navigate to the directory with the clone, and use the following command to create a remote to the source repo.
git remote add <name> <copiedURL>
git remote add upstream https://github.com/ctrlbold/dbatools.git
The name is arbitrary. You can use any valid string for the name of your remote. The traditional name of a remote to the source repository is ‘upstream’ which makes about as much sense as the rest of the words in GitHub. In this use, it is a proper noun, not a direction. Regardless, I use it, because people recognize it.
- Get (fetch) and merge changes from the source repo to your clone. All names, including the remote name and branch name, are case-sensitive.
git fetch <nameOfRemote> git merge <nameOfRemote>\<branch>
git fetch upstream git merge upstream\master
- Send the changes from your clone (on disk) to your fork (online). You don’t need to qualify this command with a remote, because the “origin” remote is the default.
Now, your clone and fork have the same content:
C:\Github\dbatools [master]> git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean
And, miraculously, your branch is “even with master”.
Thanks, Steven! It works!
To sync again
If you continue to work on this project and your fork becomes outdated again, you already have a remote to the source repo. So, just repeat the fetch, merge, and push. There’s no need to recreate or update the link to the remote repo.
git fetch upstream git merge upstream\master git push
Not too tough. Having this solution makes my GitHub live a lot easier. I still don’t like it, but I use it.
June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. You can reach her at firstname.lastname@example.org, follow her on Twitter at @juneb_get_help, and find her GitHub repos at https://github.com/juneb.