git¶
git pull force — [1]¶
This will:
- get rid of all local uncommited changes
- get rid of all local commits
- and bring your local branch in sync with remote
Sign & Verify commits¶
Official git docs and github docs
Signing commits¶
- Tell git to sign commits:
git config commit.gpgsign true # --global
- Specify what key to use by default:
- Choose the fingerprint of an
S
key:gpg --list-secret-keys --keyid-format=long
git config user.signingkey 0A46826A! # --global
- Choose the fingerprint of an
- When committing add
-S
:git commit -S -m "Test signing"
- You might want to add gpg key to github, export it via:
gpg --export --armour --fingerprint ...
Verification¶
- During pull:
git pull --verify-signatures
- Make it the default:
git config merge.verifySignatures true # --global
- Make it the default:
- In log:
git log --show-signature
- Make it the default:
git config log.showSignature true # --global
- Make it the default:
grep history¶
Look for the given regex: git log -G "#include <sys/ustat.h>"
Look at the number of occurrences: git log -S "#include <sys/ustat.h>"
Multiple branches simultaneously (in a single repo)¶
git worktree
makes is possible to materialise multiple branches simultaneously
and work with them as usual in a single git repo:
- Clone repo with master going to a nested directory:
mkdir ~/devel/ybd && cd ~/devel/ybd && git clone git@bitbucket.org:yellowbrickdata/ybd.git master --separate-git-dir=.git
- Checkout worktree that you need (repeat this as many times as you need):
git worktree add cloud_native
Operations:
-
add worktree:
git worktree add <path> <commit-ish>
If commit-ish is a branch name is has not yet been checked out locally, then git will create a tracking branch for you.
-
add worktree:
git worktree add --track -b DS_CN_YBD-22481-GCP-1 objst origin/DS_CN_YBD-22481-GCP-1
Use this if you want to create a directory
objst
that has a branch which tracks remote branch. -
list worktrees:
git worktree list
- move worktree from one directory to another:
git worktree move
- remove worktree:
git worktree remove
- repair
Common git config¶
- Prune deleted branches automatically:
git config --global fetch.prune true
- Merge strategy:
git config pull.rebase true
Make git use specific ssh key¶
ssh-keygen -t ed25519 -f ~/asdf/priv-key
set -x GIT_SSH_COMMAND "ssh -i $HOME/asdf/priv-key -o IdentitiesOnly=yes"
Submodules cheat-sheet¶
Principles¶
There are two states:
- the state associated with git repo that is a submodule
- and therefore you need to do all usual things with it, such as adding files, committing, pushing and so on…
- and the state stored in the parent repo, that describes submodules (this is in addition to usual git repo state and operations, the parent repo also includes list of submodules, urls from which they have been clones, branches they track, SHA “pointers” to commits of the branches, all of which you have to commit/pull/push).
git submodule sync:
Another non-obvious thing is that there are two places where repo URLs are kept: .gitmodules
and <repo>/.git/config
.
If someone updates and URL of a repo, you get the changes in .gitmodules
via git pull. But <repo>/.git/config
is not updated. This is why you need to use git submodule sync --recursive
(after git pull
).
Detached HEAD:
Git “mostly” knows only about specific SHA of submodules. It does not (much) about branches. While .gitmodules
(may) specify branch for a submodule, it is used only to know which branch from upstream to pull. In some sense,
it should have been named remote-branch
.
Specifically:
git submodule update --init
will clone leave submodules in a “detached HEAD” state.git submodule update
without the will checkout specific sha (leaving submodule in a “detached HEAD” state). Without--remote
, the sha will be taken from parent state, with--remote
, the sha will be taken from the branch specified in .gitmodules.
Broken push --recurse-submodules=on-demand
Note that git push --recurse-submodules=on-demand
is broken.
You would expect that git push
has the following invariant/contact: after successful push, (1) remote branch
(2) remote-tracking branch and (3) local branch should have same SHA.
This is NOT what git push --recurse-submodules=on-demand
does. The crux of the issue is that git submodules
does not know anything about branches. What git push --recurse-submodules=on-demand
does is check if SHA of
the head of the local branch exists somewhere on remote server. If it exists, it does nothing (regardless
of the states of remote branch and local branch). In other words, it does not advance local branch, as regular
git pull
does, because the required commit already exists somewhere on remote. If if does not exist, it pushes
the SHA.
Useful config¶
- Show statuses of submodules:
git config --global status.submodulesummary 1
- Fetch submodules in parallel:
git config --global submodule.fetchJobs 32
- Show diff of submodules:
git config --global diff.submodule diff
- Recurse to submodules (by default):
git config --global submodule.recurse true
- Push submodules before super-repo is pushed:
git config --global push.recurseSubmodules on-demand
Operations¶
-
Add a submodule in a parent repo
git submodule add -b branch_you_need git@github.com:DimanNe/result.git contrib/result/result git commit -m "Add result as a submodule"
-
Change tracking branch:
git submodule set-branch --branch master contrib/llvm-project
-
Remove a submodule from a repo (1)
-
This removes the filetree at
<path-to-submodule>
, and the submodule’s entry in the.gitmodules
file.Additionally, you might want to remove
.git
directory of the submodule frommodules/
:
-
-
-
Clone a repo with all submodules:
git clone --recurse-submodules git@github.com:DimanNe/scripts.git
-
or only selected submodules: (1)
-
here:
-
-
-
Show diff of submodules:
git diff --submodule=diff
(or use the config above) -
Checkout submodules too
git checkout --recurse-submodules
(or use the config above) -
Checkout all submodules to their correct versions
git submodule foreach --recursive git reset --hard # <--- Careful! git pull && git submodule sync --recursive && git submodule update --init --recursive
Note however, that the command above will leave all submodules in detached HEAD state. You need to checkout expected branches, and then
git pull
. -
Blacklist submodule (do not update it)
-
Update submodule from upstream:
- One:
git pull && git submodule sync --recursive && git submodule update --remote --merge contrib/result/result/
- All:
git pull && git submodule sync --recursive && git submodule update --remote --merge --recursive
- One:
-
Push
As described above, does not work. So, we have to use this:
git submodule foreach "git symbolic-ref --short HEAD >/dev/null 2>&1 && git push || true" && git push
Git Content Filter Driver¶
There is a way to comply with official company-wide code-style and work (locally) with the code formatted as you want.
The idea is based upon the fact that it is only important to push my code in central repo in the official format, while nobody prevents me from having and working with code that is formatted in my own way.
Luckily, git has native support for this - Git Content Filter Driver.
In .git/config
add¶
[filter "clangformat"]
smudge = .git/clang-formatter.sh .clang-format-my
clean = .git/clang-formatter.sh .clang-format-company
smudge
— the command that is called when you pull something (more precisely when git checks sources out onto your disk).clean
— the command that is called when you push something (more precisely when you add files in the git’s staging area)..clang-format-my
and.clang-format-company
— self-explanatoryclang-format
config files.-
.git/clang-formatter.sh
is a small script that invokesclang-format
. The main purpose of the script is to “prepare” value for-style
option (via awk):- the awk script below converts contents of
.clang-format-my
(or.clang-format-company
) into inline format:-style "{Option1: Value, ...}"
, because there is no way to specify filename with config toclang-format
- the awk script below converts contents of
-
do not forget to make the script executable:
chmod +x .git/clang-formatter.sh
In .git/info/
add attributes
:¶
More info and explanation…¶
…can be found here
Merge-related tricks¶
Ignore whitespaces¶
-Xignore-all-space
or -Xignore-space-change
.
Checkout their/our/base versions of a conflicted file¶
Then you can change them and finally merge: git merge-file -p hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb
.
Same as above but in-place:¶
NOTE:
- During rebase (
git rebase master
),--ours
correspond tomaster
.
Diff of the pending merge¶
- To compare your result to what you had in your branch before the merge, in other words,
to see what changes the merge is going to introduce:
git diff --ours
. - If we want to see how the result of the merge differed from what was on their side, you can run
git diff --theirs
.
Show our/base/their in conflict markers¶
Compare two diffs (range-diff)¶
After a cherry-pick or merge you can check difference of two diffs (1st diff corresponds to a commit
on the master
, 2nd diff corresponds to the merge commit or cherry-picked commit):
^!
notation represents single commit, but unlike just HEAD
(which also represents a single
commit), it denotes a range.
Diff output is also interesting (read more about it here):
- This shows us that
hola world
was in our side but not in the working copy, - that
hello mundo
was in their side but not in the working copy - and finally that
hola mundo
was not in either side but is now in the working copy.
This can be useful to review before committing the resolution.
In other words:
' -'
or' +'
represents what happened in respect to (from the point of view of) the 2nd range (removed / added lines in our branch).'- '
or'+ '
represents what happened in respect to (from the point of view of) the 1st range (removed / added lines in master branch - where we cherry-pick commit from).
In this mode, the diff of diffs will retain the original diff colors, and prefix the lines with -/+
markers
that have their background red or green, to make it more obvious that they describe how the diff itself changed.
// The second '-' shows us that both lines below were removed (the first line was removed from master's commit, the second line was removed from the branch's commit).
// However, the first line was removed from **diff** (first '-' in the first line), and replaced with the second line (first '+' in the second line).
-- MediumHash hashTable = {distinctRows, minThreadSafeBuckets()}; // in master we removed this
+- MediumHash& hashTable = *new MediumHash(distinctRows, minThreadSafeBuckets()); // but in the branch we removed this
// The second '+' shows us that both lines were added (the first line was added in the master's commit, the second line was added in the branch's commit).
// However, the first line was removed from **diff** (first '-' in the first line), and replaced with the second line (first '+' in the second line).
-+ MediumHash hashTable = {distinctRows, BuildPartitioner::minThreadSafeBuckets()}; // in master we added this
++ MediumHash& hashTable = *new MediumHash(distinctRows, BuildPartitioner::minThreadSafeBuckets()); // but in the branch we added this
Another example:
There was diff (second '-' and '+') in the branch's commit, but there is no diff between (1) master's
diff and (2) branch's diff (because we have space). Meaning that master has the same change.
- for (Row* row : packet) {
+ for(Row* row: packet) {
The second +/-
tells us what happened in one of the diffs (master’s commit or branch’s commit).
The first +/-
is from diff of the two diffs. If there is space, it means there is no diff (between the two diffs).
Default branch name¶
git-svn: skip revisions¶
Omit 1395620-1395673 revisions: