github: show history up to given commit hash

pattern: http://<site_name>/<repo_name>/commits/<commit_hash>

eg:

If you remove the trailing “s” from “commits”, you will see a diff of that commit vs the previous.

 

git | diff | make file6 appear before file2

The pain — a big commit touches on too many files, 3 of them major changes, while 5 files have cosmetic changes. In git-diff or git-difftool, I want to focus on the 3 major files.

Some developers would want to split the big commit into a “major” and a “cosmetic” commit, but one of the snapshots would be half-cooked and unusable. In such a case, the commit comment would warn “This commit is part of a bigger change. Please do not build this commit alone”.

As an alternative we can make the 3 major files show up first (or last) in diff. You can put the content below in src/diffOrder.txt and use it as instructed. All unspecified files would show up in natural order.

The line-ending issue the most common mistake with -O !

# git diff -O src/diffOrder.txt
# check EACH line below to ensure unix line ending, 
# even if entire file is unix-style.
# wildcards are needed below.

*/ProductCacheUtil*
*/CSV*
*/FutureDataCacheUtil.java
*/RJOBRIENDROPTools.java

interpreting git diff output #numbers

— based on https://stackoverflow.com/questions/2529441/how-to-read-the-output-from-git-diff

@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, ...

It is in the format @@ from-file-range to-file-range @@ [header]. The from-file-range is in the form -<start line>,<number of lines shown>, and to-file-range is +<start line>,<number of lines shown>. Both start-line and number-of-lines refer to position and length of hunk in preimage and postimage, respectively. If number-of-lines not shown it means that it is 0.

The num-of-lines in the preimage can mean the number of context lines. This is the case when the change is insert.

The num-of-lines in the postimage includes context lines too.

git | binary search4 some changes ] a given file(s)

git diff <commit> path/to/file

My shell function below can quickly show diff between a given past commit to the current tip.

You need to hardcode the filename(s) under investigation.

rollback() { # roll back to a given commit
  target=$1 #some git commit id                                                                
  pushd /N/repos/MI_EPA/                                                                       
  git diff head --name-status                                                                  
  set -x                                                                                       
  git checkout $target -- tbl/Generic/ReplaceOrder.tbl # bin/javagen.pl #tbl/MSF/NewOrder.tbl  
  git diff HEAD --name-status # git diff alone won't work    
 
  # above command should reveal only the named files changed.
                                  
  git diff HEAD                                                                                
  set +x                                                                                       
  popd                                                                                         
}                                                                                              

— to sort git tags by date: https://bintanvictor.wordpress.com/wp-admin/post.php?post=36920&action=edit&classic-editor

git | merge-commits and pull-requests

Key question — Q1: which commit would have multiple parents?

— scenario 1a:

  1. Suppose your feature branch brA has a commit hash1 at its tip; and master branch has tip at hashJJ, which is the parent of hash1
  2. Then you decide to simply q[ git merge brA ] into master

In this simple scenario, your merge is a fast-forward merge. The updated master would now show hash1 at the tip, whose only parent is hashJJ.

A1: No commit would have multiple parents. Simple result. This is the default behavior of git-merge.

Note this scenario is similar to https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-merges#rebase-and-merge-your-pull-request-commits

However, github or bit-bucket pull-request flow don’t support it exactly.

— scenario 1b:

Instead of simple git-merge, what about pull request? A pull-request uses q[ git merge –no-ff brA ] which (I think) unconditionally creates a merge-commit hashMM on maser.

A1: now hashMM has two parents. In fact, git-log shows hashMM as a “Merge” with two parent commits.

Result is unnecessarily complex. Therefore, in such simple scenarios, it’s better to use git-merge rather than pull request.

https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-merges explains the details.

— Scenario 2: What if ( master’s tip ) hashJJ is Not parent of hash1?

Now maser and brA have diverged. I think you can’t avoid a merge commit hashMM.

A1: hashMM

— Scenario 3: continue from Scenario 1b or Scenario2.

3. Then you commit on brA again , creating hash2.

Q: What’s the parent node of hash2?
A: I think git actually shows hash1 as the parent, not hashMM !

Q: is hashMM on brA at all?
A: I don’t think so but some graphical tools might show hashMM as a commit on brA.

I think now master branch shows  hashMM having two parents (hash1+hashMM), and brA shows hash1 -> hash2.

I guess that if after the 3-way-merge, you immediately re-create (or reset) brA from master, then hash2’s parent would be hashMM.


