Branching#

Branching allows us to diverge from the the primary line of work and work in isolation.

git branch <newbranchname> # create a branch
git branch # list all branches
git switch <newbranchname> # switch to <newbranchname>
git switch -C <newbranchname> # Creates and switches to that branch

Rename a branch

git branch -m oldname newname

See conventions of best practices for naming branches.

Delete a branch

git switch <primarybranch, usually main>
git branch -d <branchname to delete>
# if the branch is not merged you will get an error.
git branch -D <branchname to delete> # this will delete an unmerged branch and your changes will be lost.

Compare branches

# from main
git log main..<branchtocompare>                    # see details of commit
git log --oneline --patch main..<branchtocompare>  # see actual changes
git log --oneline --stat main..<branchtocompare>   # see stats for changes

See differences

git diff main..<branchtocompare>
git diff <branchtocompare>   # shorter form to compare to current branch
git diff --name-only <branchtocompare>     # show name
git diff --name-status <branchtocompare>   # show name and status

Stashing changes#

If we need to change form a branch but aren’t yet ready to commit the changes made, we use stash.

git stash push -m "Useful message"

New files that are not tracked will not be included in the stash.

git stash pull --all (-a) -m "Meaningful message"

Viewing Stashes

git stash list
git stash show stash@{X} # x being the numberical reference to the stash
git stash show X         # x being the numberical reference to the stash

Apply Stash

When we are ready to continue to work on our stashed files use:

git stash apply X # x being the numberical reference to the stash

Cleaning up Stashes

git stash drop X # x being the numberical reference to the stash, drop a single stash@{X}
git stash clear # clears all stashes

Merging Branches#

If there exists a direct liner non-divergent path between the branch and the main branch git carries out a fast forward merge.

# Note: It's always a good idea to use the --graph flag.
git log --oneline --all --graph
# from main
git merge <secondarybranch>

If the main branch has received additional commits (has diverged) after the second branch was created, git carries out a three-way merge or a merge-commit.

# from main
git merge --no-ff <secondarybrancj> # no fast forward mode

To disable fast-forward merges

git config ff no  # disabled for this repository
git config --global ff no  # disabled by default (not working in my version of git)
# possible alternative
# in git config file
mergeoptions = --no-commit --no-ff

Worth reading before adding to your global settings. https://stackoverflow.com/questions/2850369/why-does-git-perform-fast-forward-merges-by-default/2850413#2850413 and https://stackoverflow.com/questions/2100829/when-should-you-branch/2107672#2107672 and http://sip-router.org/wiki/git/gitconfig

Viewing Merged and Unmerged Branches#

To view the merged branches:

git branch --merged

It’s good practice to delete branches once they are merged.

To view unmerged branches:

	git branch --no-merged

Merge Conflicts#

When there are conflicting branches.

# from the branch you want to merge to (usually main)
git merge <featbranchname>

# This will result in a merge error
git mergetool
# The files can be editted by hand (don't forget to rmove the markers )
# my gitconfig uses vimdiff3
git add <filenames>
git commit # (default message will be the default message)

Aborting a Merge#

Maybe now isn’t the time to resolve these conflicts.

git merge --abort  # returns us to the state before the merge.

Undoing a Faulty Merge#

We could do a history rewrite a to remove the commit we can use reset, or if you want a complete history, we can do a revert. This is recommended only if you haven’t shared your history.

git reset --hard HEAD~1 # lose local changes
git reset --soft HEAD~1 # keep local changes

For a complete history, or in a shared repositories, it’s recommended to use revert.

git revert -m 1 HEAD # m=message and 1=the first parent of the branch

Squash Merging#

If it isn’t necessary to record the branch to be merged, history, then we can use squash merging. (Maybe there are many commits, or they are low quality or they were used for a quick bug fix or the like.) We would prefer to combine them into a single commit before merging with the main branch. Ideally is is used for small short lived branches with bad history.

git merge --squash <ShortLivedBranchName>
git add <files>
git commit -m"Meaningful message"

git branch -D <ShortLivedBranchName>  # forces the deletion of the short lived branch. -D is needed because the branch does not appear as merged, due to the git --squash

Rebasing#

To keep a cleaner history for divergent branches we can use rebasing. This rewrites the history. It’s recommended to use only on the local repository. This is irreversible. We are creating new commits. Any branches from the secondary branch would have their history messed up if the secondary branch is rebased.

# on the secondary branch
git rebase main
git swtich main
git merge  <SecondaryBranchName>
# if there are conflicts then use merge tool to resolve the conflicts
# options include
git mergetool
# decide how you are going to combine the changes
git rebase --continue
...
git --abort # to cancel the rebase and return to the previous state before rebasing.
# don't forget to remove the automatically generated backup file.
# to avoid the creation of a backup file
git config --global mergetool.keepBackup false

Vimdiff3 Mergetool#

:diffg RE # get from remote
:diffg LO # get from local
:diffg BA # get from base

Cherry Picking#

Instead of merging a complete branch of multiple commits, we can also cherry pick a commit.

#from the target branch
git cherry-pick <commitReference>    # includes commit
git cherry-pick <commitReference> -n # no commit

Cherry Picking a single file#

To cherry pick the most recent version of a file from a specific branch into another branch (main in the example) use:

git restore --source=<secondaryBranch> -- <filename>
# The latest version of the file from the secondary branch is now in the working directory of the primary branch and can be added and commit as usual.

Recover a Deleted Branch#

If like me you decided to use an git alias to delete unmerged branches, but did it form a feature branch. You main branch has now been deleted too. Don’t panic.

If you’ve just deleted the branch you’ll see something like this in your terminal Deleted branch <your-branch> (was <sha>). Then just use that <sha> in this one-liner:

git checkout -b <your-branch> <sha>

source: https://stackoverflow.com/questions/3640764/can-i-recover-a-branch-after-its-deletion-in-git