{"id":4530,"date":"2026-10-13T10:54:19","date_gmt":"2026-10-13T08:54:19","guid":{"rendered":"https:\/\/nguenkam.com\/blog\/?p=4530"},"modified":"2026-10-13T10:54:19","modified_gmt":"2026-10-13T08:54:19","slug":"git-tutorial-see-what-is-happening-and-avoid-push-conflicts","status":"publish","type":"post","link":"https:\/\/nguenkam.com\/blog\/index.php\/2026\/10\/13\/git-tutorial-see-what-is-happening-and-avoid-push-conflicts\/","title":{"rendered":"Git Tutorial: See What Is Happening and Avoid Push Conflicts"},"content":{"rendered":"\n<p>Git gives you many commands to understand the state of your repository before you act. Used well, they prevent most conflicts at push time. This tutorial explains the key commands, their main parameters, and concrete examples.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>1. The Principle: Look Before You Act<\/h4>\n\n\n\n<p>A safe routine before every push:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git fetch origin                 # 1. update your view of the remote (does not touch your code)\ngit status -sb                   # 2. current state + ahead\/behind counters\ngit log --oneline HEAD..@{u}     # 3. what others added that you don't have\ngit log --oneline @{u}..HEAD     # 4. what you have that is not pushed yet\ngit pull --rebase                # 5. integrate cleanly\ngit push<\/code><\/pre>\n\n\n\n<p>Tip: <code>@{u}<\/code> (or <code>@{upstream}<\/code>) is the remote branch tracked by your current branch. You no longer need to type <code>origin\/feature\/my-branch<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>2. Commands to Observe<\/h4>\n\n\n\n<h5>git fetch<\/h5>\n\n\n\n<p>Downloads new data from the remote without modifying your working files.<\/p>\n\n\n\n<p>Main parameters:<\/p>\n\n\n\n<ul><li><code>--all<\/code>: fetch from all remotes<\/li><li><code>--prune<\/code> (<code>-p<\/code>): remove local references to remote branches that were deleted<\/li><li><code>--tags<\/code>: also fetch tags<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>git fetch --all --prune<\/code><\/pre>\n\n\n\n<h5>git status<\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>git status -sb<\/code><\/pre>\n\n\n\n<ul><li><code>-s<\/code>: short format<\/li><li><code>-b<\/code>: show the branch and its difference with the remote<\/li><\/ul>\n\n\n\n<p>Example output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>## feature\/my-branch...origin\/feature\/my-branch &#91;ahead 1, behind 2]<\/code><\/pre>\n\n\n\n<ul><li><code>ahead 1<\/code>: you have 1 commit that is not pushed<\/li><li><code>behind 2<\/code>: the remote has 2 commits you don&#8217;t have<\/li><li>ahead AND behind means the branches have diverged, so a conflict is possible<\/li><\/ul>\n\n\n\n<h5>git log<\/h5>\n\n\n\n<p>The most powerful command for observing history.<\/p>\n\n\n\n<p>Main parameters:<\/p>\n\n\n\n<ul><li><code>--oneline<\/code>: one commit per line<\/li><li><code>--graph<\/code>: draw the branch structure<\/li><li><code>--decorate<\/code>: show branch and tag names<\/li><li><code>--all<\/code>: include all branches<\/li><li><code>-n 10<\/code> or <code>-10<\/code>: limit to 10 commits<\/li><li><code>--author=\"Marek\"<\/code>: filter by author<\/li><li><code>--since=\"2 days ago\"<\/code>: filter by date<\/li><li><code>--stat<\/code>: show files changed per commit<\/li><li><code>-p<\/code>: show the full diff of each commit<\/li><li><code>-- &lt;file&gt;<\/code>: history of a single file<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>git log --oneline --graph --decorate --all -15\ngit log --author=\"Marek\" --since=\"1 week ago\" --stat\ngit log -p -- estaTektonPipeline.json<\/code><\/pre>\n\n\n\n<h5>The .. and \u2026 notations<\/h5>\n\n\n\n<ul><li><code>A..B<\/code>: commits that are in B but not in A<\/li><li><code>A...B<\/code>: commits that are in A or B, but not in both (symmetric difference)<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>git log --oneline HEAD..origin\/feature\/my-branch    # what you don't have yet\ngit log --oneline origin\/feature\/my-branch..HEAD    # what you haven't pushed\ngit log --oneline --left-right HEAD...@{u}          # both sides at once<\/code><\/pre>\n\n\n\n<p>With <code>--left-right<\/code>, <code>&lt;<\/code> means your side and <code>&gt;<\/code> means the remote side.<\/p>\n\n\n\n<h5>git show<\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>git show 72a6c875b                           # full commit\ngit show 72a6c875b --stat                    # only the changed files\ngit show 72a6c875b:estaTektonPipeline.json   # the file as it was at that commit<\/code><\/pre>\n\n\n\n<p>Note for Windows cmd: do not add a trailing <code># comment<\/code> on the command line. Windows cmd does not treat <code>#<\/code> as a comment, so Git will fail with &#8220;ambiguous argument &#8216;#'&#8221;.<\/p>\n\n\n\n<h5>git diff<\/h5>\n\n\n\n<ul><li><code>git diff<\/code>: working directory vs index (unstaged changes)<\/li><li><code>git diff --staged<\/code>: index vs last commit<\/li><li><code>git diff HEAD<\/code>: working directory vs last commit<\/li><li><code>git diff A..B<\/code>: compare two commits or branches<\/li><li><code>git diff A...B<\/code>: compare B against the common ancestor (ideal to review a pull request)<\/li><li><code>--stat<\/code>: summary of changed files<\/li><li><code>--name-only<\/code>: list file names only<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>git diff @{u} --stat\ngit diff origin\/master...HEAD --name-only<\/code><\/pre>\n\n\n\n<h5>git blame<\/h5>\n\n\n\n<p>Shows who last modified each line, and when.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git blame -L 4,8 estaTektonPipeline.json    # only lines 4 to 8<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>3. Commands to Avoid Conflicts<\/h4>\n\n\n\n<h5>git pull: three strategies<\/h5>\n\n\n\n<ul><li><code>git pull<\/code>: fetch + merge (may create a merge commit)<\/li><li><code>git pull --rebase<\/code>: fetch + rebase (linear history)<\/li><li><code>git pull --ff-only<\/code>: refuses if branches diverged (the safest option)<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>git pull --ff-only<\/code><\/pre>\n\n\n\n<p>If there is a divergence, it fails cleanly and you decide what to do.<\/p>\n\n\n\n<p>Make your preferred behavior the default:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git config --global pull.rebase true\ngit config --global fetch.prune true<\/code><\/pre>\n\n\n\n<h5>git rebase<\/h5>\n\n\n\n<p>Replays your commits on top of another branch.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git fetch origin\ngit rebase origin\/master<\/code><\/pre>\n\n\n\n<p>Main parameters:<\/p>\n\n\n\n<ul><li><code>--continue<\/code>: continue after resolving a conflict<\/li><li><code>--abort<\/code>: cancel everything and return to the initial state<\/li><li><code>--skip<\/code>: skip the problematic commit<\/li><li><code>-i HEAD~3<\/code>: interactive mode (squash, reword, drop)<\/li><\/ul>\n\n\n\n<p>Golden rule: never rebase commits that are already pushed and used by other people.<\/p>\n\n\n\n<h5>git merge<\/h5>\n\n\n\n<p>Main parameters:<\/p>\n\n\n\n<ul><li><code>--ff-only<\/code>: accept only a fast-forward<\/li><li><code>--no-ff<\/code>: force a merge commit<\/li><li><code>--abort<\/code>: cancel a merge in progress<\/li><\/ul>\n\n\n\n<p>A dry run to preview conflicts before a real merge:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git merge --no-commit --no-ff origin\/master\ngit merge --abort<\/code><\/pre>\n\n\n\n<h5>Safe git push<\/h5>\n\n\n\n<ul><li><code>git push<\/code>: normal push, rejected if the remote has moved forward<\/li><li><code>git push --force<\/code>: dangerous, can overwrite other people&#8217;s work<\/li><li><code>git push --force-with-lease<\/code>: refuses if someone pushed in the meantime (much safer)<\/li><li><code>git push --dry-run<\/code>: simulates the push without sending anything<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>git push --dry-run\ngit push --force-with-lease     # only after rebasing your own branch<\/code><\/pre>\n\n\n\n<h5>git stash: set work aside<\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>git stash push -m \"wip: side panel\"   # stash with a name\ngit stash list                        # list stashes\ngit stash pop                         # restore and delete the stash\ngit stash apply                       # restore and keep the stash\ngit stash push -u                     # also include untracked files<\/code><\/pre>\n\n\n\n<p>Use it before <code>git pull --rebase<\/code> if your working tree is not clean. Shortcut: <code>git pull --rebase --autostash<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>4. Commands to Undo Things<\/h4>\n\n\n\n<p>Choose the command according to the situation:<\/p>\n\n\n\n<ul><li>Unpushed commit, keep changes staged: <code>git reset --soft HEAD~1<\/code><\/li><li>Unpushed commit, keep changes as modified files: <code>git reset --mixed HEAD~1<\/code> (default)<\/li><li>Unpushed commit, delete everything: <code>git reset --hard HEAD~1<\/code> (destructive)<\/li><li>Already pushed commit: <code>git revert &lt;hash&gt;<\/code><\/li><li>Discard changes in a file: <code>git restore &lt;file&gt;<\/code><\/li><li>Unstage a file: <code>git restore --staged &lt;file&gt;<\/code><\/li><li>Fix the last unpushed commit: <code>git commit --amend<\/code><\/li><\/ul>\n\n\n\n<h5>The safety net: git reflog<\/h5>\n\n\n\n<p>Even after a bad <code>reset --hard<\/code>, you can usually recover your commits.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git reflog                       # history of all HEAD movements\ngit reset --hard HEAD@{2}        # go back to the state 2 moves ago<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>5. Resolving Conflicts<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>git status                       # lists files marked \"both modified\"\n# edit the file and remove the &lt;&lt;&lt;&lt;&lt;&lt;&lt;, =======, &gt;&gt;&gt;&gt;&gt;&gt;&gt; markers\ngit add &lt;file&gt;\ngit rebase --continue            # or: git merge --continue<\/code><\/pre>\n\n\n\n<p>Useful tools:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git checkout --ours &lt;file&gt;       # keep your version (during a merge)\ngit checkout --theirs &lt;file&gt;     # keep the other version\ngit mergetool                    # open the configured tool (VS Code, IntelliJ)\ngit config --global merge.conflictstyle zdiff3   # also show the original version<\/code><\/pre>\n\n\n\n<p>Rebase pitfall: during a rebase, <code>ours<\/code> and <code>theirs<\/code> are inverted. <code>ours<\/code> is the branch you rebase onto, and <code>theirs<\/code> is your own commits.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>6. Handy Aliases<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>git config --global alias.st \"status -sb\"\ngit config --global alias.lg \"log --oneline --graph --decorate --all -20\"\ngit config --global alias.incoming \"log --oneline HEAD..@{u}\"\ngit config --global alias.outgoing \"log --oneline @{u}..HEAD\"\ngit config --global alias.sync \"pull --rebase --autostash\"<\/code><\/pre>\n\n\n\n<p>Then you can run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git fetch &amp;&amp; git st &amp;&amp; git incoming &amp;&amp; git outgoing<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>7. Searching the History<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>git log -S\"buildTaskName\" --oneline       # commits that add or remove this string\ngit log -G\"password\" --oneline            # commits whose diff matches this regex\ngit log --grep=\"fix(ci)\" --oneline        # search commit messages\ngit log --grep=\"ITOP-9220\" --oneline      # find all commits of a Jira ticket\ngit log --since=\"2 days ago\" --until=\"1 hour ago\" --oneline<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>8. Git in a DevOps Context<\/h4>\n\n\n\n<ul><li>CI\/CD (Tekton, OpenShift): each push can trigger a pipeline run. Using <code>git push --dry-run<\/code> and a clean rebase avoids useless and expensive runs.<\/li><li>GitOps (ArgoCD): the Git branch is the source of truth. A force push can desynchronize a deployment, so prefer <code>git revert<\/code>.<\/li><li>Traceability (Jira, Confluence): keep the ticket key (for example <code>ITOP-1234<\/code>) in every commit message.<\/li><li>Secrets (Vault): use <code>git log -p -S\"secret\"<\/code> or <code>git log -G\"password\"<\/code> to detect a secret committed by mistake.<\/li><li>Monitoring (Splunk): correlate an error spike with <code>git log --since=\"...\" --until=\"...\"<\/code>.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>9. Best Practices<\/h4>\n\n\n\n<ol><li>Make small, atomic commits: one logical change per commit.<\/li><li>Use conventional commit messages: <code>feat:<\/code>, <code>fix:<\/code>, <code>chore:<\/code>, <code>revert:<\/code>.<\/li><li>Always fetch first: look before you act.<\/li><li>Rebase your own branch, merge shared branches.<\/li><li>Never use <code>--force<\/code>: use <code>--force-with-lease<\/code> instead.<\/li><li>Keep pull requests short to reduce conflict risk.<\/li><\/ol>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>10. Recommended Workflow<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>git fetch --all --prune\ngit lg                          # graph view (alias)\ngit incoming                    # what do I need to receive?\ngit outgoing                    # what am I about to send?\ngit pull --rebase --autostash   # integrate\ngit push --dry-run              # simulate\ngit push                        # send<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4>11. Going Further<\/h4>\n\n\n\n<ul><li><code>git bisect<\/code>: automatically find the commit that introduced a bug<\/li><li><code>git cherry-pick &lt;hash&gt;<\/code>: apply a single commit from another branch<\/li><li><code>git worktree<\/code>: work on two branches in parallel without stashing<\/li><li><code>git rerere<\/code>: remember how you resolved conflicts and reuse the resolution<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Git gives you many commands to understand the state of your repository before you act. Used well, they prevent most conflicts at push time. This tutorial explains the key commands, their main parameters, and concrete examples. 1. The Principle: Look Before You Act A safe routine before every push: Tip: @{u} (or @{upstream}) is the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":807,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[60],"tags":[1188,1185,1187,1124,1186,1135,1123],"_links":{"self":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/4530"}],"collection":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=4530"}],"version-history":[{"count":1,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/4530\/revisions"}],"predecessor-version":[{"id":4531,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/4530\/revisions\/4531"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media\/807"}],"wp:attachment":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=4530"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=4530"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=4530"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}