Page History

Git

Mark George edited this page on 23 Mar 2022

Clone this wiki locally

Contents

Troubleshooting

Problem: Can't push because remote and local have diverged.

Solution: Fetch, then compare local vs remote to check if anything bad is likely to happen when we merge:

>git fetch
>git status
On branch master480a7506288d93cfdb9d84dc5ecdb389bb906e09
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commit each, respectively.

>git log HEAD..origin/master

This will show the remote commit(s) that don't exist locally.

You can see the actual differences using:

>git diff --color-words master origin/master

Or

>git difftool master origin/master

See also: Check a merge result without actually merging

If it looks safe to merge then:

>git merge origin/master

Better yet, switch to new branch, and merge there first just to be sure:

>git checkout -b merging
>git merge origin/master

Problem: "Bad things" happened.

Solution: You can pretty much always get back to a previous state using the reflog

# work, test, stage, commit, do "bad things"

# first commit or stash anything that you want to keep - if it is committed it is really hard to lose it

# back up the repo (make a copy of the entire thing) - it is really hard to lose stuff if you have a backup

# now find the commit ID just prior to the 'bad things happened'
git reflog

# hard reset back to the good version
git reset --hard <commit ID>

Note — this is only really a good idea if you are just trying to revert a recent mistake that has not been pushed, or revert a bad merge. If you are resetting work that has been pushed to the remote then you will effectively be rewriting history which is only going to make things worse particularly if other people are using the same repository. A revert commit is the better way to deal with things in this situation.

Remove committed rubbish from repo

If the files still exist locally and we only care about getting them out of the repo, and not about removing all traces from the history:

git rm -rf --cached --dry-run -- <rubbish>
git rm -rf --cached -- <rubbish>

# add <rubbish> to .gitignore so it doesn't make it back into the repo
echo <rubbish> >>! .gitignore

If files have been removed locally via other means then stage and commit the removal using:

git stage --all <path>
git status

If you are getting 413 / Payload Too Large errors as a result of committing big files then you need to remove all traces of the offending files from the history. This is effectively rewriting history, so warn team members prior to doing this, and let them know that they will likely need to clone a new copy:

git log --pretty=format: --name-status | cut -f2- | sort -u

This will show all files that have ever existed in the repository. Once you identify the offending files you can use the BFG repo cleaner to remove all traces of them from the history.

https://rtyley.github.io/bfg-repo-cleaner/

This tool can also be used to remove sensitive files from the entire history, or even search and replace sensitive text.

General usage:

# stage and commit everything first, and get repo into a clean state
git stage src # stage anything important
git commit
git status # check if repo is clean and everything important is committed
git reset --hard # reset any rubbish that needs it to get into a clean state

# it is recommended that you start with a bare mirrored version of the repo - at bare minimum it means that you won't be borking your original repo if things go wrong
cd ..
git clone --mirror busted_repo mirrored_repo

# clean repo
java -jar bfg.jar --delete-folders build,.gradle --delete-files .DS_Store --no-blob-protection mirrored_repo

# garbage collect the repo
cd mirrored_repo
git reflog expire --expire=now --all && git gc --prune=now --aggressive

# push to remote
git push

# clone new repo from remote
git clone https://...  fixed_repo

Verify that fixed_repo is good, then get rid of the mirrored_repo and busted_repo.

Reverting a Commit

# find the ID for the commit that you want to undo
git log

# check that you definitely have the right commit
git show <commit ID>

# revert that commit
git revert <commit ID>

This effectively adds another commit that undoes the reverted commit. This is safe to push to a shared repo.

Branches

Deleting Branches and Tags

# kill local branch
git branch -d <branch>

# kill local tag
git tag -d <tag>

# kill remote branch/tag
git push origin --delete <branch / tag>

Topic Branches

Use topic branches to keep your master clean while you are working on stuff. You can also squash a branch with a messy commit history into something that is a bit more acceptable before merging into master.

Workflow

# sync origin/master from remote
git fetch origin master

# verify which branch we are on
git branch

# if we are not on the topic branch then check it out
git checkout topic/bob

# rebase the branch on master to make sure we are up to date
git rebase master
git rebase origin/master

# work, test, stage, commit
...

# push the topic
git push origin topic/bob

When topic is complete, merge back to master.

If it is a simple history, or you want to preserve the entire branch history in the master:

git checkout master
git merge topic/bob

If the history is screwy or you want to collapse it into larger commits for sharing.

# rebase master to make sure branch is current
git rebase master

# view history to find commit IDs
git log --reverse

# interactively rebase the commits into something legible
git rebase -i <commit ID>

# merge back onto master
git checkout master
git merge topic/bob

You will usually select the commit before the one you want to keep, and then pick the first line, and squash the rest.

Which Branches Need Merging?

git branch --no-merged

Stashes

Useful when you don't want to commit but need to do something potentially destructive like switch branches. Also useful if you did a bunch of work on the wrong branch and want to checkout the right right branch before committing.

Add a stash:

git stash push

If you want to add a message (if you expect to keep the stash around) then use:

git stash push -m "message"

You can view stashes using:

git stash list

You can show the contents of a stash using:

git stash show

To apply a stash:

git stash pop

This will apply and drop the most recent stash. If a merge conflict happens then the stash will not be dropped.

If you want to keep the stash around after applying it then use:

git stash apply

If you want to drop a stash:

git stash drop

Handy tricks

Check a merge result without actually merging

There currently isn't a git merge --dry-run, but the following is a reasonable approximation:

git fetch
git merge --no-ff --no-commit origin/master

# merge result will be reported

# we are left in a partial merge state, so abort
git merge --abort

FF merges are not able to be stopped, so we disable FF merge with --no-ff. The --no-commit will stop the merge result from being committed.

Adding a remote

>git remote add origin https://...
>git push -u origin master

Substitute origin for remote name if you don't want to use origin.