Werner Digital

Technology

Git Guide II - Intermediate

Common problems and solutions using Git

Contents

Overview

This article grew out of a cookbook reference that I slowly built up in my notes. Source code management can have an amazing amount of nuances, and Git has built up an incredible array of ways to de-tangle things. Since it is very flexible, git can be very confusing and present way too many options. Here we will try to focus on more common patterns and solutions.

HEAD pointer

The HEAD pointer indicates your current position, which normally points to a current branch tip.

Headless (detached head)

Headless mode refers to setting the HEAD pointer to a commit not on a branch. Reverting a commit, then setting

git checkout HEAD@{1}

results in a detached head, or headless state.

git switch -c newbranch

would then reference the reverted commit as a new commit on newbranch.

Relative References

Relative references can look complicated and advanced, but really is just a convenient way specify a commit along a branch without digging up the id in the log.

Head Pointer Example

Using the last or last-x commit can be a little more complicated if it goes past a merged branch. In that case, their are multiple "back" paths, and we need to be able to reach any commit along any of the paths that led us to the current head of the branch.

This is best described with a simple drawing:

 H^2~3  H^2~2
 H^1~1  H^1
 H~2    H~1                    H
 O------O----------------------O   Main Branch
         \------O------O------/    Feature Branch
                H^2^1  H^2

---> time

This can get pretty complicated if you have complex branching, in those cases you should probably just specify the commit directly.

Reflog notation

HEAD@{1}

Workflows

multi clone

This is a way to allow editing on one parallel branch while compiling or running development servers on another

  • git clone . /path/newclonedir Sets up a local clone that requires less space (and sets up quicker) than a backup.

To pull the changes back into the normal copy

  • git pull /path/newclonedir HEAD

Remotes

  • git remote -v
  • git branch -r
  • git push
  • git fetch
  • git pull
  • git remote add
  • git add tracking

Complex Branches

Branch maintenance


  • create
    • git switch -c <new-branch> --track
  • add tracking
    • git push -u origin <new-branch>
  • list
    • git branch -vv
  • rename
  • rebase
  • remove tracking
    • git branch --unset-upstream <branch>
    • git push origin --delete <remote-branch>
  • delete
    • git branch -D <unmerged-branch>
    • git branch -d <merged-branch>
  • cleanup all dangling remote
    • git remote prune origin

Fast forward vs merge

Advanced Merge

Resolving conflicts

Customizing Install

Quick Config

At minimum, you should setup a few general things using the git config command. Your identity

  • git config --global user.name "John Doe"
  • git config --global user.email johndoe@example.com Your editor if you don't want to use the system default (usually vi). Here is a editor list with setup instructions.

You can ovveride any value in a specific repository by dropping the global parameter. In example:

  • git config user.email johndoe@some-project.com

  • Windows stores this in:

    • system - C:/Program Files/Git/etc/gitconfig
    • user - .gitconfig file in the user $HOME directory
    • local - .git/config in the local directory

Your can see which file each configuration item is coming from by using the command git config --list --show-origin

Default colors

I also like to tweek the command line output colors so that they are more visible:

git config --global color.status.untracked=red bold
git config --global color.status.changed=red bold
git config --global color.status.modified=green bold
git config --global color.status.updated=green bold
git config --global color.branch.remote=yellow
git config --global color.branch.upstream=yellow

Default branch

Force commit on merge

  • git config --global --add merge.ff false This configuration option disables fast-forward merges (copying commits directly from the merged branch and simply advancing the HEAD pointer) and forces a new single commit containing all changes from the merged branch.

Shortcuts

If you like to create shortcut names for the commands, you can do this in the config with an alias command

  • `git config --global alias.history "log --oneline --graph -10" which displays the last 10 commits from the log on one line each, will a text-graphic info for the branches used.

or my favorite

  • git config --global alias.history "log --oneline --graph --pretty='%h %s (%ar)'" which shows commits on one line each with branch text-graphic indicators and a relative date since commit. If you want to customize your own, use this pretty format definition.

Now you can use the command

  • git history instead of typing in all the parms that you commonly use on log.

Undo and Reorganize

  • git restore restores files into the working project directory from either the index or another commit.
  • git checkout
  • git reset resets the branch moves the branch tip, and may be used to restore the index (this overlaps with restore)
  • git revert make a new commit that changes everything back that a previous commit changed.
  • git clean

Undo staging

  • git reset will unstage tracked files without changing them or the commit history

  • git reset --soft HEAD^ uncommit and leave in stage

  • git reset --hard will unstage files and change any modified files back to the last commit.

  • git clean -f removes any untracked files, which will finish getting the project dir back to the exact last commit if you have added files.

Undo last commit

Undo the local commit and place it into staging.

  • git reset --soft HEAD~1

Undo the last commit and discard the changes

  • git reset --hard HEAD~1

Amending commits

If you have already given the commit command but wish you had included a few extra things, you can amend the previous commit.

  • git add /path/to/amended_stuff
  • git commit --amend --no-edit

If you are using a tracking branch and have already pushed this remote, then you need to rewrite the remote.

  • git push origin next -f The docs claim git push -f works but I get a perm issue I haven't solved yet on github when I do it this way.

Finding abandoned commit

git reflog lists the changes to references, which should include the reset or deletion that removed the reference from a branch or tag.

Rebase

  • git rebase <trunkBranch> <dependentBranch>
  • git rebase --abort, --continue, --skip Rebase used correctly is a great tool. It can also easily wreak havoc and create incredibly complex tangles.

Good reasons to rebase:

  • Updating dependent branches when the trunk updates The goal of rebasing is to get rid of redundant merge commits and use more fast-forward merges. This can flush out conflicts earlier, before the branch is merged back in.

Example:

  • main branch gets a package.json update
  • git switch <dependentBranch>
  • git rebase main or
  • git rebase main <dependentBranch>

Dependent dev branches may have added packages or made major semver updates, (or even backed down a version). You don't want to overwrite those changes. Rebasing the dependent branches to the new main branch head rewrites history so that the branches look like they branched after the package update. When the dependent branch merges it will be easy to see the actual changes made and there would be no conflicts to solve.

The problem: If you have other developers, or even other clones of the repository on another machine, it will have the wrong history and be out of sync. Using git fetch is a nice way of knowing when things get changed upstream, but it can give confusing advice.

After rebasing, do a git status to see this confusing message:

Your branch and 'origin/dev-branch' have diverged,
and have 7 and 7 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

git pull is not the answer here. You can't simply push the changes either. Use a forced push: git push -f origin <branchName>

Cookbook

Unused notes.

Tags

  • git tag <tagname>
  • git push -tags

Branches

delete

  • git branch -d <branch> - delete merged branch
  • git branch -D <branch> - delete unmerged branch
  • git push origin --delete <branch> - delete remote branch
  • git remote prune origin - remove references to remotes that no longer exist

Commits

view changes between commits

  • git diff <commit> <commit>

Repo

Repository commands for github

-- fetch <remote> <refspec>

pull existing repo

  • git clone ssh://git@github.com/account/x

populate new repo

  • git init (perhaps optional)
  • git remote add origin ssh://git@github.com//account/x.git
  • git push -u origin master or take over
  • git branch -u origin/master
  • git pull --allow-unrelated-histories

Setup

  • git config --global --add merge.ff false

Don't forget .gitignore