Home iOS & Swift Books Advanced Git

8
Centralized Workflow Written by Jawwad Ahmad

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

A centralized workflow is the simplest way to start with Git. With this workflow, you work directly on the main branch instead of in separate feature branches.

Creating branches in Git is extremely easy, so you should only skip creating branches when they would cause extra overhead that isn’t necessary.

In this chapter, you’ll learn about scenarios where the centralized workflow is a good fit. You’ll also learn how to handle common situations that arise when multiple developers are committing directly to the main branch.

When to use the centralized workflow

One of the primary reasons to first commit and push your code to a branch is to allow other developers to review your code before you push it to your main branch. If the code doesn’t need to be reviewed, the overhead of creating and pushing a separate branch is unnecessary. That’s where the centralized workflow is a great fit.

Here are a few scenarios where a code review may not be necessary.

1. When working alone

If you’re the sole developer on a project, you don’t need the overhead of creating branches since there are no other developers to review your code.

git checkout -b my-new-feature # 1: Create and switch to branch
# Write the code
git add . && git commit -m "Adding my new feature"
git checkout main              # 2: Switch back to main
git merge my-new-feature       # 3: Merge branch into main
git branch -d my-new-feature   # 4: Delete branch
git push main
# Write the code
git add . && git commit -m "Adding my new feature"
git push main

2. When working on a small team

If you’re part of a small team where each team member has a specialized area of knowledge, a centralized workflow is a good choice. For example, if one developer works on backend code using one programming language and another works on front-end code in a different language, it’s not always useful or practical for those team members to review code outside of their area of expertise.

Small team with non-overlapping expertise or code ownership
Sdobh teek kemp xis-ucibkuxkumt imgukbaqa in zami adsagzlob

3. When optimizing for speed

Code reviews are a great way to improve the code’s quality before pushing it to the central repository, but every code review has some overhead.

Centralized Workflow vs Branching Workflow
Cijpsuhekeh Dulgmteg nf Wleknwobc Zuvqhvuz

4. When working on a new project

The need for expediency is often stronger when working on a new project with tight deadlines. In this case, the inconvenience of waiting for a code review may be especially high.

          Drop dead launch date! Must ship by the 8th!
Lpog took taopbd meki! Hubt gwor lc pqu 6jq!

Centralized workflow best practices

Here are some best practices you can adopt to make using the centralized workflow easier. These are especially important when working in teams where multiple developers are committing to main.

Rebase early and often

When using the centralized workflow in a team, you often have to rebase before pushing to main to avoid merge commits.

Undo accidental merge commits

At times, your local main branch may diverge from the remote origin/main branch. For example, when you have local commits that you haven’t pushed yet, and the remote origin/main has newer commits pushed by others.

Getting started

To simulate working on a team, you’ll play the role of two developers, Alex and Beth!

starter
└── repos
    ├── alex
    │   └── checklists
    ├── beth
    │   └── checklists
    └── checklists.git
cd path/to/projects/starter/repos/alex/checklists # 1st Tab
cd path/to/projects/starter/repos/beth/checklists # 2nd Tab
cd path/to/projects/starter/repos/checklists.git  # 3rd Tab
git config --get remote.origin.url # Note: The --get is optional
../../checklists.git
cat .git/config
...
[user]
	name = Alex Appleseed
	email = alex@example.com
...
[user]
	name = Beth Blueberry
	email = beth@example.com

State of the project

The remote origin repository, checklists.git, contains four commits, which we’ll refer to as A1, B1, A2 and B2 instead of with their commit hashes. Alex’s and Beth’s projects also have local commits that have not yet been pushed to the remote. Alex has one additional commit, A3, and Beth has two, B3 and B4.

824f3c7 (HEAD -> main) B2: Added empty head and body tags
3a9e970 A2: Added empty html tags
b7c58f4 B1: Added index.html with <!DOCTYPE html> tag
a04ae7f A1: Initial Commit: Added LICENSE and README.md
865202c (HEAD -> main) A3: Added Checklists title within head
824f3c7 (origin/main) B2: Added empty head and body tags
3a9e970 A2: Added empty html tags
b7c58f4 B1: Added index.html with <!DOCTYPE html> tag
a04ae7f A1: Initial Commit: Added LICENSE and README.md
4da1174 (HEAD -> main) B4: Added "Welcome to Checklists!" within body
ed17ce4 B3: Added "Checklists" heading within body
824f3c7 (origin/main) B2: Added empty head and body tags
3a9e970 A2: Added empty html tags
b7c58f4 B1: Added index.html with <!DOCTYPE html> tag
a04ae7f A1: Initial Commit: Added LICENSE and README.md
Relationship between origin/main and Alex and Beth’s main branches
Yugicoarwbin tahgoum uwacet/tiiw icw Owob avf Juqj’w suiq mbaczyaw

Pushing Beth’s commits to main

Switch to the beth/checklists tab in your terminal and run git status. It should show the following to verify that it’s ahead of origin/main by two commits:

