Git Guide II - Intermediate
Common problems and solutions using Git
Contents
- Relative references
- Workflows - incomplete
- Remotes - incomplete
- Pull request - incomplete
- Customizing Installs
- What is Happening Status, log, show, diff, whatchanged
- Undo and Reorg restore, checkout, revert, diff apply
- Branch tools
- Advanced Merge - incomplete
- Rebase - incomplete
More Git Guide
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 claimgit 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
orgit 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 branchgit branch -D <branch>
- delete unmerged branchgit push origin --delete <branch>
- delete remote branchgit 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