You have to understand a lot of things before you can somewhat comfortably use git, much more than something like P4.
You have to understand a lot of things before you can somewhat comfortably use git, much more than something like P4.
You additional items are definitely helpful - but the typical team member's workflow won't cross those bridges. Team leads perhaps.
There's definitely levels of proficiency, but for a junior or even mid-level dev doing feature work, I think there red flags if they are needing to jump into stash, cherry, etc on a daily basis.
Also, git lfs and git submodules and their associated commands are necessary or not based on the project, not on your personal level of proficiency.
I also don't know of any workflow where you don't need to look at the log at least once every few days, even as a junior dev, to confirm if a bug is fixed in a build or not if for no other reason.
That doesn't seem great... Usually these days I try to put my temporary work on a temp commit or branch at least so I don't lose it to the stash. I'm not saying I'm stashing properly, just that stash is easy to mess up.
Also probably half of these are things I popped but had merge conflicts or something. I fixed the merge conflict why is it still there? (I know the reason, but still).
As well as being more descriptive if you come back after a long weekend, this also means that you can swap branches without worrying that your stash relates to a particular branch but doesn't explicitly belong to this.
git config rebase.autoStash trueI usually do all the work in branches anyways, so I'll just create a quick branch and commit it there.
I never work on a checked-out version of a branch that's currently being updated. I always check out a new branch for my work and rebase or merge off the branch others are working on.
I'm fully aware of stash, and every now and then I use it, but it's pretty infrequent. Seeing as you need to learn branching anyway it's not going to be important for every workflow.
git branch feature1 origin/master # create
...
git fetch --all
git rebase -i origin/master feature1
Also, use emacs's magit as a better ui. Even if you don't use emacs, seriously. It's fantastically easy and fast.Of course, to the point of this thread, that is yet another concept you have to understand to get a sane working environment. I assume autostash is disabled by default because the post-merge/rebase unstash could result merge conflicts and be a pain to unwind if you change your mind. If you just blindly set this option based on someone else's recommendation without knowing what the stash is, and things go wrong, you'll require a mental model of the steps involved to fix it, which you won't have.
So basically, I am proving the article's point.
Without stash, things can be nasty. Some alternatives:
1. Commit frequently, one tested piece of code at a time.
I'm sure this is what you meant. Not every environment and process allows this and not every change is small.
If you can though and have time, do this. If needed perhaps you can combine things and rebase later into larger commits. This is the reliable and clean process of development that everyone would love to have as a base, and then to do whatever they feel with it and it always stay like this with hard work and dedication.
Let's move on because that isn't always going to be the case.
2. Commit unfinished/non-working code.
The best analogy of this is like leaving unfinished crap all over the place that might look finished to some. At some point, it may go wrong. If you do this, you might name the branch with something standard indicating it's unfinished.
This always seems to happen when a developer leaves for a holiday/vacation or leaves permanently. Then some other developer tries to build/test it, it works, they smooth some rough edges and commit it. In my experience, it is was truly unfinished, the quality may end up being somewhere between a point on a line anywhere below the initial committer's typically code quality and anywhere below the fixer's code quality. There are exceptions, but as a general rule, be more careful with such commits if the personal investment and sense of ownership is not strong in that code.
3. Multiple copies of the repo.
It's likely going to be less efficient to have multiple copies of the repo from a storage standpoint.
Showing the stashes may be more efficient than searching through different versions of the files in copies of the repo or having to recursively diff repo copies.
Using multiple copies of the repo may also increase the chance of things going wrong or history being lost.
I still would recommend having a backup of anything really important in the repo at times, if you're not feeling confident or are worried about losing anything.
4. Throw away code changes.
Every time you switch priorities, you could throw away all of the work you had locally. If it was crap, this might be best. Be careful; you could lose something important.
5. Manually copy changed files to another area outside of the repo to ensure it doesn't get stomped accidentally.
This can be messy, but with tools to make it easier, it might be "ok". Compared to git stash though, it's likely less efficient, because whole files are being copied instead of just the changes.
6. Manually backup only the diff/patch files of changes.
Well, now you're just recreating git stash functionality, but sure, you could do that. People did this before git stash and still do. Create patch files. It doesn't sound as easy or clean, and you've got to put those patch files somewhere. Will that be consistent between developers? If a developer leaves or is unavailable, where would you find them?
You also need the log daily to know things like "what changes made it into this build", or almost everytime I fix a merge conflict, to understand why something is the way it is.
I can grant that cherry-pick & blame are more rarely used, though blame is often on by default in many editors, and cherry-pick is something my team does daily around every release (since we don't want to merge the trunk into the release branch the day of the release for 1 bugfix).
So which would you recommend?
Oh, there is also the option of enabling autostash I think, but that is relatively recent (maybe 1 year?)
In case you weren’t aware, stash is not as safe as other git commands, it doesn’t have the same safety net and reflog support as a commit does. It’s relatively easy to drop stashes accidentally and lose them forever. I’ve seen people do this in production by not being careful when pulling while they have a dirty work tree. The man page for git stash mentions this fact:
“If you mistakenly drop or clear stash entries, they cannot be recovered through the normal safety mechanisms. However, you can try the following incantation to get a list of stash entries that are still in your repository, but not reachable any more:
git fsck --unreachable |
grep commit | cut -d\ -f3 |
xargs git log --merges --no-walk --grep=WIP
”Otherwise yes, I know you can relatively easily lose work with git stash (I actually once lost about 2-4 days of work with a P4 shelve, which is an extremely similar feature), but it still seems easier to me than committing and then cleaning up history/resetting if you change your mind about the implementation...
Perhaps I will give this commit/reset workflow a try as well, to see how it feels. It may also be that committing still feels to much like "an event" for me, from my P4 days.
Aside from that, I hear you, git’s model is fundamentally different from Perforce. There’s no choice about whether you need a clean work tree before pulling, that’s simply a git requirement. So the main thing to ask now is what workflow you want and what safety net you want underneath it. On one side, the best way to focus is to not pull anything while you’re working and have a dirty work tree. But things come up at work, that’s not always realistic, so the next question is how to make the workflow both safe and also instinctual so that it doesn’t have friction.
I do think there’s something to your notion that commit feels more serious and heavy than stash, and that is something I personally have tried to break down. In git, commits and branches can be so much more lightweight, fluid and flexible, but it takes practice and fluency in git to be able to actually feel that.
It might be worth considering some shell or git aliases to do common things. You could easily alias a command that commits your work in progress with a comment “WIP”, and never have to type it. You can do the same with a branch and even keep around a branch for work in progress that is always temporary and gets force reset to whatever you’re working on. (Remember a branch is just a pointer and nothing more, you can completely change what it points to without hurting anything, and the change is stored in the reflog, so there’s a undo button.) I think it is possible to make a branch & commit workflow that is both easier and safer than stash.
More typically, I will "git fetch origin" to fetch the current integration branches, then "git checkout -b <feature branch> origin/master" to start a new feature branch from a given integration branch, then push that once the change is completed.
If I need to update a local copy of an integration branch, then I might well use "git pull". But I would never have any local changes made there which would require stashing, since all changes are done on feature branches.
I think diffs are faster if it's to the local repo vs the remote repo.
I always make branches off local-main, as opposed to remote main as in your example.
I think it's also helpful to have the main branch replicated across as many machines as possible. There have been one or two times where a dev has deleted remote main at my company where some (very VERY CALM) git push solved the problem. I probably could have checked out a tracking branch and fixed it that way, but having a local copy of main made it a one-step process (I think the resolution was just to git push the main branch?).
Alternately if I'm helping other developers, I'm on their branch a lot, and it basically becomes a "push your changes and I'll take a look" and I just open that folder and git pull.
I think I use more git pull now than I did originally, because I'm working on less of my own code.
I would be very happy if I could just merge to master every day, but that is extremely rare for me and my team.
If you take out the concept of builds, you just have short lived feature branches off of master, you're left with perhaps even less than the 8 commands listed above.
Your uses for log sound legitimate but usually they are resolved over other communication channels (in person or over slack or in release notes) on the teams I've worked on.
If I want to have things-from-master in my current branch, I either rebase my branch onto master or merge master into my branch. (I'm lucky that my branches are short-lived enough that rebasing on top of master is almost always easy and the right choice.) If I have unfinished things, I'll make a "WIP" commit, that I will unroll later after doing the rebase.
I nearly never use `git blame` on the command line, but have a very frequently used hotkey bound to it in my editor to show the blame annotations.
At work we do care that our git history makes sense, is free of random nonsense, but contains only self-contained commits with reasonable documentation. So I call git log many times every day, I would not know how to do it without.
Cherry-pick probably depends how may maintenance branches you maintain. We don't have many, so I don't need it very often. I guess that could be very different for someone required to support long product life cycles. Sometimes when I need to split or unite development branches that need major reorganization I use it.
Blame I need soon when I try to understand others code. Sometimes even on my own. Also when you get bug reports form the field, to understand how long certain bugs existed. Not always to be able to blame the author, but just understanding how long a line has been unchanged.
Personally I don't use stash a lot because I'm not afraid of committing anything to my working branch. I can always fix the code or the history later. Or I make a temporary branch with a descriptive name than just stash. Some use stash more frequently I have noticed.
Rebasing is always a good skill to know for when someone inevitably commits binary files or secrets to a repo and you have to do a bit of surgery to fix it.
Bisect of course isn't required knowledge but by god is it one of the most useful git commands a developer could know.
I've never used it, but it sounds like a nice tool of last resort.
It is actually a great first debugging tool for regressions (unless you already have a hunch where the problem is). It's usefulness is greatly enhanced if you keep your commits small.
But I have been in situations where I had to manually bisect the code because I just couldn't understand how the code could reproduce the problem, and having git bisect would have been a significant help.
As for `bisect`, it's magic. It is used to binary search git commits back to X point in time. You can use it to manually go through previous commands to find what code/how long ago a bug was introduced. You can also rig it up with a test command (say a unit test that you copy out of the working tree/keep in a separate worktree) and have it automatically sift through the commits and tell you the first point where it starts to fail.
And conversely (at least in my experience), being very familiar with git makes it _really_ hard to work with something like p4.
In essence, P4 is a file tracker - you have remote files and local files kept in sync (git clone, git fetch, git pull). The basic workflow is to modify, add, copy or delete one or more files (git add), group the changes into a changelist (git stage), give it a description (git commit), and submit that change list to the remote (git push). Of course, if someone else modified the same files between your last sync and your submit, you will also have to resolve conflicts.
Of course, you can sync all or part of your files to some specific older CL (git checkout). You can revert a change on the remote as well (git reset). When you look at a file or directory, you can see the log of all changes that happened to it (git log).
The biggest difference from Git is in the branching area - branches in P4 are simply copies of one or more files, together with some metadata to tell P4 that this is an intentional branch. Working with branches is simply working with different copies of a file. When you want to bring changes from one branch back into the other, you can merge (integrate in P4's parlance) one or more changes between the branches. At that time, it does a 3-way merge just like Git - last common ancestor, changes in branch A, changes in branch B => result. The most common way of creating branches is to have each branch be a copy of the entire project dir (so you would see on your local system ~/p4/root/proj1/main,~/p4/root/proj1/dev,~/p4/root/proj1/feature1 etc.).
One limitation of P4 branching is that you can't really revert a merge in P4. You can of course revert the changes, but the merge is forever considered the new common base for the two branches. So if you integrate branch A into branch B, then revert the change on branch B, and then try to integrate again, P4 will tell you that all of the changes have already been integrated. If you try to integrate B into A now, it will try to integrate the revert into branch A.
And this is essentially it. P4 scales much better than Git, so most companies have a single P4 repo, and everyone syncs only the parts that they need.
All of these things can be done from either the command line (p4 sync, p4 edit, p4 reconcile, p4 submit, cp + p4 branch, p4 integrate) or from the very simple first party GUI, P4V.
The others I'd say could be completely excluded from daily/weekly/yearly use. If you are using cherry or blame daily then there is probably something wrong :D
Also, cherry is often needed daily in short bursts around releases.
Blame is rarer, absolutely, but I don't think you're using your version control to anywhere near its full potential if you don't use blame (and log) while investigating complex bugs.
- easier to merge and ship small incremental changes than large ones
- easier to revert small commits
- better commit messages since you can summarize the smaller change instead of "coded a lot of things"
- if you have a messy commit history in a PR you can squash them when merging upstream
I'm not sure what your git methodology is but I tend to avoid cherry.
We've got a clean production/master branch that we merge develop into on releases. We merge feature branches into develop. Very very rarely we may hotfix something on develop. I don't think I've used cherry in 6 or 7 years on repos with 2-30 active devs.
What are you using cherry for daily around releases?
And sure, it's easier to ship small features than large ones. But it's harder to follow one complex change as a series of short patches than one big, self-consistent change.
So to me, commits should be as often as possible, sure, but that is normally once a day or once every few days for serious features / large bug fixes. I usually want to pull or switch branch much more often than that.
And related to release time: typically we have a single main branch that serves multiple projects. When one of these projects is close to a release, they cut off a release branch that is feature frozen, in order to fix any remaining bugs and thoroughly sanitize it. In the meantime, other teams continue merging features into master as they are ready. Bugfixes normally also go into master first, and they are then cherry-picked into the release branch (or branches, if there are multiple releases overlapping). So for us, during this release window, cherry-picking is something that every team member does constantly.
I think the habit of committing more frequently stems from working remotely across different timezones and still making code review a daily habit. It is part of our async communication. If someone hasn't committed code for a few days I'd be concerned they were going down a bad path or getting stuck.
I may not even communicate with that team member outside of a PR for the whole week so that's where most of our communication happens if I don't see them in slack or zoom - which would be the case if our work hours don't overlap.
But hey! As long as what yall are doing works for you then no worries. No team manages their code the same way - which I'd say is another plus for git.
But the more appropriate way to discard changes is leaving them to rot in the commits of a dead experimental branch, avoiding to merge them into the important branches from which releases are made.
It depends on your team's workflow, probably, but if you find yourself needing all of those daily, it seems to me like your workflow has some extra complexity that most people won't have.