On branch main
Your branch is ahead of 'origin/main' by 2 commits.
...
4da1174 (HEAD -> main) B4: Added "Welcome to Checklists!" within body
ed17ce4 B3: Added "Checklists" heading within body
824f3c7 B2: Added empty head and body tags
...
Relationship between origin/main and local main branches after Beth’s push
Lejijoesfzew cocbeep akutib/hoiv orh loxul hiez bnuthfok objek Dilg’m beml

Pushing Alex’s commit to main

Switch to your alex/checklists tab and run git status:

On branch main
Your branch is ahead of 'origin/main' by 1 commit.
...
To ../../checklists.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '../../checklists.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Updates were rejected because the tip of your current branch is behind its remote counterpart.
Integrate the remote changes (e.g. 'git pull ...') before pushing again.
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
...
From ../../checklists
   824f3c7..4da1174  main       -> origin/main
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
* 865202c (HEAD -> main) A3: Added Checklists title within head
| * 4da1174 (origin/main) B4: Added "Welcome to Checklists!" within body
| * ed17ce4 B3: Added "Checklists" heading within body
|/
* 824f3c7 B2: Added empty head and body tags
* 3a9e970 A2: Added empty html tags
* b7c58f4 B1: Added index.html with <!DOCTYPE html> tag
* a04ae7f A1: Initial Commit: Added LICENSE and README.md
Visual representation of the previous git log --oneline --graph --all command
Xuvuub tovyuyemkomiig ex pca sdaneeux wog zoc --ozaliye --jvaqr --ugf gogpimn

Undoing a merge commit

Since Alex’s main branch has diverged from origin/main, running a git pull will result in a merge commit.

1. Abort the merge commit

The easiest way to prevent a merge commit is to short-circuit the process by leaving the commit message empty.

Merge branch 'main' of ../../checklists
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
Lines starting with '#' will be ignored, and an empty message aborts the commit.
Auto-merging index.html
error: Empty commit message.
Not committing merge; use 'git commit' to complete the merge.
...
All conflicts fixed but you are still merging.
...
git merge --abort

2. Hard reset to ORIG_HEAD

So what can you do if you accidentally created the merge commit? As long as you haven’t pushed it yet, you can reset your branch to its original commit hash before the merge.

*   99f255d (HEAD -> main) Merge branch 'main' of ../../checklists
|\
| * 4da1174 (origin/main) B4: Added "Welcome to Checklists!" within body
| * ed17ce4 B3: Added "Checklists" heading within body
* | 865202c A3: Added Checklists title within head
|/
* 824f3c7 B2: Added empty head and body tags
* 3a9e970 A2: Added empty html tags
* b7c58f4 B1: Added index.html with <!DOCTYPE html> tag
* a04ae7f A1: Initial Commit: Added LICENSE and README.md

git rev-parse ORIG_HEAD
cat .git/ORIG_HEAD
865202c4bc2a12cc2fbb94f5980b00457d270113
git reset --hard ORIG_HEAD
HEAD is now at 865202c A3: Added Checklists title within head

3. Rebase the merge commit

Another strategy you can adopt is to rebase your main branch, which now contains the merge commit, onto origin/main. This applies A3 and the merge commit on top of B4. Since origin/main already has B3 and B4, i.e., the contents of the merge commit, this removes the merge commit entirely.

git rebase origin/main
* 0c761be (HEAD -> main) A3: Added Checklists title within head
* 4da1174 (origin/main) B4: Added "Welcome to Checklists!" within body
* ed17ce4 B3: Added "Checklists" heading within body
* 824f3c7 B2: Added empty head and body tags
* 3a9e970 A2: Added empty html tags
* b7c58f4 B1: Added index.html with <!DOCTYPE html> tag
* a04ae7f A1: Initial Commit: Added LICENSE and README.md

git reset --hard 865202c

Using git pull –rebase

You previously learned that git pull is the combination of two separate commands: git fetch, and git merge origin/main.

Successfully rebased and updated refs/heads/main.
* c1f9be5 (HEAD -> main) A3: Added Checklists title within head
* 4da1174 (origin/main) B4: Added "Welcome to Checklists!" within body
* ed17ce4 B3: Added "Checklists" heading within body
...
git reset --hard 865202c

Setting up automatic rebase

You may occasionally forget that you have local commits on main before you run git pull, resulting in a merge commit. Of course, this is no longer a terrible issue since you now know how to abort and undo merge commits.

hint: Pulling without specifying how to reconcile divergent branches is
hint: discouraged. You can squelch this message by running one of the following
hint: commands sometime before your next pull:
hint:
hint:   git config pull.rebase false  # merge (the default strategy)
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
git config pull.rebase true
Successfully rebased and updated refs/heads/main.
git push
...
To ../../checklists.git
   4da1174..c9266b9  main -> main

Key points

  • The centralized workflow is a good fit when working alone or on small teams, when optimizing for speed or when working on a new, unpublished project.
  • You can still create branches for in-progress code or for ad-hoc code reviews.
  • Rebase frequently to incorporate upstream changes and resolve conflicts sooner.
  • Prefer git pull --rebase instead of git pull to avoid creating merge commits.
  • Set the pull.rebase option to true in your Git config to automatically rebase when pulling.
  • There are multiple ways to undo accidental merge commits as long as you haven’t pushed them to the remote repository.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.