Using git My loyalty is to darcs[1] ;) However, darcs has some efficiency issues that prevent it from being used on big projects. Darcs' "commutation" is also rather different from other DAG-style SCMs. So... I'm learning to use git since that seems to be what everyone else is using. While it is quite dirty and low-level, it seems that if you get to know it well it is quite a powerful tool. [1] http://darcs.net/ Entry: Gerrit, reviews and branches Date: Thu Feb 24 10:44:44 EST 2011 For a contract job I'm using the gerrit code review system[1]. These are some impressions and a summary of the workflow I'm using. ---------------------------------------------------------------------- Gerrit is "reviewer-oriented", meaning it works best if the patch management burden is carried on the patch submitter side. This requires some discipline on the patch submitter side to make it not overly painful to use. The basic workflow is simple: * Start with a clean repository = HEAD of reviewed code * Optionally, pull the review from gerrit if this is a rework of a previous review submit. * Make incremental changes using multiple git commits. * Squash all changes * Resubmit to gerrit This satisfies the requirements of the reviewer: * The reviewer would like a single, coherent patch per conceptual change in order to make the review process more meaningful. However, it is a bit crude for the developer: * The developer/submitter would like a chronological account of all the changes made to a single conceptual update identified by a Change-Id, including all mistakes and fixes. A straightforward way to implement this is to use one branch per Change-Id patch set on the developer side. This branch can then keep the chronological edit history, hiding it from the reviewer. A) Creating a new review Checkout the branch on which you want to base the current review. This is most likely the head of the reviewed master branch. git checkout master git pull origin Create a new branch that identifies the review to be submitted. I.e. suppose we write a driver for a gadget. git checkout -b gadget_driver Start working in this branch, performing incremental chronological commits without any history rewriting: git commit -am 'initial code skeleton' git commit -am 'test case 1' git commit -am 'fix for foo' git commit -am 'fix for bar' git commit -am 'test case 1 passes' When you are done and would like to submit, create a new branch to contain the squashed commit: git checkout -b gadged_driver_review Now the intention is to create a single patch on top of the master branch. This can be done using an interactive rebase: git rebase -i master This will present you with a rebase script. Change all the occurrences of "pick" to "squash", except the top one. When you save that script, git performs the rebase and drops you into your editor to edit the commit message of the review. Edit the review message and make sure the last line contains the Change-Id. When done, push the patch to gerrit as described in the manual. Note that it's not really necessary to keep the squashed patch in your private repository. It's always possible to check it out again from gerrit. B) Updating a review You've submitted a review and you want to update it. If you still have the review in a branch in your private tree, simply checkout that branch and start working. If you don't have the patch you need to obtain it from gerrit. Note that at this point the reviewd head might have moved compared to the point where you submitted the patch. It is usually a good idea to rebase onto this new reviewd head, as it avoids merge conflicts for gerrit: git checkout master git pull origin Then we create our local branch, and pull in the change from gerrit. The name is arbitrary. Pick something so you can easily relate it back to the Change-Id + patch-set it is based on. I.e. here we pick the sequence number assigned by gerrit (81) and the patch set (2). git checkout -b 81-2 git fetch refs/changes/81/81/2 git cherry-pick FETCH_HEAD If the reviewed head moved too much the cherry-pick might fail so you need to resolve conflicts. After this step succeeds you have all you need on the local branch to make incremental changes, just as in the new review case. Perform commits, rebase and push. git commit -am ... git commit -am ... git commit -am ... git commit -am ... git rebase -i master git push ... Some general advice: * Make backups. Until you fully understand the git workflow, please make backups of your repository before desctructive `rebase' or `reset' operations. Git is quite unforgiving. If you ask it to throw away your work it will throw away your work! Rule of thumb is to make sure your patches are safely tucked away on a private branch before running any distructive operations. It is sufficient to backup the .git directory only. * Use gitk or any other graphical client to visualize the branches. Once you start juggling branches it helps a lot to be able to visualize the structure. * Recovering a clean starting point. The basic idea is to keep your local master branch in sync with the reviewd remote origin branch. When you accedentally commit on top of your master branch you can use the following procedure to restore to a clean master state. First make sure that all your changes are safe. Assuming you are on the master branch, commit the changes and create a new branch that contains these changes. git commit -am git branch Then switch to the master branch and discard all changes that are not on top of the locally pulled part of the remote reviewed repository. git checkout master git reset --hard origin Optionally you can re-sync the remote origin too. git pull origin [1] http://en.wikipedia.org/wiki/Gerrit_%28software%29 Entry: git fetch Date: Mon Feb 28 10:09:02 EST 2011 In gerrit, the following is used: git fetch ... git checkout FETCH_HEAD Entry: Resolving conflicts Date: Fri Mar 4 12:11:19 EST 2011 When there is a conflict after a merge, git will tell you: Automatic cherry-pick failed. After resolving the conflicts, mark the corrected paths with 'git add ' or 'git rm ' and commit the result with: git commit -c e27ab5dddd2ee284e9dcc8ae96b67be9cf5d9857 The part of the merge that succeeded will be checked into the index, and the part that conflicted will not be checked into the index. Go to the offending file(s) and fix the problem in an editor. Entry: Uploading multiple branches Date: Wed May 4 13:10:33 EDT 2011 See this script[1]: [1] http://zwizwa.be/darcs/pool/bin/git-sync Entry: Pushing into repo's active branch Date: Wed Jun 15 15:32:55 CEST 2011 Pusing into repo's active branch seems to not be allowed since it can mess up the index. Any other branch works fine. Pulling seems to be not a problem though. Entry: Tracking branches and origin Date: Tue Jun 21 14:30:36 CEST 2011 I'm completely confused now. What I understand is the following: it's straightforward to have a local branch and push it to a remote one, but creating a tracking branch seems to fail. Aha, I had a missing "fetch =" line in my .git/config [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = <....> With this, a "git pull" will indeed pull in the remote refs. So, to link a local branch to a tracking branch do something like this: git config branch.dev.remote origin git config branch.dev.merge refs/heads/perso/tschoute/dev This will end up in .git/config as: [branch "dev"] remote = origin merge = refs/heads/perso/tschoute/dev Now, "git pull" seems to pull in changes from the tracked branch, but "git push" doesn't push. Why is that? It seems that pushes always need to be explicit, i.e. tracking is really only for pull. So in the case above, a push is git push origin HEAD:perso/tschoute/dev Or, alternatively, to push it to the trackin branch using a more general command: git push origin HEAD:$(git config branch.dev.merge) Entry: git diff Date: Fri Jun 24 12:14:17 CEST 2011 # The proper order is old, new: git diff Entry: Removing a remote branch Date: Thu Jul 7 11:06:27 CEST 2011 git push origin :newfeature [1] http://gitready.com/beginner/2009/02/02/push-and-delete-branches.html Entry: Review board Date: Thu Jul 7 19:10:18 CEST 2011 Review board[1] needs unified diff format (patch format), which is the default format generated by "git diff". Sometimes the diff between two revisions R1 and R2 is too large. To create a limited review, use the following: git-diff R1 R2 file1 file2 ... [1] http://www.reviewboard.org/docs/manual/1.5/users/review-requests/creating/ Entry: git stash Date: Mon Jul 18 15:20:06 CEST 2011 RTFM: "git stash drop" wil discard changes. how to move changes back into the index instead? Use "git stash pop" to undo the "git stash save". Entry: git: get branch without merging Date: Wed Jul 27 16:31:04 CEST 2011 git fetch? When working with multiple computers, it seems best to not perform merging automatically. I was working with 2 repositories (one on the workstation, one on the laptop) and manually creating branches and pulling from one repo into the other. This is error prone. What I've found to work best is to name the external repositories appropriately. That way they can first be fetched all at once which eliminates the problem of forgetting some patches. I.e. to import stuff from my laptop called "one", I have the following in .git/config with ~/.ssh/config setup appropriately with the alias "one". [remote "one"] fetch = +refs/heads/*:refs/heads/remotes/one/* url = ssh://one/ Then "git fetch one" will fetch all branches from the laptop and put them under remotes/one in my current repo. Then it's easy to pull or branch from there. For refspec info, i.e. in the line "fetch = +:" see [1]. [1] http://progit.org/book/ch9-5.html Entry: Git visualization Date: Sat Aug 6 12:14:16 CEST 2011 Strings of patches are not so interesting. What is interesting are forks and merges. Is it possible to use gitk to only display these? I.e. give a normal view but squash all linear dependencies. EDIT: see [1]. The command that's used to export the tree can probably also be used to create a limited tree. Dissecting this gives a useful command: git log --pretty='format: %h -> { %p }' The git-log[2] manpage gives a list of variables that can be used in the format string. See PRETTY FORMATS -> format:string From this, the following would be useful for parsing the dependency tree into Scheme. Any amount of annotation could be added later and %H and %P could be used instead of the truncated %h and %p, but this is the basic idea: git log --pretty='format:(%h "%s" (%p))' This gives a parent DAG, where forks are implicit in the structure. Can the graph be turned upside down, making the forks explicit? I don't see anything in the format string, but there is a --children option. [1] https://git.wiki.kernel.org/index.php/Aliases#Usegraphvizfordisplay [2] http://www.kernel.org/pub/software/scm/git/docs/git-log.html Entry: Static accessors for for flash variables Date: Sat Aug 6 12:27:43 CEST 2011 The only way to do this properly is to statically associate *keys* to types. This can only be done by creating functions like: u32 read_110(); u32 read_VAR_NAME(); This is possible though: all info is in the default types table. Entry: Fixing submodule without cloning Date: Fri Oct 21 16:52:18 EDT 2011 I replaced a directory that contained a submodule by a symbolic link and committed that change. Git noticed that, and changed the file type to symbolic link. Now my submodule info is gone. How to fix? From here [1] I find that: The git-submodule add command does a couple of things: It clones the submodule under the current directory and by default checks out the master branch. It adds the submodule's clone path to the gitmodules file and adds this file to the index, ready to be committed. It adds the submodule's current commit ID to the index, ready to be committed. I already have a .gitmodules file. I don't need a clone as I do this manually with some symlinks later. So I just need to know how to add the commitid. The commit IDs in the index can be seen using the 'ls-tree' command. $ git ls-tree HEAD 160000 commit 4b7734dfa87e981d7e0d74b9d76e24a0425bc28f module_a 160000 commit 75e2a794db9135d98b14411f586dab9e99e26bdb module_b So how to add them back? Ok, first make sure the directory being added is in fact a valid git repository (to avoid cloning). Then do something like this: $ git submodule add module_a Adding existing repo at module_a' to the index Now, be careful about .gitmodules : it is used by "git submodule init" to put the repos in .git/config but it will not overwrite any entries that are already there. [1] http://book.git-scm.com/5_submodules.html Entry: Reverting patches Date: Thu Jun 21 12:33:23 EDT 2012 Scenario: - I have a branch "doodle" that contains an experimental feature in progress. - Once the feature in "doodle" is done, I find that it contains a lot of extra cruft that is not necessary for a merge to "master". - I create a new branch "feature", which has that extra cruft removed. I merge that one with master. Now "diff feature doodle" contains the cruft, but "feature" is now younger than "doodle". Any time a child of "feature" gets merged into "doodle", an automatic merge will clean up the cruft. How to make it so that the cruft sits on top of "feature"? Can this only be solved with a rebase, or is there a way to do this without loosing history. It seems this can be done by applying a "git revert" of the cleanup patch(es) in "feature". Something like: git revert -n doodle..feature git commit -am 'reverted cleanup doodle->feature' Entry: Local copies of subprojects Date: Thu Aug 21 23:30:12 CEST 2014 Haven't found a good way to make a subproject without a subproject. I.e. if top is darcs and includes the subproject in the tree, how to properly sync things? Can git pull from a non git archive? What about this approach: - soft link git -> ~/git/zl/.git - never do anything automatically! - merge working tree, e.g. libprim/zl to current master The trouble is that this is "inverted". I.e. applying a change to libprim/zl is a revert to master zl. This in itself is not an issue, but it would be simpler to have a separate git repo. So what about this: keep separate git repos that are in sync with the parent darcs/git, but never update them automatically. Maybe it's best to just record the hash, and make a script that checks out that hash, then does a merge with master, and then merge those changes into the parent darcs/git. This allows git to do the merging. Deleting the .git when it's not used for merging then avoids confusion since there are two source control mechanisms looking at the same working tree. Important ideas: - let git do the merging - use only temporary git repository during the merge - record the hash of last merged version Entry: remove submodule Date: Wed Aug 16 09:10:31 EDT 2017 git submodule deinit git rm https://stackoverflow.com/questions/29850029/what-is-the-current-way-to-remove-a-git-submodule Entry: Submodule dirty Date: Mon May 6 15:05:46 EDT 2019 https://stackoverflow.com/questions/4873980/git-diff-says-subproject-is-dirty Submodules are now regarded as dirty if they have any modified files or untracked files To solve this, add it to .gitignore