Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • (master) git pull
  • (master) git commit -m "A"
  • (master) git pull (results in a silent, automatic merge commit P1 since this pull is not fast-forward)
  • (master) git pull (results in another silent, automatic merge commit P2 since this pull is not fast-forward)
  • (master) git commit -m "B"
  • (master) git pull (Yet another merge commit P3)
  • (master) git push (The repository master now is identical to his local master)

...

  • (master) git pull
  • (master) git branch harry_branch origin/master --track
  • (harry_branch) git commit -m "A"
  • (harry_branch) git pull (results in a silent, automatic merge commit P1 since this merge is not fast-forward)
  • (harry_branch) git pull (results in a silent, automatic merge commit P2 since this merge is not fast-forward)
  • (harry_branch) git commit -m "B"
  • (master) git pull (Is fast-forward. No merge commit created)
  • (master) git merge harry_branch (results in an explicit merge commit P3}
  • (master) git push (The repository master now is identical to his local master)

...

As is evident, the github history graph is still complex, but perhaps more "intuitive" in the sense that it preserves the fact that commits 1,2,3,4,5 and 6 had been published in master, and that Harry's commits A and B occurred in some other branch. Harry's pull merges are also preserved - but this time it is clear that changes (commits 4 and 5) were propagated from master into his own branch during the pull/merge, and that he merged his branch back into the published master at the end.

With this technique, pushing the local branch (harry_branch) to the repository occasionally would make no difference, and would be safe. This pattern has an identical end result to maintaining a published fcrepo-XXX feature/bug branch, and merging it with master in the end.

Development in a local branch with rebase

Development in an unpublished local branch, and using git rebase instead of pull or merge to update the local branch with changes to master is also a valid pattern. This technique results in the elimination of the local branching history, and rather than a final merge applies all local commits in sequence to the end of the current master. This may be used when the local branch and merge history is unimportant or unneecessary (perhaps bad luck - while making two trivial local commits, somebody happened to push master in the meantime).

It is important to never rebase a published branch if you intend on ever pushing that branch again. As a safety, Git will refuse to push a branch that has had its history re-written with rebase. Although it is possible to force the changes through with --force, never do that!

Let us use the same Tom, Dick, & Harry example, except with Harry performing his development in a local, non-published branch, with occasional rebasing to track the changes in master.

  • (master) git pull
  • (master) git branch harry_branch origin/master --track
  • (harry_branch) git commit -m "A"
  • (harry_branch) git fetch; git rebase origin/master (Modifies Harry's A commit so that it appears to have occurred after all changes that have been imported from master)
  • (harry_branch) git fetch; git rebase origin/master (Modifies Harry's A commit so that it appears to have occurred after all changes that have been imported from master)
  • (harry_branch) git commit -m "B"
  • (harry_branch) git fetch; git rebase origin/master (Modifies Harry's A and B commits so that they appear to have occurred after all changes that have been imported from master)
  • (master) git pull (Is fast-forward. No merge commit created)
  • (master) git merge harry_branch (fast-forward. Does not result in merge commit)
  • (master) git push (The repository master now is identical to his local master)

Image Added

This results in a very simple history. Since rebase operations result in new commits at the end of a tree, Harry's a and B commits were transformed into A' and B', which could be simply applied almost as a patch directly to the end of master in a fast-forward merge. The end result is exactly the same as if Harry were to git cherry-pick the two commits from his harry_branch onto master - they both result in new commits at the tail of a branch.