Note

  • direct-commit on master is implicitly fast-forward, but merge can be fast-forward or non-fast-forward.
  • fast-forward merge can be replaced by a rebase as in Scenario 1a. Result is same as direct-commit.
  • fast-forward merge-commit (Scenario 1b) and 3way merge (Scenario 2) both create a merge-commit.
  • git-pull includes a git-merge without –no-ff

git | reword historical commit msg

Warning — may not work if there’s a recent merge-in on your branch

Find the target commit and its immediate parent commit.

git rebase -i the.parent.commit

First commit in the list would be your target commit. Use ‘r’ for the target commit and don’t change other commits. You will land in vim to edit the original bad commit msg. Once you save and quit vim, the rebase will complete, usually without error.

Now you can reword subsequent commit messages.

git | tag | (commit^tagger)date

A git tag can have two dates (and author, and msg)

  • the tag-creation date i.e. tagger date (and tagger name, tagger msg)
  • date (and author and msg) of the commit referenced by the tag
    • this is always earlier than the tagger date

A lightweight tag has no tagger date (or tagger name or msg)

When you run ‘git show myTag’ you will see the tagger date ( and tagger name + msg) only if the tag is an annotated (a.k.a heavyweight) tag.git

— sort by date

git tag –sort=-taggerdate # may not work well for lightweight tags

git log --tags --simplify-by-decoration --pretty="format:%ci | %d" |sort -r

git | commit – -am # risky

Consider the harmless-looking command git commit –am

Most of the time, I rely on this command as a safe way to amend commit message without touching the original files in the commit.

However, if there’s further changes on file1 in staging, then this command would silently introduce this change into the commit. Once you finalize the commit message and save it, this change inadvertently becomes part of the commit !

git | beyondCompare

— bcomp advantage over git-diff
bcomp shows byte count in new vs old versions… can detect intrafile cut-paste

— based on http://www.scootersoftware.com/support.php?zz=kb_vcs#gitwindows

git config --global diff.tool bc
git config --global difftool.bc.path /c/Progra~1/BeyondCompare4/BComp.exe
git config --global difftool.prompt false

The above sets up git-difftool but how about git-diff? I feel one of the two is sufficient.

git config --global merge.tool bc
git config --global mergetool.bc.path /c/Progra~1/Beyond~1/BComp.exe

— no integration with git-diff?
Think again. If your git-diff always uses a GUI, then you can’t get the console UI which is frequently valuable. Therefore, it’s a good thing that you can’t modify git-diff.

http://www.scootersoftware.com/support.php?zz=kb_vcs#gitwindows worked for me, including a 3-way merge GUI —

git mergetool feature/my_br6 origin/feature/my_br6 -- buildChoops.sh

 

git | list all(and only)commits from branching point to a descendant commit

Background — Suppose you made 3 commits on your feature branch name “parix”, but meanwhile, someone added 4 commits in master branch. Therefore there is now a divergence in the commit history graph.

Often times you need to visualize the divergence. You need to exactly what 3 commits are on your branch after the common ancestor.

git log master..pairx # listing the 3 additional commits in pairx branch, right after the common ancestor i.e. the branching point

git log pairx..master # to show those 4 commits.

git | merge conflict #personal tips

I prefer cherry-pick.

