Git Notes #5 [edge cases]

Since I use GIT, during my daily work, I always face with some edge cases. This post is about to list such cases in one place. I collected set of situations, verified on practice.

 

 

Case #1 – Squash commits

Condition:

  • You have set of commits either after changes by push or pull requests merges.

Goal:

  • You want to squash those commits into one

Approaches:

  • by doing git commit --amend and then “git push --force
  • by doing git rebase -i and mark commits as “squash||s” and then “git push --force
  • using “git commit --squash=SHA-ID" and later, when “git rebase --interactive --autosquash BASE-SHA-ID” occur marked commits as “squash! message from SHA-ID commit” will be squashed.
  • the same as above, but using --fixup=SHA-ID so that to strip/skip commit message window asking to change message after squashing.

Sub-Case – when u create commits from github fork-pull-request-approach only

  • U need to re-push but with cleared history via git reset --soft, “create new SHA commit” and push with --force aka +.

In addition, there is my post about more use cases with interactive rebase.

Case #2 – Split big commit into a few commits

Condition: You have huge commit, but want to split into a few smaller commits.

Goal: Separate logic, or change chunks of code into commits.

Approach: in short – u have to reset changes but not entirely, and then modify INDEX/HEAD state.

git reset --soft HEAD~1
git reset HEAD path/to/file/or/folder
git commit -C ORIG_HEAD

git reset --soft HEAD~4
git commit -m "New comment for squashed commits"

or

git rebase -i HEAD~4

Case 3 – Tracking branch from remote

Variant 1 – using git checkout

By default, when you create new branch, based on top of another branch, then, git tracks that base branch on remote repository, and give you hints, when it’s hanged. But it occur, when you create local branch, based on remote branch.

git checkout -b my-local-branch origin/master

Then you have such message:

Branch my-local-branch set up to track remote branch master from origin.
Switched to a new branch ‘my-local-branch

Here is explanation from git-scm for git-checkout:

Specifying -b causes a new branch to be created as if git-branch were called and then checked out. In this case you can use the --track or --no-track options, which will be passed to git branch. As a convenience, --track without -b implies branch creation;

-t || –track
When creating a new branch, set up “upstream” configuration.

Variant 1.1. – checkout to remote branch

Imagine, someone else pushed new branch, and u don’t have local copy yet. So when u execute avoiding origin

git checkout that-new-branch

you will have message:

Branch that-new-branch set up to track remote branch that-new-branch from origin
Switched to a new branch ‘that-new-branch

Variant 2 – using git push

git-push has -u || --set-upstream

For every branch that is up to date or successfully pushed, add upstream (tracking) reference, used by argument-less git-pull and other commands.

If u do git push -u origin new-master-shmaster, u will have message

Branch new-shamter-2 set up to track remote branch new-master-shamter from origin.

Case #3.1 – detached HEAD

Condition(s):

  • git checkout SHA
  • git checkout mybranch/2.3 #
  • or bare repo case, when default branch was master, and u can’t git checkout another-master and git checkout origin/another-master gives you detached HEAD.

Problem: In fact, you have HEAD in detached state.
Solution 1 (silly):

git branch my-temporary-work
git checkout master
git merge my-temporary-work

Solution 2 (smart):

git checkout --track mybranch/2.3
Resource: stackoveflow.

 

 

Case #4 – Reset code after not needed pull/merge

Condition:

  • Set of commits, then your commit, then late git pull, and ur commit is BEFORE merge pseudo-commit.

Goal:

  • U want to revert old code BEFORE the pull but remain pulled merged code

Approaches:

  • Interactive rebase and selecting SHA id as drop. Not very stable, when lot of merges.
  • Creating branch and then cherry-picking all after commits and then merge new branch in old (but reset –hard) branch.
  • git pull origin [branch] --rebase || -r – “it will pull the latest commits on the current branch while placing your commits on top of whatever new commits were brought — yes, this is basically rebasing your commits.”. Resource1. Resource 2.

 

Case #5 – End-Of-Line conversion

Condition: You either work between Windows/Unix, or IDEA created file with wrong ending line, or you copied code/text from Outlook/etc to file.

Problem: Windows vs. Unix…

Approaches:

  1. By using configs: core.eol [lf, crlf, native], core.autocrlf [true, input, false], core.safecrlf [true, warn, false]
  2. By using .gitattributes [text: auto]

Notes (quotes from git-scm):

  • core.safecrlf:

    warning: CRLF will be replaced by LF in README.md.

  •  https://git-scm.com/docs/gitattributes#_checking_out_and_checking_in
  • core.autocrlf overrides core.eol
  • If the text attribute is unspecified, Git uses the core.autocrlf configuration variable to determine if the file should be converted.
  • If you simply want to have CRLF line endings in your working directory regardless of the repository you are working with, you can set the config variable “core.autocrlf” without using any attributes.
  • This example shows, that we can configure git to not touch binary files when end of line conversion occur.

Case #6 – set git config(s) in advance to clone+fetch

Condition: U don’t have any local git repo, and u want to set git attribute (autocrlf, eol and maybe ignorecase) from CLI using git config --global but BEFORE cloning your target repository.

