Git through common problematic cases

Most people on my team started as junior engineers. This means, that we can not always assume any git knowledge below the surface. One of my teammates even described this as "we basically know push and pull". So I tried to have a lecture on some of the cases and how to resolve it.

We do use GitHub for our main repository, so some of the things could be GitHub specific. Also we use master as our main branch with production code.

Also, the examples will be given as command line code, simply because this is what I am most used to. But there are other ways to do it.

Can't merge to master because of merge conflicts

This is what actually started the idea for the training. Sometimes there will be merge conflict. They will block the merging to the master. Since we disable the resolving of merge conflicts inside of the Github, they need to be resolved on the local machine. And the example I mentioned resolved this by opening a new pull request.

git checkout master
git pull
git checkout <branch name>
git pull
git merge master
git status
git add <filename>
git commit
git push

The first thing that needs to happen in this case is to make sure that both master and branch are in the same state at the GitHub versions. This is what the first four lines do.

After this, the master is merged to the branch. If the merging to master is locked because of merge conflicts, this means that merge conflicts will also happen the other way. Check with the git status, to see which files have conflicts and resolve them. Basically find the content between <<<<<<< and >>>>>>>, that have ======= in between. This is the place of merge conflict. Figure out which of this code needs to be there at the end. Add the file. Repeat this, until there are no more conflict files. Then just commit the changes and push them.

Move changes from master to my branch

In order to make sure merge conflicts do not happen, the next question was logical. How to make sure the changes in master are accounted for in the branch, before pushing.

There are basically two ways, merge and rebase.

# use one of the following lines
git rebase master
git merge master

Both of them will make sure, that the changes from master are accounted for in the branch. Where they are show is the only difference. With rebase, the branch changes will be after the master changes. With the merge is the reverse, the branch changes are before the master changes.

Before pushing to the GitHub, both are an option and it is the question of the preference. I personally prefer the rebase. Once the changes are pushed to GitHub, only the merge is possible - as otherwise the push will require to be a forced push.

Unknown commits on the branch

Example of this would be ending with the branch, that has the commits from previous work, that is already in the pull request. Or there could be unexplained commits for people outside of our team, that are there as the first commit.

Interactive rebase

git log
git rebase -i <sha of commit before the one you want to replace>

Now, if you notice this before you push your branch for pull request, then you can simply rewrite the history, so that branch never had these commits. In this case, you can use the git log to check the sha of the commit before the ones, that you want to delete. This is a value, that is needed in the interactive rebase.

Once rebase is started, you can see the list of all commits after the one put in the rebase command. You can simply delete the lines of the commit, that you want to disappear.

Merge master

git merge master

Now if you already pushed commits to the GitHub, then the rebase is a very impolite thing to do (though in our workflow, it would be alright in most cases). If the changes are already in the master, then merging the master in the branch will make the changes 'disappear'. They will still be in the branch, but when checking pull request on GitHub, it will not be changed.

This was once used to resolve the problem, when the commit from a person from a another team ended up on start of one of our branches. Don't ask me how that happened - because I do not know. What also surprised me, was that merge was needed at all to make GitHub aware of it. Does it not compare the differences with the current branch? Well, something to maybe keep an eye on.

Revert the commit

git revert <sha of the commit you want to remove>

The last option is to revert the commit you want to remove. This will simply create the commit, that will be the reverse of the commit you want to delete. But this will mean, that these changes will not be seen in the pull request in the GitHub.

Push to new repository cloned with GitHub provided link

This was a problem in our session. New repository was created, and we cloned it, but we could not push to it. The reason being that in our repositories we use the origin links with API tokens from GitHub in them, so some of us do not have ssh configured on our computer. If one forgets this, and clones with the normal URL, there is not reason to need to re-clone the repository.

git remote
git remote set-url <name of remote> <new url with token>

All that needs to happen is to change the link to where the push goes to the one with the API token. The names of all remotes can be listed with git remote. After this, the link can be set with the git remote set-url <name of remote> <new url with token> command. You can even see the current set URL with the git remote get-url <name of remote>.

My work is on the wrong branch

Sometimes we get disrupted in our work, and before finishing one task, we start on the next one. I know that sometimes before I notice, I have commits for two completely separate Jira tickets on the same branch.

git cherry-pick <sha of commit to copy>

Now, if there is only one commit, then I will create a new branch from master and cheery-pick the commit I need. Cherry-pick will copy and not move the commit, so the commit will be present on two different branches. We already discussed the way to remove unwanted commit in the upper section on the unknown commits in branch.

git checkout -b <new branch name>
git rebase -i <commit before any of the changes>

Now if there are multiple commits for both, then I will simply create a branch from the current one. This basically gives me a sort of copy of all the work, so it is now on both branches. I will then rewrite both branches with git rebase -i, so that work on each branch represent one piece of work.

Local-only commits on master branch

This one and the next is more a personal one, since it happens all the time. I would start working, create the commits, and tried to push. But I can not push to master without peer review, so it would fail.

git checkout -b <new branch name>
git checkout master
git stash
git reset --hard HEAD~<number of commits to remove>
git pull
git stash apply

In this case you first create a new branch to create a copy of my work.

You can then stash changes with git stash. What this does is remove all the uncommitted changes from the directory. I always have some, for some reason or another.

You can then do git reset --hard HEAD~<number>, to remove a specific number of commits. Git reset removes them from history as well (like rebase), it is as they never existed. But since master should be the same in GitHub and local machine, this is not a problem. Just pull the master and all the changes come back. This is why I usually put a bit higher number, since the data is not going to be deleted anyway.

You then also bring back the changes from the stash with git stash apply, if you had any and you saved them in stash at the start.

I pushed, but GitHub does not prompt for new pull request

Sometimes the upper thing can also happen, but on the branch, that was already merged to master. I simply continue to do the work on the same branch. I promise it is not intentionally.

If the files changed were different, than this is not a problem. Just after pushing, instead of getting a prompt in the pull request, you would need to do a couple more clicks. On the main page of repository in GitHub, there is a branch button with the number of current branches. Click on that and click on pull request button next to the right branch, and you can open a pull request for these changes as well.

Links to other useful examples

Since I created this page, so I could share it with any other junior developer we will get in the future, it makes sense to also start colecting other stuations, that I di not cover.

I commited the changes for two issues in the same commit

One thing I noticed with one of my teammates is, that they are adding the files to git with gt add -A. So in this case, I would imagine that commiting something you really did not want to commit can happen frequently. It also happens to me sometimes, if I forget to include the --patch flag.

James wrote the example of how to resolve this in his post How to Split Commits.

Export the git repository into zip

Git has a way to save the repository into archive, like zip or tag.gz and so on. I did not have a use case yet, but it might be in the future, so I am saving the link.

Here Jamie wrote about save the repository into zip file.

Restore the past version of a file

In the new git version, there is apperently also the restore command, that allows you to bring back the past versions of the files. Tehnically one could use this insteead of the revert. More info in 3 Reasons to Upgrade Git For The First Time Ever