Often the merge/rebase/cherry-pick/stash-pop operation would silently put some of the changes in _staging_, therefore git-diff would fail to show them. I have to use git-diff-HEAD instead:(

–If you have no choice then here’s the standard procedure

  1. git rebase master
  2. you get a merge conflict and file1 now contains <<<< ===
  3. vi file1 # to remove the bad stuff
  4. git add file1 # on the unnamed branch
  5. # no commit needed
  6. git rebase –continue # will bring you back to your feature branch

 

git | stash survival guide

Two simple steps:

git stash #no args

# Now git diff will no longer show the uncommitted changes:) …later

git stash pop

# Now git diff shows the original uncommitted changes

If git-stash-pop hits conflict on 1 file out of 3

  • the one file content will show >>>>. I will leave it alone for now
  • the other 2 files would show the original uncommitted changes. git-diff-HEAD would show these changes.
  • now I can use git-reset-HEAD and git-diff

git | fork auto-sync

https://confluence.atlassian.com/bitbucketserver/keeping-forks-synchronized-776639961.html says

Fork syncing helps you to keep your fork in Bitbucket Server up-to-date with changes in the upstream repository. Bitbucket Server can do this automatically for all branches (and tags) you haven’t modified in the fork.

I guess forking (and pull request) is not a feature of GIT per se but a feature of Bitbucket and GitHUB server. I think the server get notified upon a new commit and runs fast-forward git pull.

git | rebasing: basics

For me, rebase is the procedure to re-sequence commits… A simple form of history rewrite.

git pull --rebase # moves your local commits to the end of the linked list
How do I merge multiple Work-In-Progress commits into a single commit?

The following will take the last five commits and give you an interactive session that allows you to (optionally) merge them.

git rebase -i HEAD~4

More details are here http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html

git | diff | tips #investigating merges

git diff –help # manpage

git diff –color-words # good

git diff -U22 # 22 lines of context

3 forms of diff — See 3trees@git

— diff of a merge-conflict file is different.

I think it is a 3-way diff. Output shows current “merged” file vs orig1 and orig2. https://git-scm.com/docs/git-diff#_combined_diff_format explains “++”

— if you see a line showing up due to nothing but white space change, you can usually use “-w” to suppress it but sometimes you need to pinpoint the white space change:

git diff |cat -vet

— on linux, git-diff output seems to truncate long lines

https://stackoverflow.com/questions/136178/git-diff-handling-long-lines shows

git config core.pager 'less -r' 

git | reverting a file: various ways

I didn’t like git-revert as it complicates history. See https://www.atlassian.com/git/tutorials/undoing-changes/git-revert

How do I revert a file to remove bad commits?

git checkout HEAD -- <file or directory>

This use of git-checkout is unrelated (as far as I know) to the commit-level checkout. This solution is officially /sanctioned/ … Some git commands would suggest …

(use "git checkout -- <file>..." to discard changes in working directory)

How to Revert non-commited changes

git reset --hard HEAD  # reverts tracked files back to the head
git clean -fxd .   # deletes untracked files in current dir. You may want to cd to the repository base first

[Victor] My vote for best explanation of git-reset goes to https://git-scm.com/blog/2011/07/11/reset.html. Detailed, but more readable than the manual. Many examples are useful to me.

Warning — if you want to temporarily play with an older version of file1.java, then be careful. Make sure you save the current tip of your branch. If you git-reset–hard then you may lose that tip!

How to Revert branches back to origin

git fetch origin
git reset --hard origin/yourBranch

git | cherry-pick commits to push to main branch

How to Cherry-Pick Commits to Push to main ‘develop’ branch

Say you are working on a feature branch with other people, with you all making commits and pushing to the remote replica of this branch. At some point you will want your commits merged with develop/production. Git does provide a method of choosing which commits you would like to push. The easiest way to explain this is with an example. We assume that all the following commands are run in a command line tool.

git checkout feature/commodity
git log -3 --pretty=oneline #look at the 3 most recent commits to the repository (includes all commits to all branches - in a nicely displayed format)
$ git log -3

Looking at this log, I want to push only the latest change to ‘develop’ branch (via a pull request), the commit associated with the e5a67d7088622dd8821dfdd8e2fafa2dc938b75c hash number. Keeping a record of this hash number I then create a new feature branch from ‘develop’ branch in Stash. Assume this new branch is called feature/cherry_pick

git fetch #update local repo to get this new branch info
git checkout feature/cherry_pick #switch to this new branch, at the moment is exactly the same as origin/develop
git cherry-pick e5a67d7088622dd8821dfdd8e2fafa2dc938b75c #where the hash number is the commit I actually want to merge into 'develop'
git commit #commit this change to the local feature/cherry_pick 
git push #push this change to the remote feature/cherry_pick branch

You can now create a pull request in Stash to merge this cherry-picked commit into ‘develop’

git | diff | utils #two-repo

–Method: git-diff command

This is a basic but versatile tool. It looks at two commits you provide. Note each tag represents a commit. Each branch name also represents the commit at the tip of the branch.

–Method: have two local repos (clones) to check out different branches

Figure out how to clone two physical repos. After that you could use many conventional diff tools.
In 2019 again this proved 3 times more effective than all alternatives. I was able to check out two clones of the same repo, roll back one of them to an arbitrary commit (possibly on a branch) and use my favorite diff tool to slice and dice the large number of code changes. BeyondCompare is my favorite but it is sadly not free.

–Method: download remote version as complete file, then diff that against local file

Here’s my earlier favorite choice to diff a remote branch version vs a local branch version:

mv buildChoops.sh buildChoops.local
git checkout origin/feature/my_br6 buildChoops.sh # download the file from the remote branch into the current directory locally
... now run any diff tool between the two files

–Method: bitbucket/github comparison between branches/tags

Stash supports a directional PR diff, distinct from the git-diff command. Based on the pull-request concept, Stash treats your two commits as a FROM and a TO and shows the sequence of commits between them, as if in a pull-request.
If you click the arrow to reverse the direction, then Stash will always show a different diff result!
  • If from A to B there’s a fast forward merge, then Stash PR diff from B to A would show zero commit, since such a pull request would be empty.
  • If between A and B there’s no fast-forward merge, this PR-diff would show fewer commits than git-diff.
In summary, PR-diff is directional; git-diff is directionless. This directional diff feature is kind of unique to Stash and therefore valuable.

–Method: git diff using Beyond Compare 3

See more details in separate blogpost git+beyondCompare

–Method: git diff using Winmerge

see http://stackoverflow.com/questions/1881594/use-winmerge-inside-of-git-to-file-diff, updated June 2015

I hope there are similar solutions for other diff GUI tools.

tidy up messy commits, for pull request

Git – tidy up messy commits, for pull request

A common situation: you make many incremental, quick and dirty commits on your branch, undoing and redoing changes. You don’t need to push the entire history to a shared branch like Develop.

Many solutions exist. Git offers many commands to rewrite history on a branch. Many git users use them regularly. We could use them to clean up the commits on a feature branch before pulling them into a pull request. Here are some of the methods.

This will rewrite the history of your branch, you should only do this if no one is sharing your branch (or you don’t care about them).

1st approach – rebase, if your history is clean

This example will let you interactively select from the last four commits.

git rebase -i HEAD~4
# Alternatively, specify the last-good commit:
git rebase -i e8a9cf093   # Rebase all subsequent commits

2nd approach – If your history is too complicated (many merges etc.)

git tag backup                      # we do this so that we don't lose our history
git checkout -B temp origin/develop # checkout a clean temporary branch from develop
git merge --squash <branch>         # merge our old branch in, squishing all the commits
git commit
git branch -f <branch> temp         # force the branch to point at the new commit

3rd approach – git reset and commit again

Suppose you have 5 commits to squash,

git reset --soft <a previous commit hash, or something like HEAD^5> # rollback to the named commit.
# reset --soft modifies only commit history, not work tree or index.
# Now you can look at the files involved in those 5 commits, staged for the next commit
git diff --cached --name-status
git commit -m 'your msg'
# git push ... and create pull request

All methods so far assume the complicated commit history is in local repo not on a Stash branch. If your Stash branch has stuff you want to pull into Develop, but your Stash branch has complicated history, then …

4th approach – create a pull-request candidate branch

# create the candidate_branch from Develop, on stash
git tag backup                      # we do this so that we don't lose our history
git fetch                           # will show the new branch
git checkout candidate_branch
git merge --squash old_branch       # merge our old branch in, squashing all the commits
git commit                          # the default msg shows list of files. you might want to uncomment that in the msg.
git push origin candidate_branch    # now the candidate branch is up-to-date on Stash. Ready for pull request.

5th approach – rewrite public history

Dangerous – Know what you are doing
# clean up your local branch as illustrated above
git push --force origin   your_Stash_branch    # overwrite the Stash branch and overwrite public history - Dangerous!

6th approach – edit commit message after many merges, using git-filter-branch

The git-rebase and git-cherry-pick commands often fail in the presence of a complicated history of merges, file deletes etc. Try filter-branch.

Dangerous – can modify many commit messages by accident
# clean up the branch xyz
git checkout -b xyzRewrite # Always do the rewrite on a cloned branch please
git filter-branch --force --msg-filter 'sed "s/addYENRates/[CFMQUANT-262] addYENRates/" ' e635d034ea8..HEAD

At end of the command, 1st hash is the parent of the flawed commit. 2nd hash must be HEAD (or perhaps the name of a branch).

Command should tell you the branch is rewritten. If no commit message matches the pattern, then the command would tell you the branch is left unchanged, so it’s safe if you have a typo that matches nothing.

In either case, you can then run git diff against the original branch and verify they are identical content-wise.

In this real example we were lucky the message “addYENRates” was rather unique. If it shows up on 2 commits, then both would be rewritten.

bitBucket: investigate history of merges

Scenario: throughout the month, your main branch takes in many pull-requests. You want to see the sequence of merge points in chronological order.

On BitBucket, the commit listing is chronological but doesn’t show the merge commits. I usually unhide the merge commits, so on my page the merge commits are interleaved with “regular” commits in chronological order like

  • merg3
  • merge2
  • commit2c
  • merge1
  • commit1b
  • commit2b
  • commit 2a
  • commit1a
  • commit3a

The regular commits line up according to their commit timestamps. the most recent merge could have its one and only commit created months ago !

What I want is “nothing but the merge points”. I think I have to ignore the “regular” commits and only look at the merge commits.

git | tag with annotation

I always want a long description for my git branch. Not sure how best to do it. https://stackoverflow.com/questions/4750845/how-do-you-annotate-a-branch has some suggestions.

— Git tag can have an annotation:

git tag -a newTag some_commit #create
git show newTag #reveal the annotation, creation time etc
git tag -d newTag #deletes the tag

Two ways to edit annotation:

  1. We can remove and re-tag with a modified annotation.
  2. git tag <tag name> <tag name> -f -a

git | diff | highlights trailing space

By default there’s a default-enabled highlight (in red) of trailing spaces. I encounter it all the time when running git-diff. Extremely distracting.

$ git config core.whitespace  trailing-space # the dash before “trailing” disables it

Beware: when you copy-paste into a command window, the dash may become something else 😦

If successful, this command adds a line into .git/config like

whitespace = -trailing-space

You can remove this line to restore the “factory default”.

git | backup b4 history rewrite

Personal best practice. Git History rewrite is not always no-brainer and riskless. Once in 100 times it can become nasty.

It should Never affect file content, so at end of the rewrite we need to diff against a before-image to confirm no change.

The dumbest (and most foolproof) before-image is a zip of entire directory but here’s a lighter alternative:

  1. git branch b4rewrite/foo
  2. git reset or rebase or cherry-pick
  3. git diff b4rewrite/foo

Note the branch name can be long but always explicit so I can delete it later without doubt.

github tips #email

  • q(git config credential.helper store) is the command that finally fixed my git-bash forgetting-password problem on my win7 Dell. The github “recommended” command only worked in my win10 Lenovo and office win7:
    • git config --global credential.helper wincred 
      
  • On Linux, I only needed to run a simple command to cache my credentials
$ git config credential.helper store
$ git push 
Username for 'https://github.com': <USERNAME>
Password for 'https://USERNAME@github.com': <PASSWORD>
  • to comply with email privacy,
    • git config --global user.email {ID}+{username}@users.noreply.github.com
      You can find it in https://github.com/settings/emails
  • –to download an entire branch on the github web interface: CloneOrDownload button can download a zipfile
  • –to mass upload using drag-n-drop
  • I realize that the zipfile downloaded has a problem. When I open the zipfile (but not extract) and select the files, the drag-n-drop interface fails. I had to copy the files to a tmp directory.
  • –delete folder: not so easy
  • –file naming tips
    • ^ is slightly less ideal
    • I would use underscore as default and use dash sparingly (like 0-N). Camel case is even better.
  • –rename file @web interface is easy
  • –rename branch: no web interface. I had to …
    • checkout branch locally,
    • rename branch
    • commit
    • push, which triggered a login pop-up.
  • –rename folder: no web interface. I had to
    • git mv dir1 com/xx/dir1 # close MSWE if you get perm error
    • git commit
    • git push

git | origin/branch1 ^ remote/origin/branch1

In q[ git branch -a ], we see

remote/origin/branch1
remote/origin/br2

I verified that

q[ git log remote/origin/br1 ] and q[ git log origin/br1 ] are identical

–To delete a local tracking branch without deleting the remote branch:

git branch -rd origin/br2

— my incomplete understanding:

There can be many remotes in a git repo. Each remote is “pointer” to a peer repo at some URL, identified by a nickname. The familiar “origin” is the default nickname of the remote you cloned from.

q[ git remote -v ] shows the URL and nickname of each remote.

github branch backup

I plan to use bighub branches for backup:

  1. pick a single most valuable directory, like cpp (or py)
  2. once a while merge it into another branch like perl1 or bash
    • Watch out for any deletion of a directory like cppProj. You may need to merge the cppProj branch

Note:

  • Do NOT rely on this back-up. It’s a low-cost back-up with unknown reliability
  • more frequent back-up doesn’t hurt and should be quick and simple

git | %%tips { Macq

–to recreate current feature branch br2 as a clone of br3
The slower method is ” git checkout br3; git branch -d br2; git checkout -b br2″
The slightly faster method is “git reset –hard br3”

–Most git commands accept –help

  • Short Questions
    • How to rename a branch on Stash directly?
    • How to add a tag on Stash directly?
    • How to view the commit and annotation on one or multiple git tags?
    • How to add or edit the annotation of a tag on Stash?
    • How do I clean my external files?
    • How do I revert a file?
    • How do I get my local branch back to a remote?
    • Why is `git status` slow in Linux?
    • How do I list all branches that change a file?
    • How do I delete a local branch?
    • How do I merge multiple WIP commits into a single commit?
    • How do I get rid of obsolete remote branches
    • How to resolve pull request merge conflicts
    • A few ways to create new feature branch
      • Using command line without Stash
    • How to Revert non-commited changes
    • How to Revert branches back to origin
    • How to List names of modified files only
    • How to Cherry-Pick Commits to Push to develop
  • Using TortoiseGit
    • Overview
    • Some of the many convenience features
      • Feature: list of all uncommitted files (like git commit –name-status)
    • Installation
    • Using TortoiseGit
      • Setting up the Key
      • Clone the Stash repository (Workspace)
      • Interface 
      •  Commands
      • Enable icons in Win Explorer

Short Questions

How to rename a branch on Stash directly?

On your Stash page change base branch to the target branch (say BB) -> click on the “…” dropdown -> choose “Create a branch from here” -> specify new name (say NN) -> delete branch BB.

How to add a tag on Stash directly?

Navigate to the page of your chosen commit (such as this sample page), then find the link on the top right “Tag this commit”

How to view the commit and annotation on one or multiple git tags?

git show your_tag # look into one tag

The Stash page below also helps, provided the tag is published on Stash.

How to add or edit the annotation of a tag on Stash?

…  shows all tags, including the commit and the annotation. Choose one you want to edit and go to Actions column click the 3 dots and choose Edit.

Why is `git status` slow in Linux?

It might be due to the networked file system. We’ve found that doing the following might speed it up:

git config --global core.preloadindex True

See stackoverflow for details.

How do I list all branches that change a file?

FILENAME=<myfile>
git log --all --format=%H $FILENAME | while read f; do git branch --contains $f; done | sort -u

How do I get rid of obsolete remote branches

First remove it from Stash. Easy and safe to do on Stash web site. Go to the page listing all mod branches. Locate your branch you don’t want. Click on the “…” on the far right, to show the dropdown and find “Delete”. This would complete the deletion on the central repo. Next in your local repo, run

git fetch --prune

This command would sync up the local repo to match central repo on Stash. The deleted branch will now be deleted locally. Without this step, git branch -a would forever show the obsolete branch.

Remember each local repo or central repo holds a copy of all the branches. Deleting branch123 from one repo doesn’t automatically delete branch123 from another repo. Similarly, adding branch321 to one repo doesn’t automatically add branch321 to another repo.

How to resolve pull request merge conflicts

If possible, we use the Stash interface to merge. The resulting commit message on Develop branch looks like

Merge pull request #151 in CFMQUANT/mod from bugfix/tar to develop 

Merge pull request #147 in CFMQUANT/mod from feature/CFMQUANT-253 to develop 

Merge pull request #135 in CFMQUANT/mod from feature/CFMQUANT-243 to develop

Sometimes we get a conflict when merging the PR. For example, PR #153 has one file modified in the incoming branch release/0142c. Same file was very recently modified on Develop. Therefore this file had 2 concurrent changes, resulting in a merge conflict. This is what Victor did to resolve it, not necessarily optimal or recommended.

 

git fetch origin release/0142c
git branch -d develop
git checkout develop                    # check out latest from Stash
git merge FETCH_HEAD                    # one file in conflict, to be resolved
cd models/PyQXLModelsLib/ddl/
vi PyQXLModels_ddl_pycxx.cfg
git diff origin/develop PyQXLModels_ddl_pycxx.cfg    # ensure the diff is exactly what we expect
git add PyQXLModels_ddl_pycxx.cfg
git commit # see the comment message below
git log --simplify-merges               # verify
git push origin HEAD                    # may need special permission to push Develop to Stash

Here’s the commit message I used in the merge commit:

Merge pull request #153 manually from release/0142c to develop
[CFMQUANT-235] resolve minor conflict on one file

 

 

Using TortoiseGit

Overview

Another option for a visual interface for git under Windows is TortoiseGit.  This isn’t a GUI, as such, but a shell that sits on top of Windows Explorer.  Some people like it for this reason; some people dislike it for this reason.

It gives a convenient way to see the status of your files, and a convenient way (right-clicking) to select most commonly-used git commands (you can of course still use the command line whenever that is more convenient).  There is some pain involved in setting up the key for connecting to the server – scroll down to ‘Interface’ and ‘Commands’ below to see if you think the gain is worth the pain.

Git command line is rather rich and powerful, featuring hundreds (possibly thousands:) of commands + sub-commands + combinations of switches to those commands. (https://www.kernel.org/pub/software/scm/git/docs/ lists more than 100 top-level commands). It’s impractical for any GUI tool to emulate the command line. Tortoise provides a good information-radiator tool, that saves you lots of repetitive typing.

Some of the many convenience features

Convenience is where Tortise distinguishes itself from other software tools.

In WinExplorer, Tortise lets you right click any modified file (or multiple files) to commit to your local branch. After the commit, the dialog screen lets you push to the central branch on Stash.

Similarly, if you have a new file to commit, you can right click and add, then commit.

You can also right click and diff with previous version.

Feature: list of all uncommitted files (like git commit –name-status)

You can show in a window a list of all files modified locally but uncommitted. You can then double-click each file to pop-up a diff screen, such as BeyondCompare. You can click to select one or more of these files to commit. For me this is the most convenient way to keep track of a large number of code changes. Same feature exists in TortiseSVN.

You can limit the scope to one directory (AgModels for eg). Within the same screen, you get a checkbox to widen the scope to entire repo.

There’s another checkbox to include unversioned (i.e. newly created) files. You can then add them to git with very few clicks.

Installation

TortoiseGit is free, and can be downloaded from http://tortoisegit.org/download/  You’ll need to download the 64 bit version, and then run it (it will prompt for an x account password during the installation).  It does try to restart a lot of programs (so make sure you’ve saved everything), but doesn’t require a restart.  The default options in the install wizard worked fine for me.

Using TortoiseGit

Setting up the Key

For some reason, TortoiseGit only accepts keys in putty key format, so the key that works with Git needs to be converted to this different form (this should be a once-off).  An explanation of how is given here: http://develop-for-fun.blogspot.com.au/2013/12/configure-tortoise-git.html (basically you need to download puttygen.exe to convert the format of the key).

Clone the Stash repository (Workspace)

To create a new repository based on the stash repository, you can use Windows Explorer to create a new folder, move into that folder and then right click, and choose “Git Clone…” to bring up a dialog box.

Set the url to ssh://git@….. the directory to where you want it installed.  You’ll need to click on “Load Putty Key” and put the address of the putty key saved down from puttygen earlier, which could be any stable folder in your C: drive. Avoid moving this folder.

Interface

Once successfully installed, Windows Explorer in folders associated with a workspace will now look something like

  • A green circle with a tick: up-to-date. (Victor: based on my earlier TortoiseSVN knowledge, a folder marked green means everything in it is in-sync with remote, a valuable knowledge.)
  • A red circle with an exclamation mark: file on disk has been changed and not committed (or for a folder, a file somewhere in that folder – possibly several folders deep – has been changed).
  • A blue circle with a question mark: “external” ie have not been added to the list of committed files
  • A yellow triangle with an exclamation mark: “conflicted” ie the equivalent of “overlap” in AccuRev

A full list of icons is given at https://tortoisegit.org/docs/tortoisegit/tgit-dug.html#tgit-dug-general-dia-icons

 Commands

Right clicking on a file or folder brings up a menu of git commands.  Right clicking at the top of the directory tree means you can commit all changes, or click on merge to bring up a GUI interface to merge everything.

The full manual can be found at https://tortoisegit.org/docs/tortoisegit/

If you are using a workspace that you set up outside of TortoiseGit (e.g. via the command line), then before you push to, or pull from, the server, you will need to point TortoiseGit to the putty key, by selecting TortoiseGit->Settings, then under “Git”, select “Remote”, then click on “origin” in the “Remote” box, and then set Putty Key.

Enable icons in Win Explorer

Victor had to fix his registry to get Windows Explorer to show the (nice little) icons. In addition to http://martinbuberl.com/blog/tortoisegit-icons-not-showing-workaround/ , he had to kill all Explorer.exe processes in taskmgr before relaunching Explorer.

Explorer “overlay” (technical jargon) is no mature and stable technology. Victor estimates a few times a year the icons would disappear on (subset of) the version-controlled files, often for no obvious reason, and then reappear at a random time. In spite of frequent disappointments, many users love these little icons.

git-bash | diff on win-word

beyond-compare… I haven’t configured it successfully.

–Pandoc

  • Pandoc shows word differences, whereas default shows entire paragraph even for a one-word change
  • minor imperfections in the display of words near table boundaries

I added this chunk into my .gitconfig file:

 [diff "pandoc"]
   textconv=pandoc --to=markdown
   prompt = false
 [alias]
   wdiff = diff --word-diff=color --unified=1

git | 3trees #reset

Three trees are 1) workTree 2)staging i.e. Index 3)HEAD i.e. the current branch