Problem: git asks u to be inside of git repo to run git config --local.

Solution 1 (simple):

git config --global core.autocrlf false

And when u cloned codebase, unset that global setting but set local setting in new repo.

Solution 3 (geek):

git clone --config  core.autocrlf=false git@bitbucket.org:username/repo.git

Text from docs:

Set a configuration variable in the newly-created repository; this takes effect immediately after the repository is initialized, but before the remote history is fetched or any files checked out.

 

Case #7 – Fast Forward

–ff

When the merge resolves as a fast-forward, only update the branch pointer, without creating a merge commit. This is the default behavior.

–no-ff

Create a merge commit even when the merge resolves as a fast-forward. This is the default behaviour when merging an annotated (and possibly signed) tag.

–ff-only

Refuse to merge and exit with a non-zero status unless the current HEAD is already up-to-date or the merge can be resolved as a fast-forward.

git merge --no-ff
git cherry-pick --no-ff
git rebase --no-ff

Case #8 – core.ignorecase and “git mv –force SRC DST”. !!!

http://stackoverflow.com/questions/52950/how-to-make-git-ignore-changes-in-case

You can force git to rename the file in a case-only way with this command:

git mv --force name.txt NAME.TXT

Note: this doesn’t change the case of the file in your checked out copy on a Windows partition, but git records the casing change and you can commit that change. Future checkouts will use the new casing.
Note: this really change file case on MacOS (where core.ignorecase = false by default). TODO on Windows (where core.ignorecase = false by default).

or via unstaging file at all:

git rm --cached name.txt
mv name txt Name.TXT

Case #9 – Assume some file as unchanged. !!!

Condition: Sometimes, projects have files which we need to have in git, so that we can clone it and later use, but we don’t need that file to be registered by git as changed (when it’s changed). So we want to assume, that file is unchanged. You may know, that sometimes, IDEA file project.iml is changed, when u change your project settings, or pom.xml changed by Java/Maven code, and u don’t want to commit, or very common use case, file .properties, where u modify your local/environment data, but don’t want to commit it.

Problem: File modified, and git track it.

Solution: Git has nice tool “update-index” for this. Note: this works ONLY for local working copy index. Settings are not PUSH-ed to remote repository, and as result, you can’t get those changes to another developer copy.

git update-index --assume-unchanged .properties
git update-index --assume-unchanged gl_test.iml

git ls-files -v # to see, what files are assumed as unchanged

H .gitattributes
h .properties
H After_Detached_Commits.txt
h gl_.iml
H last.txt

git update-index --no-assume-unchanged ggg.iml

git update-index --refresh
git update-index --really-refresh

Anti Case #8 + #9

When trying to checkout another branch I faced with this:

error: Your local changes to the following files would be overwritten by checkout:  .properties
Please commit your changes or stash them before you switch branches.

error: The following untracked working tree files would be overwritten by checkout:
    last.txt
    new file.txt
Please move or remove them before you switch branches.

 

First is side-effect from Case #9 (with assumed unchanged) and second is side-effect from Case #8 (with in fact git mv --force).

git-checkout-throws-an-error-on-assume-unchanged-files quote:

  • --assume-unchanged assumes that a developer shouldn’t change a file. This flag is meant for improving performance for not-changing folders like SDKs.
  • --skip-worktree is useful when you instruct git not to touch a specific file ever because developers should change it. For example, if the main repository upstream hosts some production-ready configuration files and you don’t want to accidentally commit changes to those files, --skip-worktree is exactly what you want.

So it looks like --skip-worktree is much better. But I researched, similar problem discussions and no I am not sure:

 

I used rm, command to remove file “last.txt”, I could check out to another branch, but that branch did have “Last.txt”, so I have the loop-issue with adding/removing file. Odd.

error: The following untracked working tree files would be overwritten by checkout:
Last.TXT
Please move or remove them before you switch branches.

 

Case #10 – bare repository

Notes:

  • git clone --bare git@bitbucket.org:alundiak/foo.git
  • git clone --bare git@bitbucket.org:alundiak/foo.git my-new-repo.git
  • cd foo.git folder where some git commands work (“fatal: This operation must be run in a work tree“).
  • git remote show origin.
  • simple git fetch inside of bare repo, doesn’t affect history –  gitk/git log doesn’t show all new changes but it exists in index, and reachable by working copy, but this may help:
  • cd foo.git;
    git fetch # in fetch shows that FETCH_HEAD changed, but gitk doesn't show history
    git fetch origin master:master #works
    git fetch origin master:master --prune --force # works
    git fetch origin 'refs/heads/*:refs/heads/*' #works. Using single quote is crucial.
  • or
    git --git-dir=foo.git fetch origin master:master # works. Use "master:master" to apply changes and see it via gitk

Case #11 – Access via SSH keys

Add use case with ssh-keygen using passphrase and reloading Git Bash.

Add info about SSH 6 vs. 7 – from BT.

Case with SSH to bittbucket – there is diff. between repo SSH key (so called deployment key) and account ssh key.

 

And this is only edge cases I remember. I will update this post in future, with nice, well tested and verified on practice interesting edge cases, so that I could have one place with list of all examples.

Resources

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s