https://www.atlassian.com/git/tutorials/undoing-changes/git-reset is my first and most memorable introduction on the three trees of git, in the context of git-reset.

—– A (code) change can be in three states or statuses (https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F)

  • 1) an unstaged change is only in local tree, easily lost 😦
  • 2) a staged change. It exists in staging and workTree
  • Note “uncommitted” can mean (1) or (2) !
  • 3) a committed change is saved in the branch + staging + workTree

To transform an unstaged change to a staged change, you use git-add. For an entire file (rather than a code change), you “mark” it as staged using git-rm, git-mv or git-add. Some “undercover” operations also put a change into staging. So git-diff won’t show it, but git-diff-HEAD would.

To transform unstaged or staged change to a committed change, you use git-commit

I often forget a change is in staged-uncommitted. The bare git-diff won’t reveal it.

—- paint analog

A code change is like a color paint. We paint new color on tree1, tree2, then tree3.

git-reset-mixed — rollback the paint from tree3 then from tree2, leaving the new paint only on tree1 i.e. workTree. In contrast, git-reset-hard is removing the paint in all 3 trees

git-diff compares tree1 vs tree2.

—- git reset is the main focus of this blogpost

In general, a particular (code)change goes through two transitions between the three “trees”:

  • from local tree -> staging
  • from staging -> branch

To undo uncommitted (either unstaged or staged) changes, git-reset comes in three strengths:

  1. mixed: reset the staging tree. by default git-reset means q(git reset –mixed HEAD) and updates the HEAD ref + staging
    • transform a staged or committed change to become an unstaged workTree change
  2. hard: wipe out all changes by resetting workTree, staging tree i.e. all trees
    • transform any committed, staged or unstaged workTree changes to … no change.
    • “reset –hard” is the one dangerous reset. Without it, workTree won’t change.

Note the “soft” strength is irrelevant to our goal. Only used to undo a commit, to rollback to an earlier commit.

—-git diff

http://blog.osteele.com/2008/05/my-git-workflow/ has a nice flow chart:

$ git diff HEAD # compares workTree vs HEAD
$ git diff – -staged # compares staging vs HEAD, only needed to investigate the (mysterious) staging
$ git diff # by default compares workTree vs staging, but won’t show new files added to staging 😦

Q: How do you remember the last (unsticky) point?
A: Recall that when you do a git-add on an updated file, your update gets copied from workTree into staging. Thereafter the change is no longer flagged by the bare git-diff which compares workTree vs staging.

git | remote update

I had a strange problem

  1. I cloned a repo and had only one branch
  2. I know there’s a remote branch rb3, but
  3. git checkout rb3 # failed with “error: pathspec ‘rb3’ did not match any file(s) known to git.”

To investigate, I ran

  1. git branch -a # shows no trace of rb3
  2. git remote update
  3. git branch -a # now shows remotes/origin/rb3

I now think the problem was — I have a dir named ‘rb3’  !

git | manage feature branch #2delete

A few ways to create new feature branch

If you want the branch to show up in Stash, then it is probably simpler to create it on Stash website (choose the correct parent branch …). After that

git fetch # no arg after "fetch"... will show the new branch downloaded to local repository as "remote branch"
git branch -a # list all (local and remote) branches
git checkout feature/new_br_from_stash # creates a matching local branch

Using command line without Stash

git branch feature/my_br # creates a brand-newbranch named feature/my_br, in the local repo only.
git branch # lists all available branches in local repo
git checkout feature/my_br # selects my_br as the default branch.
git branch -m feature/my_BR # renames the default branch to feature/my_BR, in the local repo only.
git push origin feature/my_BR # creates and copies branch to Stash, assuming branch doesn't exist there.

(Note Git branches are designed to be disposable. So in theory you could create a branch for a small change and then merge and throw it away. No best practice no recommendatation yet.)

All feature branches, including temporary throw-away branches, can be deleted from Stash.

git | branch sync – learning notes

Branching is different in git vs svn/cvs. By itself git branching isn’t too complex.

The git local/remote set-up is new to me. By itself, not too complex.

However, in reality we have to manage the combination of branching and sync – a higher level of complexity addressed in many online discussions. I would prefer to rely on the git commands (rather than GUI tools or Stash) to understand and manage it.

I like the philosophy in http://longair.net/blog/2009/04/16/git-fetch-and-merge/

–comparing the local branch vs remote branch

git diff feature/my_br6 origin/feature/my_br6 — path/to/your/file

–to see the mapping between local ^ remote branches

git branch -vv

— FETCH_HEAD:

http://stackoverflow.com/questions/1800783/compare-local-git-branch-with-remote-branch

–check out a remote branch? local branch to track remote branch?

http://makandracards.com/makandra/521-check-out-a-remote-branch-in-git

http://stackoverflow.com/questions/1783405/checkout-remote-git-branch

–delete a branch locally, then remotely:

http://gitready.com/beginner/2009/02/02/push-and-delete-branches.html